处理模型和视图时切换与多态


12

我想不出更好的解决方案。我有一个显示元素列表的视图控制器。这些元素是可以作为B,C,D等的实例并从A继承的模型。因此,在该视图控制器中,每个项目都应转到应用程序的不同屏幕,并在用户选择其中的一个时传递一些数据。 。我想到的两个选择是(请忽略语法,它不是特定的语言)

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等是模型,因此他们不应该了解与视图相关的内容。还是在那种情况下应该?

Answers:


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
接受的实现不应该是“ theViewFactoryVisitor.setViewFor(this);”。对不起,如果我很傻!
瑞安

@Ryan好抓住。这个错误已经存在三年了!
Chuck Krutsinger '16

1

评论多于答案,但我认为这是一个折腾。无论是查看已经知道所有关于型号,因此可以选择屏幕(开关)或模型必须知道所有关于视图,以便可以选择屏幕(多态性)。我认为您必须选择随着时间的推移最简单的选择;这个问题没有正确的答案。(我希望有人能证明我错了。)我本人确实倾向于多态。

我碰到了这个问题。最烦人的案例是流浪者类,其实例在地图上徘徊。要绘制它,显示器需要了解Wanderer或Wanderer需要了解显示器。问题是有两个显示器(还有更多显示器)。随着不同的Wanderer子类的数量不断增加,我将绘图代码放入Wanderer子类中。这意味着每个大型类只有一个需要了解Graphics2D的方法,只有一个需要了解Java3D的方法。丑陋。

最后我确实拆分了班级,给了我两个平行的班级结构。Wanderer类摆脱了对图形的了解,但是DrawWanderer类仍然需要比体面的更多地了解Wanderer,并且它需要知道两个(也许更多)完全不同的图形环境(视图)。(我想这个“分裂课堂”的想法可能是一个答案,但实际上它所做的只是稍微有点问题了。)

我确实认为这是面向对象设计的一个非常普遍和基本的问题。


0

我认为在这种情况下,使用开关比使用多态性更好。

这是一件相当简单的事情,所以我认为不必因为使用多态而使它变得过于复杂。

我想在这篇博客文章中发表文章。只要您正确使用switch语句,它们就不一定丑陋。在您的情况下,像在控制器中使用的那样抽象模型可能是过大的选择,并且可能会带来不必要的结果。就像违反了SRP。


我明白你的意思。好吧,我不认为多态性过于复杂。在我看来,A类不是抽象的,实际上是使用的。感谢您的想法,尽管我仍在等待更好的解决方案,并且更倾向于采用多态方法。
拉斐尔·奥利维拉

1
不用担心,只给我2美分。尽管为了解决必须将视图逻辑放入模型中的问题,您始终可以只用装饰器将它们包装起来,从而使模型不受视图逻辑的影响。然后,您可以在装饰器类而不是模型上使用多态。
2013年

0

解决方案2的问题在于,由于B,C,D等是模型,因此他们不应该了解与视图相关的内容。

我对此表示赞同。我也有点担心组合框中的对象会发生行为。我不确定这是从来没有做过的“坏事”,对我来说这似乎是不自然的选择。

另外,它看起来也不像A,它的子类是您具有有趣的多态性的类型。有趣的类型实际上是Screen。在此示例中,A仅是一个类,其中包含用于通知Screen创建的信息。

如果使组合框包含任何a.type返回值的列表,则switch语句似乎更自然。但是,不是将其放在click事件处理程序中,而是将其放在中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它们所包含的标志中,则可能会简化您的设计。


谢谢您的回应。不幸的是,模型包含许多信息,而不仅仅是它们的类型或目标视图控制器。另外,尽管我没有提到,但ScreenX,ScreenY等需要在构造时接收有关B,C,D的信息,因此我不能仅使用传递类型的工厂,而需要传递模型本身。
拉斐尔·奥利维拉
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.