Переключение против полиморфизма при работе с моделью и представлением

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 и т. Д. Являются моделями, они не должны знать о вещах, связанных с просмотром. Или они должны в этом случае?

Рафаэль Оливейра
источник

Ответы:

6

Я думаю, что, возможно, реализация шаблона посетителя будет полезна здесь. Классы B, C и D должны быть «посещены» для определения типа представления, но не должны ничего знать о представлениях. ViewFactory (ниже) будет посещать элемент и использовать полиморфизм для определения правильного представления для построения. Нет операторов переключения. Не нужно спрашивать о внутренних моделях, чтобы решить, что строить. Интерфейс посетителя использует полиморфизм, чтобы выбрать правильный установщик для представления. Установщик может передать элемент в конструктор определенного типа представления (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, либо Wanderer должен был знать о дисплее. Проблема была в том, что было два дисплея (и еще больше). Поскольку число различных подклассов Wanderer было большим и растущим, я поместил код для рисования в подклассы Wanderer. Это означало, что у каждого большого класса был ровно один метод, который должен был знать о Graphics2D, и ровно один метод, который должен был знать о Java3D. Некрасиво.

В итоге я разделил класс, дав мне две параллельные классовые структуры. Класс Wanderer был освобожден от знаний о графике, но классу DrawWanderer по-прежнему нужно было знать о Wanderer больше, чем было прилично, и ему нужно было знать о двух (и, возможно, более) совершенно разных графических средах (представлениях). (Я полагаю, что эта идея расщепления класса может быть своего рода ответом, но все, что она действительно делает, это немного сдерживает проблему.)

Я думаю, что это очень общая и фундаментальная проблема объектно-ориентированного проектирования.

RalphChapin
источник
0

Я думаю, что использование переключателя - лучший вариант, чем использование полиморфизма для этого случая.

Это довольно простая вещь, поэтому я не думаю, что это может быть слишком сложно при использовании полиморфизма.

Я хотел бы отметить в этом сообщении в блоге . Операторы Switch не обязательно безобразны, если вы правильно их используете. И в вашем случае, абстрагирование таких моделей для использования в контроллере может быть излишним и может привести к нежелательным результатам. Вроде нарушает СРП.

Maru
источник
Я понимаю вашу точку зрения. Ну, я не думаю, что полиморфизм слишком сложен. И класс A в моем случае не является абстрактным, он фактически используется. Спасибо за ваши мысли, хотя я все еще жду лучшего решения и более склонен к подходу полиморфизма.
Рафаэль Оливейра
1
не беспокойся, просто дай 2 цента по этому вопросу. Хотя для решения вашей проблемы с необходимостью использования логики представления в ваших моделях, вы всегда можете просто обернуть их декораторами, чтобы ваши модели оставались свободными от логики представления. Тогда вы можете использовать полиморфизм в классах декоратора вместо модели.
Мару
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...        
}

Это позволяет вам протестировать поведение построения экрана и приятно вытянуть некоторые функциональные возможности из вашего пользовательского интерфейса. Он сохраняет ваши слои View без изменений. Возможно, это упрощает ваш дизайн, если это означает, что Aподклассы можно свернуть в typeфлаг, который они содержат.

tallseth
источник
Спасибо за ответ. К сожалению, модели содержат много информации, а не только их тип или назначение контроллера. Кроме того, хотя я и не упомянул, ScreenX, ScreenY и т. Д. Должны получать информацию о B, C, D при построении, поэтому я не могу использовать фабрику, только передавая тип, мне нужно передать саму модель.
Рафаэль Оливейра