20

私はMVPパターンを使用して大規模なアプリケーションを開発しています。開発に取り組んでいる間、私は構成と継承のどちらを使用すべきかという質問を思いつきました。例:フィールドABを持つFooというフォームがあるとします。アプリケーションの他の部分には、同じフィールドABがあり、追加のフィールドCを持つフォームバーがあります。

現在、コードは、フォームBarがフォームFooから継承するビューでの継承アプローチで記述されています。次に、プレゼンターはモデルとは少し異なるデータを処理します。これは非常に簡単に機能しますが、フォームが異なっていても共通の入力(AとB)を処理するため、「isA」の経験則に従うかどうかはわかりません。

しかし、ここで私は「継承よりもコンポジション」とリスコフの置換原則を考えていて、継承の代わりにコンポジションを使うべきだと思うようになりました。ただし、MVPを使用しているため、フィールドABを持つフォームFooのプレゼンター、フィールドCを持つBarのプレゼンター、およびFooのプレゼンターへの参照が必要になるため、予想よりも複雑になっています。フィールドABを注入できます。

問題は、データをBarに渡すことができるようにするために、 Fooのプレゼンターにいくつかのソートゲッターとセッターを追加する必要があるため、より多くのコードであることが証明されていることです。これは、作曲を提供するためにMVPを破っているような気がします。

だから私の質問は:

私の場合、継承よりも合成を使用する方が本当に良いですか?なんで?

コンポジションを使用するとMVPが「中断」されますか?

4

5 に答える 5

10

私の場合、継承よりも合成を使用する方が本当に良いですか?なんで?

はい。大規模なアプリでは、構成の信頼性、安全性、保守性、発見性、文書化性、理解性が向上するためです。私見では。:)

コンポジションを使用するとMVPが「中断」されますか?

はい。それはあなたが今しているような単純なMVPを壊します。コンポジションを使用すると、コードを結合する方法を選択できます。これは、大規模なアプリに非常に適しています。結合方法について具体的にする必要があるため、より多くのコードを使用します。

単純なアプリが成長し、単純なMVP継承からより洗練された構成への移行の良い候補になることは非常に合理的です。これは、新しい方法での再結合を可能にするデカップリングステップです。

これは、フロント/バックAPI駆動型アプリに移行する単純なWebアプリの数に似ています。これは基本的に、フロントエンドユーザービューとバックエンドストレージモデルの分離です。

于 2013-02-05T03:12:49.573 に答える
8

フォームが異なる場合、それらは共通の入力(AおよびB)を処理します。

つまり、FooプレゼンターはBarプレゼンターとは概念的に異なり、たまたまいくつかの共通の入力を共有しているため、継承によって関連付けられるべきではありません。ユーティリティクラスへの一般的な入力を処理するコードを抽出し、FooプレゼンターとBarプレゼンターの両方で再利用します。

Fooの概念が変更された場合、Barには影響しません(逆に、Fooの概念も変更せずにBarの概念を変更できない場合、 「is A」の関係であり、継承を実際に使用できます)

疑わしいときは、常に作曲を好む

于 2013-01-27T04:56:09.517 に答える
6

よりクリーンな構成は、クラスを持つことです。

モデル:A、B、C、Foo、
バービュー:AView、BView、CView、FooView、BarView
プレゼンター:APresentor、BPresentor、CPresentor、FooPresentor、BarPresentor

FooViewにAViewとBViewが含まれているのに対し、BarViewにはAView、BView、CViewが含まれており、プレゼンターは同様の構成になっています。

この構成により、A、B、およびC(およびそれらのビューとプレゼンター)がモジュール化されるため、好きなように組み合わせて組み合わせることができ、複合クラス(FooとBar)が統合を処理します。

これは継承と一緒に使用できます。BarがFooの特定のケースである場合、BarはFoorから継承する必要があり、BarPresentorはFooPresentorから継承する場合があります。ただし、ビューは動作に応じて継承に適している場合とそうでない場合があるため、ケースごとにビューの継承を検討します。

于 2013-02-03T16:08:32.350 に答える
5

まず、クラスについて知っておく必要のある最も重要なことである、サブクラスも常に存在するという基本から始めましょう。スーパークラスの完全なインスタンス。したがって、スーパークラスでフィールド変数を定義する場合、サブクラスのインスタンスを作成すると、このフィールドは常に作成されます。super.getVariable()を使用して、サブクラス内のその変数を取得し、フィールドを再利用できます(クラス変数、フィールド、フラグ、これはオブジェクト指向プログラミングでもすべて同じです)。ただし、外部からsubclassInstance.getVariable()を呼び出すだけでも、同じフィールドを取得できます(サブクラスで変更する必要はありません)。したがって、通常は外部からスーパークラス(抽象クラ​​スを含む!)のフィールドを取得/設定するだけなので、サブクラスで「スーパー」を呼び出す必要はほとんどありません。フィールド変数は常にプライベートとして設定する必要があるため、フィールド変数にアクセスするために「スーパー」を呼び出さないことを常にお勧めします(スーパーでも可能であるため)protectedsuper.getField()のようなメソッドは、煩わしいですが必要です)。

それでは、データモデルから始めましょう。modelAとmodelBがあります。それらをスーパークラスから、または単にObjectから継承することができます。ただし、Objectからのみ継承する場合は、単純な(Java!)インターフェースを定義し、このインターフェースをmodelAおよびmodelBに実装できます。次に、これら2つのクラスをインターフェイスを介して処理します(これらは両方とも「インターフェイスオブジェクト」であり、一般的に処理できます)。modelAとmodelBで構成されるmodelCがある場合は、modelC内で両方のモデルのインスタンス(「逆参照」とも呼ばれる)を使用するだけで、これ以上のコードは必要ありません。したがって、このモデルはできるだけ小さくシンプルなもの(「Bean」)を選択できます。コンポーネントベースの実装。これは、データ構造の通常の方法です。

GUIまたはフォームがある場合は、見た目が異なります。おそらく多くの共通のコードがあり、このコードを数十の異なるクラスに分割してから、コントローラー/プレゼンタークラスにコンポーネントをまとめたくないでしょう。したがって、すべての共有フィールド/フラグと、それらにアクセスして変更するためのいくつかのメソッドを含む抽象クラスを定義できます。次に、formAとformBからこれらの一般的なメソッドを呼び出し、コードを再利用します。

集合論はあなたに何かを言いますか?AとBの2つの円と、AとBの共通部分があります。抽象クラスは交差であり、サブクラスformAとformBは集合の違いです。正しいプログラムをコーディングすることを学ぶことは...集合論を理解することです。;-)

あなたの言葉で言うと、Foo形式のコードのほとんどは抽象的なスーパークラスFoobarにあり、このクラスはABを処理できます。次に、フォームFooとフォームBarを継承し、CはおそらくほとんどFoobarのサブセットのままですが、Cを処理するためにBarにハビリティを追加します。これがセットの違いです。

結局、BarはいつでもFooになることはなく、どちらもFoobarだけになります。あなたはいくつかの新しい共有フィールド/フラグを手に入れましたか?問題ありません。コードをFoobarに移行すると、両方のサブクラスで使用できます。

しかし、ある日、 Fooとは少し異なる3番目のコンポーネントFooTooが必要になった場合はどうでしょうか。問題ありません。FooBarFooを抽象クラスにしてFooBarを拡張し、 FooFooTooをサブクラスとして作成します。最終結果は、ルートが(通常)抽象クラスであり、リーフが実際のクラスであるクラスツリーになります。この構造により、コードの再利用が最大化されます(クラス名は変更されないため、変更する必要はありません)。クラスFooを使用している他のコード)。

あなたはあなたのフォーム(またはそのプレゼンター)にセッター/ゲッターを実装すると言いましたか?次に、モデルmodelAmodelBも使用する必要があります(ただし、CはBarでのみ使用され、Fooでは使用されないためmodelCは使用しません)。これらのモデルは、 FooBarの間でデータを転送するためのラッパーとして使用されます。また、このデータフローは、FooBarではなく、プレゼンターが制御する必要があります。

だからあなたの質問はこれになります:プレゼンターとは何ですか?実際、プレゼンターはGUIコンポーネントとデータモデルを実行するコードです。一方ではGUIコンポーネントを使用し、もう一方ではデータモデルのゲッター/セッターを使用するのは「フレーム」です。これは、GUIレイヤーとデータレイヤーの2つのレイヤーの間、さらには異なるGUIコンポーネントと異なるデータモデルの間のミドルウェアです。

したがって、通常、それを行う方法は2つしかありません。プレゼンター/コントローラーを使用しない場合と使用する場合です。これがないと、多くのswingコンポーネントコードをプレゼンタークラスにコピーアンドペーストする必要があります。だから何?そうです、スイングコンポーネントを使用するときは、常に M)VPパターンを使用しているので、別の方法で使用することはできません。

つまり、フレームワークを作成するには、フレームワークを操作するプログラマーに最大限の柔軟性を提供したいので、コンポーネント設計を使用する必要があります。しかし、生産的なシステムはフレームワークと同じではありません。それは多くのフレームワークプログラマーが考えるエラーです。したがって、フレームワークプログラマーが「コンポーネントベースの実装がすべてだ」と言った場合、彼は間違っている可能性があります。彼がフレームワークのコンポーネントをプログラミングしているからといって、これはプレゼンターに対して同じことをする必要があるという意味ではありません。

それで、GUIコンポーネントとGUIプレゼンテーションについて話し始めます。メソッド「include(...)」を使用して、単純なHTMLサイトを取得し、そこから数十のPHPサイトを作成できるため、「できるだけ多くのプレゼンターコンポーネント」を作成することができます。ただし、コンポーネントベースの設計では、コードの保守性が常に向上するとは限りません。1つのクラスだけで何かを行うことができ、それを明確で読みやすいものにすることができれば、10ではなく1つのクラスを使用します。1つのプレゼンター=1つのクラス、またはより具体的には、1つのGUIフレーム/タブ=1つのクラス。

繰り返しになりますが、2つの類似したフレーム/タブがありますが、それらが同じではない場合は、どうすればよいですか?共有コードを抽象クラスに移行し、2つのサブクラスを作成しますよね?いいえ、最初にそれらのGUIが何を共有するかを考える必要があります。彼らは旗を共有しましたか?したがって、フラグを抽象スーパークラスに移動します。しかし、それらは単に異なる動作をしますか?同じクラス内に2つの異なるメソッドを実装するだけで、必要なときにいつでも呼び出すことができます。それが最も重要です。

あなたの言葉でそれを言うと:GUI Pres1FooBarABCをすべて使用します。また、GUI Pres2は、フラグが異なる場合にのみ別のクラスに属します。それ以外の場合は、フラグPres1とフラグPres2を設定し、メソッド内でこのフラグを確認します。によってif(flag="Pres1"){} else if(flag="Pres2"){}。このように、最大​​限の柔軟性とコードの再利用性が得られます。

Javaクラスを、柔軟性がなく、再利用できず、変更できないものと見なさないでください。優れたプログラマーであれば、必要に応じて、プログラムの構造を直感的に変更できます。人工的な概念を考える必要はありません。オブジェクト指向プログラミングパターンを理解するだけです。

「コンポーネント」は常に「コンストラクターを持つもの」を意味します。ただし、コンポーネントの代わりにメソッドを使用して何かを行う場合もあります。したがって、誰かが「コンポーネントの設計がすべてだ」と言った場合、彼は「コンストラクターベースの設計がすべてだ」と言います。ただし、コンストラクターを作成するには、フィールド変数/フラグが必要です。フィールド変数がなければ、そのためだけに新しいクラスを作成することはまったく意味がありません。

重要:「コンポーネント」はメソッドではないことを意味します。GUIクラス内で非常に簡単に処理する多くのメソッドを使用することは明らかです。したがって、最終的には、いくつかのメソッドを呼び出すだけです。したがって、コンポーネントとメソッドを混在させないでください。使用するコード行を減らすことがすべてであるため、私は常に強力なメソッド指向の設計を提案します。したがって、できるだけ多くのメソッドを定義します...ただし、できるだけ少ないクラス/コンポーネントも定義します。

于 2013-02-04T05:24:37.523 に答える
4

もちろん、FooがBarを拡張しない場合は、ゲッターとセッターが追加されるため、コードを追加する必要があります。しかし、非常に大きな利点は、FooがBarに依存しなくなったことです。これはごくわずかなメリットに見えるかもしれませんが、50を超えるクラスで継承を使用するとどうなるか想像してみてください...ロジックがないと地獄になり、使用するコンポーネントを変更する必要がある場合は非常に複雑になります他のいくつかのクラスによって拡張されたクラスで。

保守上の理由から、継承の使用は避けてください。あなたが言ったように、「バーはフーではない」ので、バーはフーを拡張するべきではありません。私が経験したことでは、継承は決して良い解決策ではなく、クラスのファミリーにのみ使用する必要があります(たとえば、複合パターンを使用する場合)。

于 2013-02-01T20:55:37.537 に答える