모델과 뷰를 다룰 때 스위치 대 다형성


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 (아래)는 아이템을 방문하고 다형성을 사용하여 빌드 할 올바른 뷰를 결정합니다. 스위치 문이 없습니다. 빌드 할 모델을 결정하기 위해 모델 내부에 대해 묻지 않습니다. 방문자 인터페이스는 다형성을 사용하여보기에 대한 올바른 세터를 선택합니다. setter는 항목을 특정보기 유형 (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

@Ryan Good catch. 이 실수는 3 년 동안 여기에있었습니다!
Chuck Krutsinger

1

답변보다 더 많은 의견이 있지만, 나는 그것을 던지기라고 생각합니다. 어느보기는 화면 (스위치)를 선택할 수 있도록 모델에 대한 모든 것을 알고있다 또는 모델 있도록보기에 대한 모든 것을 알고있다 화면 (다형성)을 선택할 수 있습니다. 시간이지나면서 가장 단순하다고 생각하는 것을 선택해야한다고 생각합니다. 더 없다 바로 질문에 대한 대답은. (누군가 나를 잘못 증명할 수 있기를 바랍니다.) 나는 다형성에 의지합니다.

나는이 문제에 약간 부딪쳤다. 가장 성가신 사례는 Wanderer 클래스였으며, 인스턴스는지도에 대해 방황했습니다. 그림을 그리려면 디스플레이가 Wanderer에 대해 알아야하거나 Wanderer가 디스플레이에 대해 알아야했습니다. 문제는 두 개의 디스플레이가 있다는 것입니다 (더 많은 디스플레이가 나타남). 서로 다른 Wanderer 서브 클래스의 수가 많고 늘어나면서 드로잉 코드를 Wanderer 서브 클래스에 넣었습니다. 즉, 각각의 큰 클래스에는 Graphics2D에 대해 알아야 할 메소드가 정확히 하나 있고 Java3D에 대해 알아야 할 메소드가 하나만있었습니다. 추한.

나는 클래스를 분할하여 두 개의 병렬 클래스 구조를 제공했습니다. 방랑자 클래스는 그래픽에 대해 알고에서 해방되었지만, DrawWanderer 클래스는 여전히 더 방랑자에 대한 괜찮은보다 알 필요 하고 그것에 대해 알 필요가 (어쩌면 더) 완전히 다른 그래픽 환경 (조회수). (이 분류 아이디어는 일종의 대답 일 수 있다고 생각하지만 실제로는 문제가 조금 포함되어 있다고 생각합니다.)

이것이 객체 지향 디자인의 매우 일반적이고 근본적인 문제라고 생각합니다.


0

이 경우 다형성을 사용하는 것보다 스위치를 사용하는 것이 더 나은 옵션이라고 생각합니다.

그것은 매우 간단한 일이므로 다형성을 사용하여 지나치게 복잡해 져야한다고 생각하지 않습니다.

블로그 게시물 에서 동전을 만들고 싶습니다 . 스위치 문은 제대로 사용하는 한 반드시 추악한 것은 아닙니다. 그리고 귀하의 경우, 컨트롤러에서 사용하기위한 것과 같은 추상 모델은 과도하게 사용되어 원치 않는 결과를 초래할 수 있습니다. SRP를 위반하는 것과 같습니다.


너의 의도를 알 겠어. 글쎄, 나는 다형성이 지나치게 복잡하다고 생각하지 않습니다. 그리고 제 경우에는 A 클래스가 추상적이 아니며 실제로 사용됩니다. 나는 여전히 더 나은 해결책을 기다리고 있으며 다형성 접근법에 더 기울어지고 있지만 당신의 생각에 감사드립니다.
Raphael Oliveira

1
걱정하지 않아도됩니다. 뷰 로직을 모델에 넣어야하는 문제를 해결하기 위해 항상 데코레이터로 래핑하여 모델에 뷰 로직이 없도록 할 수 있습니다. 그런 다음 모델 대신 데코레이터 클래스에서 다형성을 사용할 수 있습니다.
Maru

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에서 일부 기능을 멋지게 끌어낼 수 있습니다. View 레이어링을 그대로 유지합니다. A서브 클래스가 type포함 된 플래그 로 축소 될 수있는 경우 디자인을 단순화 할 수 있습니다 .


키 큰 응답에 감사드립니다. 불행히도 모델에는 유형이나 대상 뷰 컨트롤러뿐만 아니라 많은 정보가 포함되어 있습니다. 또한 언급하지 않았지만 ScreenX, ScreenY 등은 건설시 B, C, D에 대한 정보를 받아야하므로 유형을 전달하는 공장 만 사용할 수 없으므로 모델 자체를 전달해야합니다.
Raphael Oliveira
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.