モデルとビューを扱うときの切り替えとポリモーフィズム


12

私の問題に対するより良い解決策を見つけることができません。要素のリストを表示するView Controllerがあります。これらの要素は、B、C、Dなどのインスタンスになり、Aから継承できるモデルです。したがって、そのView Controllerでは、各項目はアプリケーションの異なる画面に移動し、ユーザーがそれらのいずれかを選択するとデータを渡す必要があります。私の頭に浮かぶ2つの選択肢は次のとおりです(構文を無視してください、それは特定の言語ではありません)

1)スイッチ(私はそれが悪いことを知っています)

//inside the view controller
void onClickItem(int index) {
    A a = items.get(index);

    switch(a.type) {
         case b:
             B b = (B)a;
             go to screen X;
             x.v1 = b.v1; // fill X with b data
             x.v2 = b.v2; 
         case c:
             go to screen Y;
         etc...
    }
}

2)多型

//inside the view controller
void onClickItem(int index) {
    A a = items.get(index);
    Screen s = new (a.getDestinationScreen()); //ignore the syntax
    s.v1 = a.v1;   // fill s with information about A
    s.v2 = a.v2;
    show(s);
}

//inside B
Class getDestinationScreen(void) {
    return Class(X);
}

//inside C
Class getDestinationScreen(void) {
    return Class(Y);
}

解決策2の問題は、B、C、Dなどがモデルであるため、ビューに関連するものを知らないことです。それともその場合彼らはすべきですか?

回答:


6

ここでは、おそらくビジターパターンの実装が役立つと思います。B、C、およびDクラスは、ビュータイプを決定するために「アクセス」する必要がありますが、ビューについて何も知る必要はありません。ViewFactory(下)はアイテムにアクセスし、ポリモーフィズムを使用して、構築する正しいビューを決定します。switchステートメントはありません。何を構築するかを決定するためにモデル内部について尋ねることはありません。ビジターインターフェイスは、ポリモーフィズムを使用して、ビューの正しいセッターを選択します。セッターは、特定のビュータイプ(XまたはYまたはZ)のコンストラクターにアイテムを渡すことができ、そのビューはアイテムからフィールドを設定できます。

   //inside the view controller
   void onClickItem(int index) {
      ViewFactoryVisitable a = items.get(index);
      ViewFactory aViewFactory = new ViewFactory(
      s = aViewFactory.getViewFor(a);
      show(s);
   }

--------

//Element interface
public interface ViewFactoryVisitable
{
    public void accept(ViewFactory theViewFactory);
}

---------

public interface ViewFactoryVisitor
{
   // one for each concrete type, polymorphism will choose correct setter
   public set setViewFor(B b);
   public set setViewFor(C c);
   public set setViewFor(D d);
}

--------

// B, C, D must implement this visitable interface
class B implements ViewFactoryVisitable
{ 
   ...

   //accept the ViewFactory as a visitor
   public void accept(ViewFactoryVisitor theViewFactoryVisitor)
   {
      theViewFactoryVisitor. setViewFor(this);
   }

   ...
} 

--------

class ViewFactory implements ViewFactoryVisitor
{
   ViewFactory(ViewFactoryVisitable theItem) {
      theItem.accept(this);
   }

   private View mView = null;
   ...

   public void setViewFor(B b) {
      // construct a view x and populate with data from b
      mView = new ViewX(b); 
   }

   public void setViewFor(C c) {
      mView = new ViewY(c); 
   }

   public void setViewFor(D d) {
      mView = new ViewZ(d); 
   }

   View getView() {
      return mView;
   }

} 

1
acceptの実装が「theViewFactoryVisitor.setViewFor(this);」ではない場合 愚かだとすみません!
ライアン

@ライアン良いキャッチ。この間違いは3年間続いています!
チャッククルチンガー

1

答えよりもコメントの方が多いですが、それは投げるだけだと思います。どちらのビューは、それが画面(スイッチ)を選択できるようにモデルについてのすべてを知っていなければならないか、モデルがようビューについてのすべてを知っている必要があり、それが画面(多型)を選択することができます。時間の経過とともに最もシンプルになると思うものを選択する必要があると思います。質問に対する正しい答えはありません。(誰かが間違っていることを証明してくれることを願っています。)私は自分自身でポリモーフィズムに傾倒しています。

この問題に少しぶつかりました。最も迷惑なケースは、放浪者のクラスで、そのインスタンスは地図をさまよっていました。描画するために、ディスプレイはWandererについて知る必要があるか、Wandererはディスプレイについて知る必要があります。問題は、2つのディスプレイがあったことです(さらに表示が増えていました)。さまざまなWandererサブクラスの数が大きく成長しているため、Wandererサブクラスに描画コードを配置しました。つまり、各大クラスはGraphics2Dを知っておく必要があると正確に一つの方法ありましたし、 Java3Dのを知っているために必要なことを正確に一つの方法を。醜い。

最終的にクラスを分割し、2つの並列クラス構造を与えました。放浪のクラスは、グラフィックスを知ることから解放されたが、それでも多くの放浪者についてまともだったよりも知っておく必要DrawWandererクラスました、それは知っているために必要な2完全に異なるグラフィック環境(ビュー)(そしておそらくそれ以上)。(このクラス分割のアイデアは一種の答えかもしれないと思うが、実際にそれがするのは問題を少し含むことだけだ。)

これは、オブジェクト指向設計の非常に一般的で基本的な問題だと思います。


0

この場合、ポリモーフィズムを使用するよりも、スイッチを使用する方が良い選択肢だと思います。

これは非常に簡単なことなので、ポリモーフィズムを使用することで複雑になりすぎることはないと思います。

このブログ投稿でコインを作りたいです。Switchステートメントは、適切に使用する限り、必ずしも見苦しいとは限りません。そして、あなたの場合、コントローラで使用するためのそのようなモデルを抽象化するのはやり過ぎかもしれず、望ましくない結果をもたらすかもしれません。SRPに違反するような。


あなたの言ってる事がわかります。ポリモーフィズムが複雑すぎるとは思わない。そして私の場合のAクラスは抽象的ではなく、実際に使用されています。あなたの考えをありがとう、私はまだより良い解決策を待っており、ポリモーフィズムアプローチにもっと傾いています。
ラファエルオリベイラ

1
心配する必要はありません。問題について2セントを与えるだけです。モデルにビューロジックを配置する必要があるという問題を解決するために、モデルをビューロジックから解放するために、いつでもデコレータでラップすることができます。次に、モデルの代わりにデコレータクラスでポリモーフィズムを使用できます。
マル

0

解決策2の問題は、B、C、Dなどがモデルであるため、ビューに関連するものを知らないことです。

この懸念に同意します。また、コンボボックスにあるオブジェクトが動作することを少し心配しています。それがそれをやったことがない「悪いこと」かどうかはわかりませんが、それは私にとって不自然な選択のように思えます。

また、それはそうではなく、Aサブクラスは興味深い多態性を持つタイプです。興味深いタイプは実際にScreenです。この例でAは、Screen作成を通知する情報を保持するクラスにすぎません。

コンボボックスにa.type返すもののリストを含めると、switchステートメントがより自然に見えます。ただし、クリックイベントハンドラーに配置する代わりに、に配置しScreenFactoryます。次にあります:

//inside the view controller
void onClickItem(int index) {
    A a = items.get(index);

    s = _screenFactory.GetScreen(a);
    show(s);
    }
}

//inside a ScreenFactory implementation
internal Screen GetScreen(A typeIndicator)
{
switch(a.type) {
     case b:
         return new ScreenX();
     case c:
         return new ScreenY();
     etc...        
}

これにより、画面構築の動作をテストでき、UIからいくつかの機能をうまく引き出します。ビューのレイヤー化はそのまま維持されます。おそらくA、サブクラスtypeをそれらが含むフラグに折りたたむことができる場合、それはデザインを単純化します。


tallsethにご連絡いただきありがとうございます。残念ながら、モデルには、タイプや宛先ビューコントローラだけでなく、多くの情報が含まれています。また、言及しなかったが、ScreenX、ScreenYなどは、構築時にB、C、Dに関する情報を受け取る必要があるため、型を渡すだけのファクトリを使用することはできず、モデル自体を渡す必要があります。
ラファエルオリベイラ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.