2012-08-22 9 views
4

내가 case 문을 통해 반복해서 같은 일에 근접하고 있던 일부 레거시 코드 리팩토링 오전 :이지옥 또는 수용 가능한 디자인?

(SameInterfaceCast).SetProperties(Prop1,Prop2,Prop3); 
될 수 있도록 그래서

switch(identifier) 
    case firstIdentifier: 
     (SomeCast).SetProperties(Prop1,Prop2,Prop3); 
     break; 
    ... 
    case anotherIdentifier: 
     (SomeDifferentCast).SetProperties(Prop1, Prop2, Prop3); 
     break; 

을, 나는 고유의 인터페이스를 만들려고

그러나 일부 항목은 모든 속성을 사용하지 않는 것으로 나타났습니다. 그래서 나는 더 이런 일을 생각하기 시작했다 :

if(item is InterfaceForProp1) 
    (InterfaceForProp1).SetProp1(Prop1); 
if(item is InterfaceForProp2) 
    (InterfaceForProp2).SetProp2(Prop2); 
if(item is InterfaceForProp3) 
    (InterfaceForProp3).SetProp3(Prop3); 

하고이 같은 클래스를 만들 수 있습니다

public class MyClassUsesProp2And3 : InterfaceForProp2, InterfaceForProp3 

그러나, 내가 지나치게이 코드를 세분화하고 두렵다을하고 수 풍선 너무 많이. 어쩌면 본질적으로 하나의 방법 인터페이스가 될 것을 너무 두려워해서는 안되지만,이 길을 가기 전에 디자인 패턴을 놓치고 있는지 알고 싶습니다.

모든 속성이 고유 종류

UPDATE (꽤 권리 맞게 내 마음에 튀어하지만 있던 유일한 사람은 Decorator 또는 Composite 패턴이었다).

궁극적으로 이것은 의존성 주입의 한 형태입니다. 코드는 Ninject와 같은 것을 사용하기에는 너무 엉망이지만 나중에는 이들 중 일부를 없애고 주입 컨테이너를 사용할 수도 있습니다. 현재 변수를 설정하는 것 이상의 일을하는 논리가 있습니다. 이것은 모든 레거시 코드이며, 조금씩 조금씩 정리하려고하고 있습니다.

+0

동일한 유형의 Prop1, Prop2 및 Prop3입니까? Interface1Prop1, InterfaceForProp2 및 InterfaceForProp3의 수퍼 클래스를 원한다고 생각하기 때문에 Prop1, Prop2 및 Prop3 유형의 인수를 취하는 SetProp() 메서드가 있습니다. – dmn

+0

아마도 Prop1, Prop2 및 Prop3은 아직 개체로 표시되지 않은 도메인 개념의 속성입니다. 그러면 인터페이스가 단순해질 수 있습니다. – neontapir

+0

@dmn 방금 모든 유형이 다르다는 것을 추가하여 질문을 업데이트했습니다. 그들이 모두 같았다면 이것은 케익이 될 것입니다. –

답변

1

기본적으로 액터 (setProp 메소드로 유형)를 '액터'유형으로 만들고 속성 (prop1 ... n)에 'Prop'과 동일한 유형을 작성하려고합니다. 당신이 instanceof를, 내가 방문자 패턴으로 이동하는 것입니다 생각할 수있는 유일한 방법을 사용하지 않도록하려면이 '소유'방문자를 만드는

actor.setProp(prop) 

에 코드를 감소시킬 것이다. 내 인생을 더 편하게하기 위해 템플릿 방법을 사용합니다. Java에서는 (Prop의 두 실제 종류에 대해) 이렇게 보일 것입니다.

class Actor { 

    protected void set(Prop1 p1) { 
     // Template method, do nothing 
    } 

    protected void set(Prop2 p2) { 
     // Template method, do nothing 
    } 

    public void setProp(Prop p) { 
     p.visit(this); 
    } 

    public interface Prop { 
     void visit(Actor a); 
    } 

    public static Prop makeComposite(final Prop...props) { 
     return new Prop() { 

      @Override 
      public void visit(final Actor a) { 
       for (final Prop p : props) { 
        p.visit(a); 
       } 
      } 
     }; 
    } 

    public static class Prop1 implements Prop { 
     public void visit(Actor a) { 
      a.set(this); 
     } 
    } 

    public static class Prop2 implements Prop { 
     public void visit(Actor a) { 
      a.set(this); 
     } 
    } 
} 

이이 같은 물건을 수행 할 수 있습니다

ConcreteActor a = new ConcreteActor(); 
    Prop p = Actor.makeComposite(new ConcreteProp1(42), new ConcreteProp2(-5)); 
    a.setProp(p); 

을 ... 슈퍼 좋은이다!

+0

이것을 올바르게 읽으면, 속성을 가지고있는만큼 많은 호출을해야하지만 여분의 if를 피할 수 있습니다 :'actor.setProp (Prop1); actor.setProp (Prop2);'등? 방문자 패턴을 알았지 만 그것을 사용하지는 않았으며 지금까지는 구현에 대해 매우 모호했습니다. 내가 사용법에 대해 정확하다면, 나는 정말로 이것을 좋아한다. 그러나 나는 현재 부동산 클래스에 대한 통제권을 가지고 있지 않다. 나중에 더 많은 리팩토링을 통해 나중에이 작업을 수행 할 것입니다. 당신이 최선을 설명했기 때문에 당신에게 수표를 주었고 그것은 내가 옳은 대답이라고 느낀 것입니다 (비록 내가 그것을 사용할 수 없더라도) –

+0

수표를 가져 주셔서 감사합니다!이는 방문자의 일반적인 사용은 아니지만 손에있는 문제를 해결합니다. 음, 네, 각각의 새 속성마다 새 집합()을 추가해야하지만 새 속성을 추가하는 경우 기본 더미 Actor 메서드에 새로운 더미 템플릿 메서드로 추가 한 다음 해당 개체에 대한 특정 구현을 추가하면됩니다. 새 속성에 응답 할 수 있어야하는 Actor의 하위 클래스입니다. 이것은 거의 최소한의 변경 시나리오입니다. –

+0

코드를 정리하고 좀 더 구체적으로 만들고 소품에서 컴포지트를 만드는 방법을 추가했습니다. 실제 속성 클래스를 제어 할 수 없더라도 리팩터링 할 때 항상이 구조로 래핑 할 수 있습니다. –

1

"올바른 답변"이 있는지는 모르겠지만 여기에 내가 할 일이 있습니다.

class Properties { 
    prop1 
    prop2 
    prop3 
} 

interface PropertySetable { 
    setProperties(Properties prop); 
} 
public class MyClassUsesProp2And3 implements PropertySetable { 
    setProperties(Properties prop) { 
     //I know I need only 2 and 3 
     myProp2 = prop.prop2; 
     myProp3 = prop.prop3; 
    } 

} 

호출 함수에서는 캐스트가 없어야합니다.

someFunc(..., PropertySetable, Properties,...) { 
     PropertySetable.setProperties(Properties); 
} 

이것이 기본 구조입니다.

속성을 캡슐화해야합니다. 속성을 비공개로 만들고 관련 생성자를 사용해야합니다. 또는 Builder 패턴을 사용하여 Properties ... 및 더 많은 것을 빌드하십시오.

+0

참고,하지만 당신은 여전히 ​​본질적으로 여기서도 똑같은 일을하고 있습니다. 나는 심지어 사용되지 않는 변수를 지나치는 것을 피하고 싶다. –

+1

아니요 ... 나는 클래스가 호출 코드가 아닌 필요한 것을 결정하도록하는 것을 선호합니다. 'Properties' 객체를 넘기는 것에 대한 당신의 반대는 무엇입니까? 반원들에게 필요한 것을 물어보고 그들에게 그것을주는 것은 나에게 낭비가된다. 모든 것을 취하고 필요한 것을 취하십시오. 유지 관리 가능성 때문에 개별 속성을 전달하는 것보다 낫습니다. 포인터/참조를 전달하는 경우 성능/메모리가 현명하므로 문제가되지 않습니다. – Chip

+0

+1 내가 더 생각하면할수록이 방법이 적합합니다. 방문자 패턴이 더 좋습니다. 그래서 내가 그것을 확인 표시 –

1

저는 처음부터 코드를 리팩터링하려는 이유에 따라 대답이 달라집니다.

  • 스위치 블록을 완전히 없애려면 3 가지 인수를 취하는 하나의 방법으로 인터페이스를 구현해야합니다. 각 클래스는 어느 인수가 적용되는지, 그리고 그러한 인수를 어떻게 처리해야하는지 파악해야합니다.
  • 전체 코드 양을 줄이려면 클래스 별 속성 설정 논리를 추가하는 것이 원래 switch 문보다 적다는 것을 확신하지 못합니다.
  • 편집기의 코드 완성 기능을 이용하려면 속성의 정확한 수와 올바른 방법을 삽입/인수 후 바로 각 클래스의 올바른 방법 (들)을 구현하고 인터페이스를 피하기 모두
+0

IMO, 세 번째 옵션이 전혀 이유가되지 않습니다. 코드를 변경하는 주된 이유는 코드를보다 관리하기 쉽고 읽기 쉽도록 만들어서 더 솔리드하게 만드는 것이 었습니다. 방문자 패턴이이를 수행하는 가장 좋은 방법입니다. –

1

visitor pattern 당신이 switch 문에 직면 할 때 표준 용액이며, 형변환. 각 사례에 대한 코드는 방문자 클래스에서 별도의 메소드로 이동합니다. 그리고 클래스는 각각 하나의 인터페이스 만 구현합니다 - 방문자를 받아들이는 방법은 accept입니다. 지금까지했던 것보다 많은 코드로 끝나지 만, 훨씬 더 깨끗한 코드를 읽게 될 것입니다.