Перемикайтеся на поліморфізм при роботі з моделлю та видом


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);" Вибачте, якщо я дурний!
Райан

@Ryan Хороший улов. Ця помилка тут вже 3 роки!
Чак Крутсінгер

1

Більше коментаря, ніж відповіді, але я думаю, що це підкидання. Або Вид повинен знати все про Модель, щоб він міг вибрати екран (перемикач), або Модель повинна знати все про Вид, щоб він міг вибрати екран (поліморфізм). Я думаю, що вам доведеться вибрати те, що, на вашу думку, буде найпростішим з часом; немає правильної відповіді на питання. (Я сподіваюся, що хтось може довести мене неправильно.) Сам я схиляюся до поліморфізму.

Я трохи натикаюся на цю проблему. Найбільш дратівливим випадком був клас Wanderer, екземпляри якого бродили про карту. Щоб намалювати його, потрібно знати або дисплей, який потрібно знати про Wanderer, або Wanderer. Проблема полягала в тому, що було два дисплеї (з більшою кількістю). Оскільки кількість різних підкласів Wanderer була великою і зростаючою, я помістив код малювання в підкласи Wanderer. Це означало, що кожен великий клас мав рівно один метод, який потрібно знати про Graphics2D і точно один метод, який потрібно знати про Java3D. Некрасивий.

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

Я думаю, що це дуже загальна і фундаментальна проблема об'єктно-орієнтованого дизайну.


0

Я думаю, що в цьому випадку перехід з комутатором - кращий варіант, ніж поліморфізм.

Це досить проста річ, тому я не думаю, що це повинно бути надто складним шляхом використання поліморфізму.

Я хотів би пограбувати в цій публікації в блозі . Висловлювання комутаторів не обов'язково негарні, якщо ви правильно їх використовуєте. І у вашому випадку абстрагування подібних моделей для використання в контролері може бути надмірним і може призвести до небажаних результатів. Як і порушення ЗРП.


Я бачу вашу думку. Ну, я не думаю, що поліморфізм є надскладним. І клас А в моєму випадку не абстрактний, він фактично використовується. Дякую за ваші думки, хоча я все ще чекаю кращого рішення та більш схильного до підходу поліморфізму.
Рафаель Олівейра

1
не хвилюйтесь, просто даю 2 мої центи на тему. Хоча для вирішення вашої проблеми з необхідністю вводити логіку перегляду у свої моделі, ви завжди можете просто обернути їх декораторами, щоб ваші моделі не залишалися логікою перегляду. Тоді ви можете використовувати поліморфізм на класах декораторів замість моделі.
Мару

0

Моя проблема з рішенням 2 полягає в тому, що оскільки B, C, D тощо є моделями, вони не повинні знати про пов'язані з переглядом речі.

Я погоджуюся з цим занепокоєнням. Мене також мало хвилює те, що предмети, які сидять у комбобоксі, матимуть поведінку. Я не впевнений, що це "погана річ", що ніколи цього не робив, мені це здається неприродним вибором.

Крім того, це не здається, Aі саме підкласи - це той тип, з яким у вас цікавий поліморфізм. Цікавий тип насправді Screen. У цьому прикладі A- просто клас, який містить інформацію для Screenстворення інформації.

Якщо ви змусили комбінований список містити список a.typeповернень, то оператор перемикання здається більш природним. Однак замість того, щоб помістити його прямо в обробник подій, натисніть його 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...        
}

Це дозволяє перевірити поведінку екрана, і добре витягне деякі функції з вашого інтерфейсу. Він зберігає ваш вигляд з виду недоторканим. Можливо, це спрощує ваш дизайн, якщо це означає, Aі підкласи можуть бути згорнуті на typeпрапор, який вони містять.


Дякую за високу відповідь. На жаль, моделі містять багато інформації, а не лише про їх тип або контролер подання призначення. Крім того, хоча я не згадував, ScreenX, ScreenY і т.д. повинні отримувати інформацію про B, C, D на будівництві, тому я не можу використовувати завод лише для передачі типу, мені потрібно передати саму модель.
Рафаель Олівейра
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.