web-dev-qa-db-ja.com

大きなインターフェースを扱う

特定のタイプの連立方程式を解くプログラムに取り組んでいます。主なデータオブジェクトは、方程式、変数、ソリューションです。次に、このインターフェイスがあります。これは、UIレイヤーに公開したいすべてのものを表します。

_public interface ISystemSolver
{
    // Configuration
    int NumberBase { get; set; }
    bool CaseSensitive { get; set; }
    bool LeadingZeros { get; set; }

    // State
    IReadOnlyList<int> ForbiddenValues { get; }
    IReadOnlyList<char> ForbiddenVariables { get; }
    event EventHandler OnTooManyVariables;
    bool AreThereTooManyVariables { get; }

    // Variables
    IEnumerable<Variable> Variables { get; }
    void RemoveVariable(int index);
    void AddVariable(char variable, int value);

    // Equations
    IEnumerable<Equation> Equations { get; }
    void RemoveEquation(int index);
    void AddEquation(string equation);

    // Solving
    IEnumerable<Solution> Solve();
}
_

それを実装するクラスは、確かに単一の責任ある原則に違反します。どうすればこれを回避できますか?

私はこのアイデアを持っていました:特定の領域をインターフェイスに抽出します。たとえば、変数を処理する3つのメソッドは、IVariablesというインターフェイスを形成します。

しかし、たとえば変数を追加する必要がある場合、それはデメテルの法則の違反ではないでしょうか? SystemSolver.Variables.AddVariable(...)を呼び出す必要があるため

4
Patrik Bak

まず、デザインについて考え、いくつかの原則に従うようにしていただきありがとうございます。私はそうでないものによって生成されたコードの中を歩くのを楽しんでいない。

デメテルの法則

デメテルの法則を説明します。ドットカウントの練習ではありません。

SystemSolver.Variables.AddVariable(...);

これを行うことは、これを行うことと同じです。

Variables vars = SystemSolver.Variables;
vars.AddVariable(...);

いずれにせよ、これはここの関係に応じて良いか悪いかです。 SystemSolverVariablesが意図的かつ明示的にこの方法で使用するように設計された親しい友人である場合、何かを構築させようとしているドメイン固有言語などの場合は、セルフアウト。これは意図したとおりに使用しており、非常に強力で表現力のあるものにすることができます。

しかし、偶然に何かを手に取っているのは、たまたまこのようにして見つけられるからです。つまり、これらの個別のクラスには親密で不変の関係が必要であると決定しています。その決定は、それらのクラスを使用するクライアントの責任であってはなりません。それはそれらのクラスの設計者次第です。あなたがこれを行い、誰かが関係を変えることを決めた場合、クライアントが壊れたので泣かないでください。これが機能し続けるとは決して約束されていません。

続けることはできますが、以前は Demeter について話しました。

依存性注入

もしあなたがそのような親密な関係を作りたくないなら、どうやって必要なものを手に入れようとしていますか?あなたに渡されます。

SystemSolver ss = new SystemSolver();
Variables vars = new Variables();
UI ui = new UI(ss, vars);

そこで、UIはそれが何を必要としているのかを知っていますが、彼らはお互いについてさえ知りません。それは理にかなっていますか? SystemSolverが変数を作成しない限り、変数は無意味かもしれません。もしそうなら、おそらく彼らは親密な関係を持っています。もしそうなら、それらをそのように設計して、必要なものを見つけるために点在することは、デメテルの違反ではありません。

単一責任の原則

責任が1つしかない場合、変更する理由は1つだけです。それはあなたが内側を見たときにあなたがたった一つのものを見つけたという意味ではありません。それはあなたが内部で見つけるすべてが同じものに焦点を当てていることを意味します。

あなたの車について考えてください。あなたの車のほとんどすべてが車のものです。座席はカーシートです。タイヤは車のタイヤです。ダッシュボードは車のダッシュボードです。コーヒーカップは、ここで何をしてるの?多くのものが車に収まる可能性がありますが、車を購入するときにそこに見つけたいものではないものもあります。

カップを交換しても車に影響はありません。車を変えてもカップには影響しません。ほとんどの場合、どちらかを変更してもカップホルダーにのみ影響します。これが、SRPのフォローに本当に関心がある理由です。変更の範囲を制限します。私も これについて前に話しました をしたので、ここで停止します。 (私はその間ずっとこのサイトにいましたか?)

SRPに違反していない理由

「[ISystemSolver]は、UIレイヤーに公開したいすべてのものを表しています。」

それは単一の責任です。それは大きな問題です。しかし、それはまだ単一です。あなたはそれを分解したいのですが、それは良いことですが、SRPはそうではありません。

メソッドは5つの異なるグループに分けられ、それぞれが1つのアイデアに関連しています。それは私に5つの責任を示しています。悪いですか?いいえ。これらの責任はすべて、依然として必要なUIを提供することに集中しています。

しかし、これら5つは一緒にクラスター化できるようにする必要があります。これは私に思い出させます

ファサードパターン

これらの5つのグループは、それぞれ個別のクラスにすることができます。それらのオブジェクトは、それぞれの作業を委任するUIのファサードクラスに追加されます。これにより、物事を分離することができますが、構造の複雑さはUIから隠されます。また、必要なものがすべてそこにあるため、デメテルの法則を心配する必要がなくなります。

教えて、聞かないで

ゲッターとセッターをたくさん使用しています。これらはカプセル化を破り、手続き型コードの作成につながることを理解してください。オブジェクトに何をすべきかを指示し、その方法を決定させるよりも、何かを聞いて、学んだことに基づいて決定を行う方がはるかに強力です。 this one と書いてよかったのですが、読んでよかったです。

9
candied_orange