Alternar vs Polimorfismo ao lidar com modelo e visualização


12

Não consigo descobrir uma solução melhor para o meu problema. Eu tenho um controlador de exibição que apresenta uma lista de elementos. Esses elementos são modelos que podem ser uma instância de B, C, D, etc e herdar de A. Portanto, nesse controlador de exibição, cada item deve ir para uma tela diferente do aplicativo e passar alguns dados quando o usuário seleciona um deles . As duas alternativas que me vêm à cabeça são (ignore a sintaxe, não é um idioma específico)

1) interruptor (eu sei que é uma porcaria)

//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) polimorfismo

//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);
}

Meu problema com a solução 2 é que, como B, C, D, etc. são modelos, eles não devem saber sobre coisas relacionadas à exibição. Ou deveriam, nesse caso?

Respostas:


6

Penso que talvez uma implementação do padrão de visitantes seja útil aqui. As classes B, C e D precisariam ser "visitadas" para determinar o tipo de exibição, mas não precisariam saber nada sobre as exibições. O ViewFactory (abaixo) visitaria o item e usaria o polimorfismo para determinar a exibição correta a ser construída. Nenhuma declaração de opção. Não é necessário perguntar sobre os modelos internos para decidir o que construir. A interface do visitante usa polimorfismo para selecionar o configurador correto para a visualização. O setter pode passar o item para o construtor do tipo de vista específico (X ou Y ou Z) e essa exibição pode preencher seus campos a partir do item.

   //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
A implementação de accept não deve ser "theViewFactoryVisitor.setViewFor (this);" Desculpe se estou sendo idiota!
Ryan

@Ryan Boa captura. Este erro está aqui há 3 anos!
Chuck Krutsinger

1

Mais um comentário do que uma resposta, mas acho que é uma brincadeira. Ou o View tem de saber tudo sobre o modelo para que ele possa escolher a tela (switch) ou o modelo tem de saber tudo sobre a vista de modo que pode escolher a tela (polimorfismo). Eu acho que você tem que escolher o que você acha que será o mais simples ao longo do tempo; não há certo resposta para a pergunta. (Espero que alguém possa me provar errado.) Eu mesmo me inclino ao polimorfismo.

Eu topo um pouco com esse problema. O caso mais irritante foi uma classe Wanderer, cujas instâncias vagavam por um mapa. Para desenhá-lo, o monitor precisava saber sobre o Wanderer ou o Wanderer precisava saber sobre o monitor. O problema era que havia duas telas (com mais aparecendo). Como o número de diferentes subclasses Wanderer era grande e crescente, coloquei o código de desenho nas subclasses Wanderer. Isso significava que cada classe grande tinha exatamente um método que precisava saber sobre Graphics2D e exatamente um método que precisava saber sobre Java3D. Feio.

Acabei dividindo a classe, dando-me duas estruturas de classes paralelas. A classe Wanderer ficou livre de conhecer os gráficos, mas a classe DrawWanderer ainda precisava saber mais sobre o Wanderer do que era decente e precisava conhecer dois (e talvez mais) ambientes gráficos completamente diferentes (Views). (Suponho que essa ideia de dividir a classe possa ser uma resposta, mas tudo o que realmente faz é conter um pouco o problema.)

Eu acho que esse é um problema muito geral e fundamental do design orientado a objetos.


0

Eu acho que ir com o switch é uma opção melhor do que ir com o polimorfismo para este caso.

É uma coisa bastante simples de se fazer, então não acho que tenha que ser complicado demais pelo uso de polimorfismo.

Eu gostaria de cunhar nesta postagem do blog . As instruções de troca não são necessariamente feias, desde que você as use corretamente. E no seu caso, abstrair modelos como o para uso em um controlador pode ser um exagero e gerar resultados indesejados. Como violar o SRP.


Eu entendo o seu ponto. Bem, não acho que o polimorfismo seja complicado demais. E a classe A no meu caso não é abstrata, é de fato usada. Obrigado por seus pensamentos, embora eu ainda esteja esperando por uma solução melhor e mais inclinado à abordagem do polimorfismo.
Raphael Oliveira

1
não se preocupe, apenas dando meus 2 centavos sobre o assunto. Embora para resolver seu problema com a necessidade de colocar a lógica de visualização em seus modelos, você sempre pode envolvê-los com decoradores para que seus modelos fiquem livres da lógica de visualização. Em seguida, você pode usar o polimorfismo nas classes decoradoras em vez do modelo.
Maru

0

Meu problema com a solução 2 é que, como B, C, D, etc. são modelos, eles não devem saber sobre coisas relacionadas à exibição.

Eu concordo com esta preocupação. Também estou um pouco preocupado que objetos que estão em uma caixa de combinação tenham comportamento. Não tenho certeza de que seja uma "coisa ruim" nunca ter feito isso, apenas parece uma escolha não natural para mim.

Além disso, não parece Ae suas subclasses são do tipo com o qual você tem polimorfismo interessante. O tipo interessante é realmente Screen. Neste exemplo, Aé apenas uma classe que contém informações para informar a Screencriação.

Se você fizer com que a caixa de combinação contenha uma lista de quaisquer a.typeretornos, uma instrução switch parece mais natural. No entanto, em vez de colocá-lo corretamente no manipulador de eventos click, eu o colocaria em a ScreenFactory. Então você tem:

//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...        
}

Isso permite testar o comportamento de criação da tela e extrai algumas funcionalidades da sua interface do usuário. Ele mantém as camadas da sua vista intactas. Talvez isso simplifique seu design, se isso significa que as Asubclasses podem ser recolhidas na typebandeira que elas contêm.


Obrigado pela resposta tallseth. Infelizmente, os modelos contêm muitas informações, não apenas o tipo ou o controlador de exibição de destino. Além disso, embora eu não tenha mencionado, o ScreenX, ScreenY etc. precisam receber informações sobre B, C, D na construção, portanto, não posso usar uma fábrica apenas passando o tipo, preciso passar o próprio modelo.
Raphael Oliveira
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.