データベースにアクセスするために、約50のメソッドを持つ大きなインターフェースを使用しています。インターフェイスは私の同僚によって書かれました。私たちはこれについて議論しました:
私:50の方法は多すぎます。コードの匂いです。
同僚:それについて私は何をすべきでしょうか?あなたはDBアクセスを望んでいます-あなたはそれを持っています。
私:ええ、でもそれは不明確で、将来的にはほとんど維持できません。
同僚:わかりました、あなたは正しいです、それはニースではありません。インターフェイスはどのように見えるべきですか?
私:それぞれ10個のメソッドを持つオブジェクトを返す5つのメソッドはどうでしょうか?
うーん、でもこれは同じでしょ?これは本当により明確になるでしょうか?努力する価値はありますか?
時々私はインターフェイスが必要な状況にあり、最初に頭に浮かぶのはone、bigインターフェイスです。これの一般的なデザインパターンはありますか?
更新(SJuanのコメントに対応):
「メソッドの種類」:データベースからデータを取得するためのインターフェースです。すべてのメソッドの形式(疑似コード)
List<Typename> createTablenameList()
メソッドとテーブルは厳密には1対1の関係にあるわけではなく、常にデータベースから得られるある種のリストを取得するということに重点が置かれています。
はい、50のメソッドはコードの匂いですが、コードの匂いはそれをもう一度見直すことを意味し、自動的に間違っているということではありません。そのクラスを使用するすべてのクライアントが50のメソッドすべてを潜在的に必要とする場合、それを分割するケースがない場合があります。ただし、これはありそうもないことです。私の要点は、インターフェースを任意に分割することは、まったく分割しないよりも悪いことです。
それを修正するための単一のパターンはありませんが、望ましい状態を説明する原則は インターフェース分離の原則 (SOLIDの「I」)であり、クライアントに依存を強制するべきではないと述べています使用しないメソッドについて。
ISPの説明は、それを修正する方法についてのヒントを提供します:clientを見てください。多くの場合、クラスを見るだけですべてが一緒に属しているように見えますが、そのクラスを使用してクライアントを使用すると明確な区分が生じます。インターフェイスを設計するときは、常に最初にクライアントを検討してください。
インターフェイスを分割する必要があるかどうかと場所を決定するもう1つの方法は、2番目の実装を作成することです。 2番目の実装は多くのメソッドを必要としないので、それらが明らかに独自のインターフェースに分割されるべきです。
同僚:わかりました、そうです、それはニースではありません。インターフェイスはどのように見えるべきですか?
私:それぞれ10個のメソッドを持つオブジェクトを返す5つのメソッドはどうでしょうか?
それは良い基準ではありません(そのステートメントには実際には基準がまったくありません)。あなたはそれらをグループ化することができます(私の例では、アプリケーションが金融取引アプリであると仮定します):
うーん、でもこれは同じでしょ?これは本当により明確になるでしょうか?努力する価値はありますか?
あなたが正しい基準を選んだら、間違いなく。そうしないと、間違いなく:)になります。
いくつかの例:
OOプリミティブの簡単な例については、 ADODBオブジェクト を参照してください(DB APIはおそらくこれをすでに提供しています)
Djangoデータモデル( https://docs.djangoproject.com/en/dev/topics/db/models/ ))を見て、高レベルの抽象化(C++ではボイラープレートコードがさらに必要になる可能性がありますが、これはいい考えです)この実装は、MVC設計パターン内で「モデル」の役割を念頭に置いて設計されています。
機能的なプリミティブ(C API)のみで構成されるフラットなAPIアイデア( http://www.sqlite.org/c3ref/funclist.html )については、sqlite APIをご覧ください。
時々、インターフェースが必要な状況にあり、最初に頭に浮かぶのは、1つの大きなインターフェースです。これの一般的なデザインパターンはありますか?
これはmonolithic classと呼ばれるデザインのアンチパターンです。クラスまたはインターフェースに50のメソッドがあると、 [〜#〜] srp [〜#〜] に違反する可能性があります。モノリシッククラスは、すべての人にとってすべてになることを目指しているために生まれました。
[〜#〜] dci [〜#〜] メソッドの膨張に対処します。基本的に、クラスの多くの責任は、特定のコンテキストにのみ関連するロール(他のクラスにオフロードされる)に割り当てられます。ロールの適用は、ミックスインまたは decorators を含むさまざまな方法で実現できます。このアプローチにより、クラスは集中的で無駄のないものになります。
たとえば、それぞれ10個のメソッドを持つオブジェクトを返す5つのメソッドはどうでしょうか。
これは、オブジェクト自体がインスタンス化されるときにすべてのロールをインスタンス化することを提案します。しかし、なぜ必要のない役割をインスタンス化するのでしょうか。代わりに、実際に必要なコンテキストでロールをインスタンス化してください。
DCIへのリファクタリングが明らかでない場合は、より単純な ビジターパターン を使用できます。ユースケースコンテキストの作成を強調せずに、同様の利点を提供します。
編集:これについての私の考えはいくつかを変えました。私は別の答えを出しました。
それは私に他のすべての答えがポイントを欠いているように見えます。重要なのは、インターフェイスは理想的には動作の原子的なチャンクを定義する必要があるということです。 Thatは、SOLIDのIです。
クラスには1つの責任がありますが、これには複数の動作が含まれる可能性があります。典型的なデータベースクライアントオブジェクトに固執するために、これは完全なCRUD機能を提供する場合があります。これは、作成、読み取り、更新、削除の4つの動作です。純粋なSOLID=の世界では、データベースクライアントはIDatabaseClientではなくICreator、IReader、IUpdater、IDeleterを実装します。
これには多くの利点があります。まず、クラス宣言を読むだけで、クラスについて多くのことを即座に学ぶことができます。クラスが実装するインターフェースは全体像を伝えます。第2に、クライアントオブジェクトが引数として渡される場合、さまざまな便利なオプションがあります。これはIReaderとして渡すことができ、呼び出し先は読み取りのみができると確信できます。異なる動作を個別にテストできます。
ただし、テストに関しては、完全なクラスインターフェイスの1対1のレプリカであるクラスでインターフェイスを平手打ちするのが一般的な方法です。テストだけで十分であれば、これは有効なアプローチかもしれません。それはあなたがかなり速くダミーを作ることを可能にします。しかし、これは決してSOLIDであり、専用の目的でのインターフェースの乱用ではありません。
だから、はい、50の方法は匂いですが、それが悪いかどうかは意図と目的に依存します。それは確かに理想的ではありません。
データアクセスレイヤーでは、1つのクラスに多くのメソッドがアタッチされる傾向があります。 Entity Frameworkや他のORMツールを使用したことがある場合は、それらが何百ものメソッドを生成することがわかります。私はあなたとあなたの同僚が手動でそれを実装していると思います。コードの匂いは必要ありませんが、見た目がきれいではありません。ドメインを知らなければ、言うのは難しいです。
FPとOOPの両方を使用するすべてのAPIに対して、ほぼ普遍的にプロトコルを使用します(必要に応じてインターフェイスと呼びます)。(マトリックスを思い出してください。具体例はありません!)もちろん、具体的な型がありますしかし、プログラムの範囲内では、すべてのタイプが何らかのコンテキスト内で役割を果たすものと考えられています。
つまり、プログラム全体や関数などに渡されるオブジェクトは、名前付きの動作セットを持つ抽象的なエンティティと考えることができます。オブジェクトは、プロトコルのセットである役割を果たすと考えることができます。人(具体的なタイプ)は、男性、父親、夫、友人、および従業員である可能性がありますが、エンティティをそれらの2つ以上の合計と見なす多くの機能を想像することはできません。
複雑なオブジェクトが多数の異なるプロトコルに準拠している可能性はあると思いますが、それでも50メソッドのAPIに到達するのは難しいでしょう。ほとんどのプロトコルには1つまたは2つのメソッドがあり、おそらく3つですが、50にはなりません! 50のメソッドを持つエンティティは、それぞれが独自の責任を持つ小さなコンポーネントの束の集合体にする必要があります。エンティティ全体は、その中のAPIの合計を抽象化する、より単純なインターフェースを提供します。
オブジェクトとメソッドの観点から考えるのではなく、抽象化とコントラクトの観点から考えることから始めてください。