大きなインターフェースを分割する


9

データベースにアクセスするために、約50のメソッドを持つ大きなインターフェースを使用しています。インターフェイスは私の同僚によって書かれました。これについて話し合いました:

私:50の方法は多すぎます。コードの匂いです。
同僚:それについて私は何をすべきか?あなたはDBアクセスを望んでいます-あなたはそれを持っています。
私:ええ、でもそれは不明確で、将来的にはメンテナンスが困難です。
同僚:わかりました、そうです、それは良くありません。インターフェイスはどのように見えるべきですか?
私:それぞれ10個のメソッドを持つオブジェクトを返す5つのメソッドはどうでしょうか?

うーん、でもこれは同じでしょ?これは本当により明確になるでしょうか?努力する価値はありますか?

時々、インターフェースが必要な状況にあり、最初に頭に浮かぶのは、1つの大きなインターフェースです。これの一般的なデザインパターンはありますか?


更新(SJuanのコメントに対応):

「メソッドの種類」:データベースからデータを取得するためのインターフェースです。すべてのメソッドの形式は(疑似コード)です。

List<Typename> createTablenameList()

メソッドとテーブルは厳密には1対1の関係にあるわけではなく、常にデータベースから得られるある種のリストを取得するという事実に重点が置かれています。


12
関連情報(どのような方法があるか)がありません。とにかく、私の推測:数値で除算すれば、あなたの同僚は正しいです、あなたは何も改善していません。可能な分割基準の1つは、「エンティティ」(テーブルとほぼ同等)によって返される(UserDaoCustomerDaoProductDao
つまり

実際、いくつかのテーブルは、「クリーク」を形成する他のテーブルに意味的に近いです。メソッドも同様です。
TobiMcNamobi 2014年

コードを見ることはできますか?私はそれが4歳であることを知っています、そしておそらくあなたは今までにそれを修正しました:Dしかし、私はこの問題について考えたいと思います。私は以前にこのようなことを解決しました。
clankill3r

@ clankill3r確かに、上記の質問を投稿させた特定のインターフェースにもうアクセスできません。問題はもっと一般的です。約50テーブルと、各テーブルのDBを想像する方法などList<WeatherDataRecord> createWeatherDataTable() {db.open(); return db.select("*", "tbl_weatherData");}
TobiMcNamobi

回答:


16

はい、50のメソッドはコードのにおいですが、コードのにおいはそれをもう一度見直すことを意味し、自動的に間違っているということではありません。そのクラスを使用するすべてのクライアントが50のメソッドすべてを潜在的に必要とする場合、それを分割するケースがない場合があります。ただし、これはありそうもないことです。私の要点は、インターフェースを任意に分割することは、まったく分割しないよりも悪いことです。

修正するための単一のパターンはありませんが、望ましい状態を説明する原則は、使用しないメソッドに依存するようにクライアントを強制するべきではないことを示すインターフェイス分離原則(SOLIDの「I」)です。 。

ISPの説明は、それを修正する方法についてのヒントを提供しますクライアントを見てください。多くの場合、クラスを見るだけですべてが一緒になっているように見えますがそのクラスを使用しているクライアント見ると、明確な区分が明らかになります。インターフェイスを設計するときは、常に最初にクライアントを検討してください。

インターフェイスを分割する必要があるかどうかと場所を決定するもう1つの方法は、2番目の実装を作成することです。よくあることは、2番目の実装では多くのメソッドを必要としないため、それらを明確に独自のインターフェースに分割する必要があるということです。


これとutnapistimの答えは本当に素晴らしいです。
TobiMcNamobi 2014年

13

同僚:わかりました、そうです、それは良くありません。インターフェイスはどのように見えるべきですか?

私:それぞれ10個のメソッドを持つオブジェクトを返す5つのメソッドはどうでしょうか?

それは良い基準ではありません(実際にはそのステートメントには基準がまったくありません)。あなたはそれらをグループ化することができます(あなたのアプリケーションが私の例では金融取引アプリであると仮定します):

  • 機能(挿入、更新、選択、トランザクション、メタデータ、スキーマなど)
  • エンティティ(ユーザーDAO、預金DAOなど)
  • アプリケーション領域(金融取引、ユーザー管理、合計など)
  • 抽象化レベル(すべてのテーブルアクセスコードは独立したモジュールです。すべての選択APIは独自の階層にあり、トランザクションサポートは独立しています。モジュール内のすべての変換コード、モジュール内のすべての検証コードなど)

うーん、でもこれは同じでしょ?これは本当により明確になるでしょうか?努力する価値はありますか?

あなたが正しい基準を選んだら、間違いなく。そうでない場合、間違いなく:)。

いくつかの例:

  • 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をご覧ください


3

時々私はインターフェースが必要な状況にあり、最初に頭に浮かぶのは1つの大きなインターフェースです。これの一般的なデザインパターンはありますか?

これは、モノリシッククラスと呼ばれるデザインアンチパターンです。クラスまたはインターフェースに50のメソッドがあると、SRPに違反する可能性があります。モノリシッククラスは、すべての人にとってすべてになることを目指しているために生まれました。

DCIはメソッドの膨張に対処します。基本的に、クラスの多くの責任は、特定のコンテキストにのみ関連するロール(他のクラスにオフロードされる)に割り当てられます。ロールの適用は、ミックスインやデコレーターなど、さまざまな方法で実現できます。このアプローチにより、クラスは集中的で無駄のないものになります。

たとえば、それぞれ10個のメソッドを持つオブジェクトを返す5つのメソッドはどうでしょうか。

これは、オブジェクト自体がインスタンス化されるときに、すべてのロールをインスタンス化することを示唆しています。しかし、なぜ必要のない役割をインスタンス化するのでしょうか。代わりに、実際に必要なコンテキストでロールをインスタンス化してください。

DCIへのリファクタリングが明らかでない場合は、より単純なビジターパターンを使用できます。ユースケースコンテキストの作成を強調せずに、同様の利点を提供します。

編集:これについての私の考えはいくつかを変えました。私は別の答えを出しました。


1

それは私に他のすべての答えがポイントを欠いているように見えます。重要なのは、インターフェイスは理想的には動作の原子的なチャンクを定義する必要があるということです。それがSOLIDの私です。

クラスには1つの責任が必要ですが、これには複数の動作が含まれる可能性があります。典型的なデータベースクライアントオブジェクトに固執するために、これは完全なCRUD機能を提供する場合があります。これは、作成、読み取り、更新、削除の4つの動作です。純粋なSOLIDの世界では、データベースクライアントはIDatabaseClientではなくICreator、IReader、IUpdater、IDeleterを実装します。

これには多くの利点があります。まず、クラス宣言を読むだけで、クラスについて多くのことを即座に学ぶことができます。クラスが実装するインターフェースは、全体像を伝えます。第2に、クライアントオブジェクトが引数として渡される場合、別の便利なオプションが提供されます。これはIReaderとして渡すことができ、呼び出し先が読み取ることしかできないと確信できます。異なる動作を個別にテストできます。

ただし、テストに関しては、完全なクラスインターフェイスの1対1のレプリカであるクラスでインターフェイスを平手打ちするのが一般的な方法です。テストだけで十分であれば、これは有効なアプローチかもしれません。それはあなたがかなり速くダミーを作ることを可能にします。しかし、それは決してSOLIDではなく、本当に専用の目的のためのインターフェースの乱用です。

だから、はい、50の方法は匂いですが、それが悪いかどうかの意図と目的に依存します。それは確かに理想的ではありません。


0

データアクセスレイヤーでは、1つのクラスに多くのメソッドがアタッチされる傾向があります。Entity Frameworkや他のORMツールを使用したことがある場合、それらが数百のメソッドを生成することがわかります。私はあなたとあなたの同僚が手動でそれを実装していると思います。コードの匂いは必要ありませんが、見た目がきれいではありません。ドメインを知らなければ、言うのは難しいです。


メソッドまたはプロパティ?
JeffO 2014年

0

私は、FPとOOPの両方を持つすべてのAPIに対して、ほぼ普遍的にプロトコル(必要に応じてインターフェースと呼びます)を使用しています。(マトリックスを思い出してください。具体例はありません!)もちろん具体的なタイプはありますが、プログラムの範囲内では、すべてのタイプが特定のコンテキスト内で役割を果たすものと見なされます。

つまり、プログラム全体や関数などに渡されるオブジェクトは、名前付きの動作セットを持つ抽象エンティティと考えることができます。オブジェクトは、プロトコルのセットである役割を果たすと考えることができます。人(コンクリートタイプ)は、男性、父親、夫、友人、従業員のいずれかですが、エンティティをそれらの2つ以上の合計と見なす多くの機能は想像できません。

複雑なオブジェクトが多数の異なるプロトコルに準拠している可能性はあると思いますが、それでも50メソッドのAPIに到達するのは難しいでしょう。ほとんどのプロトコルには1つまたは2つのメソッドがあり、おそらく3つですが、50では決してありません!50のメソッドを持つエンティティは、それぞれが独自の責任を持つ小さなコンポーネントの束の集合体にする必要があります。エンティティ全体は、その中のAPIの合計を抽象化する、より単純なインターフェースを提供します。

オブジェクトとメソッドの観点から考えるのではなく、抽象化とコントラクト、およびサブジェクトが特定のコンテキストでどのような役割を果たすかという観点から考え始めます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.