web-dev-qa-db-ja.com

モデルとビューを処理するときにスイッチとポリモーフィズムを切り替える

私の問題に対するより良い解決策を見つけることができません。要素のリストを表示するビューコントローラーがあります。これらの要素は、B、C、Dなどのインスタンスになることができるモデルであり、Aから継承します。そのため、そのビューコントローラーでは、各項目はアプリケーションの異なる画面に移動し、ユーザーがそれらの1つを選択したときにいくつかのデータを渡す必要があります。私の頭に浮かぶ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などがモデルであるため、ビューに関連するものについて知らないはずです。それとも彼らはその場合にすべきですか?

12

おそらく visitor pattern の実装がここで役立つと思います。 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;
   }

} 
6

答えというよりはコメントのほうが多いですが、それはトスアップだと思います。ビューがモデルをすべて知っている必要があるため、画面を選択できる(スイッチ)か、モデルがビューをすべて知っている必要があるため、itが画面を選択できる(多態性)。時間が経つにつれ、最もシンプルになると思うものを選択する必要があると思います。 質問に対する答えはありません。 (誰かが私の間違いを証明してくれることを願っています。)私自身、多態性に傾いています。

私はこの問題に少しぶつかります。最も迷惑なケースはWandererクラスで、そのインスタンスはマップをさまよっていました。それを描くには、Wandererについて知る必要があるディスプレイか、Wandererがディスプレイについて知る必要がありました。問題は、ディスプレイが2つあったことです(さらに表示が増える予定です)。さまざまなWandererサブクラスの数が多く、増えているため、描画コードをWandererサブクラスに配置しました。つまり、大きなクラスごとに、Graphics2Dについて知る必要のあるメソッドが1つだけあったand Java3Dについて知る必要のあるメソッドが1つだけだった。醜い。

結局、クラスを分割して、2つの並列クラス構造を取得しました。 Wandererクラスはグラフィックについて知る必要がありませんでしたが、DrawWandererクラスはまともなandよりもWandererについてもっと知る必要がありましたtwoについて知る必要がありました)完全に異なるグラフィック環境(ビュー)。 (私はこのクラス分けのアイデアは一種の答えかもしれないと思いますが、実際に行うことは少し問題を含んでいるだけです。)

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

1
RalphChapin

このケースでは、ポリモーフィズムを使用するよりもスイッチを使用する方が良いオプションだと思います。

これはかなり簡単なことなので、多態性の使用によって過度に複雑になる必要はないと思います。

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

0
Maru

ソリューション2の私の問題は、B、C、Dなどがモデルであるため、ビューに関連するものについて知らないはずです。

私はこの懸念に同意します。また、コンボボックス内にあるオブジェクトが動作することにも少し懸念があります。それが一度も「悪いこと」だとは思わないのですが、私には不自然な選択のように思えます。

また、それはAのようではなく、そのサブクラスは、興味深いポリモーフィズムを持つタイプです。興味深い型は実際にはScreenです。この例では、AScreenの作成を通知する情報を保持するクラスにすぎません。

あなたがコンボボックスに何でものリストが含まれている場合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フラグに折りたたむことができる場合、設計を単純化します。

0
tallseth