2009-03-25 4 views
2

수정할 수없는 클래스에서 다형성 동작을 구현하는 가장 좋은 방법은 무엇입니까?작성하지 않은 클래스에서 다형성 사용

if(obj is ClassA) { 
    // ... 
} else if(obj is ClassB) { 
    // ... 
} else if ... 

확실한 대답은 기본 클래스에 가상 메서드를 추가하는 것입니다,하지만 불행히도 코드가 다른 어셈블리에 내가 수정할 수 없습니다 : 저는 현재 몇 가지 코드가 같이 있습니다. 위의 추악하고 느린 코드보다 이것을 처리 할 수있는 더 좋은 방법이 있습니까?

+0

if 문이 두 개인 경우 리팩터링이 지나치게 복잡 할 수 있습니다. 당신은 가끔씩 Patterns-Happy가 될 필요는 없습니다.) – Sung

답변

9

흠 ... Adapter에 더 적합한 것 같다.

public interface ITheInterfaceYouNeed 
{ 
    void DoWhatYouWant(); 
} 

public class MyA : ITheInterfaceYouNeed 
{ 
    protected ClassA _actualA; 

    public MyA(ClassA actualA) 
    { 
     _actualA = actualA; 
    } 

    public void DoWhatYouWant() 
    { 
     _actualA.DoWhatADoes(); 
    } 
} 

public class MyB : ITheInterfaceYouNeed 
{ 
    protected ClassB _actualB; 

    public MyB(ClassB actualB) 
    { 
     _actualB = actualB; 
    } 

    public void DoWhatYouWant() 
    { 
     _actualB.DoWhatBDoes(); 
    } 
} 

많은 코드처럼 보이지만 원하는 코드에 훨씬 가까운 클라이언트 코드가됩니다. 또한 실제로 어떤 인터페이스를 사용하고 있는지 생각해 볼 수 있습니다.

+0

정말요? 방법? zildjohn이 적응하려고하는 다른 인터페이스는 무엇입니까 ...? 방문자가 잘 맞습니다. –

5

Visitor 패턴을 확인하십시오. 이렇게하면 클래스를 변경하지 않고도 클래스에 가상 메서드를 추가 할 수 있습니다. 작업중인 기본 클래스에 Visit 메서드가없는 경우 동적 캐스팅과 함께 확장 메서드를 사용해야합니다. 여기에 몇 가지 예제 코드는 다음과 같습니다

public class Main 
{ 
    public static void Example() 
    { 
     Base a = new GirlChild(); 
     var v = new Visitor(); 
     a.Visit(v); 
    } 
} 

static class Ext 
{ 
    public static void Visit(this object b, Visitor v) 
    { 
     ((dynamic)v).Visit((dynamic)b); 
    } 
} 

public class Visitor 
{ 
    public void Visit(Base b) 
    { 
     throw new NotImplementedException(); 
    } 

    public void Visit(BoyChild b) 
    { 
     Console.WriteLine("It's a boy!"); 
    } 

    public void Visit(GirlChild g) 
    { 
     Console.WriteLine("It's a girl!");    
    } 
} 
//Below this line are the classes you don't have to change. 
public class Base 
{ 
} 

public class BoyChild : Base 
{ 
} 

public class GirlChild : Base 
{ 
} 
+1

위키 피 디아를 읽은 후에는 각 클래스에 콜백 메소드를 추가해야하지만 클래스를 수정할 수는 없습니다. 내가 잘못 읽고 있니? – zildjohn01

+0

이것은 동적 디스패치를 ​​제공하지 않습니다. 방문자 패턴이 어떻게 작동하는지 실제로 이해합니까? – jwg

+0

동적 발송을 시연하는 코드를 추가했습니다. – RossFabricant

0

표준 접근법은 보호 된 인스턴스 변수로 "상속"하려는 클래스를 래핑 한 다음 래핑 된 모든 비공개 멤버 (메서드/속성/이벤트/등)를 에뮬레이션한다고 가정합니다. 클래스에있는 클래스입니다. 그런 다음이 클래스와 해당 멤버를 가상으로 표시하여 표준 다형성 기능을 사용할 수 있습니다.

다음은 내가 말하는 의미의 예입니다. ClosedClass은 액세스 권한이없는 코드가있는 어셈블리에 포함 된 클래스입니다. 당신이 잘 설계되었습니다 참조하는대로 조립하면

public virtual class WrapperClass : IClosedClassInterface1, IClosedClassInterface2 
{ 
    protected ClosedClass object; 

    public ClosedClass() 
    { 
     object = new ClosedClass(); 
    } 

    public void Method1() 
    { 
     object.Method1(); 
    } 

    public void Method2() 
    { 
     object.Method2(); 
    } 
} 

, 당신이 이제까지 appropiately 표시 될 것이다 액세스 할 수있는 모든 유형/회원 (추상은 가상은 밀봉),하지만 실제로 이것은 불행하게도이 아니다 (때로는 기본 클래스 라이브러리에서이 문제점을 경험할 수도 있습니다). 제 견해로, 래퍼 클래스는 여기로가는 길입니다. 그것의 이점을 가지고 있습니다 (심지어 을 상속 받기 원하는 클래스가 상속 가능한 임), 즉 클래스의 사용자가 액세스하지 못하도록하는 메소드의 수정자를 제거/변경하는 경우에도 마찬가지입니다. BCL의 ReadOnlyCollection<T>은 이에 대한 좋은 예입니다.

+0

이것은 기능상 harpo가 지적한 어댑터 패턴과 동일합니다. –

+0

그래, 코드 샘플을 포함하도록 자신의 게시물을 편집 한 후 깨달았다. 아 글쎄, 그의 원래 게시물이 나를 이길 것 같아 ... – Noldorin

-1

Extension methods은 기존 클래스에 메소드 서명을 추가하는 쉬운 방법을 제공합니다. 이 작업에는 3.5 프레임 워크가 필요합니다.

는 정적 유틸리티 클래스를 작성하고이 같은 것을 추가
public static void DoSomething(this ClassA obj, int param1, string param2) 
{ 
    //do something 
} 

이 페이지의 유틸리티 클래스에 대한 참조를 추가하고,이 방법은를 ClassA의 구성원으로 표시됩니다. 기존 메서드를 오버로드하거나 이러한 방식으로 새 메서드를 만들 수 있습니다.

+0

나는 그것을 좋아한다. 불행히도 .NET 2.0에 붙어 있습니다. 하지만 어쨌든 여기 upvote가있어 – zildjohn01

+1

나는 당신이 포인트를 놓친 것 같아 - 확장 방법은 가상 수 없습니다. –

+0

가상 방법은 요점이 아니 었습니다 - 다형성 행동이었고, 그것을 달성하는 다른 방법이 있습니다. 분명히 가상 메서드가있는 파생 클래스는 '올바른'방법이지만 확장 메서드는 수행해야하는 대상에 따라 빠르고 더러운 대안이 될 것입니다. –

0

Decorator 패턴을 살펴보십시오. Noldorin은 패턴의 이름을 밝히지 않고 실제로 설명했습니다.

장식자는 상속하지 않고 동작을 확장하는 방법입니다. Noldorin의 코드에서 변경할 유일한 것은 생성자가 꾸미고있는 객체의 인스턴스를 받아야한다는 것입니다.

관련 문제