2017-01-04 3 views
1

완벽한 인터페이스를 만들기 위해 확장 방법을 사용합니다.어떻게</p> <pre><code>interface IFoo { void Do(); void Stuff(); } </code></pre> <p>이 (레거시) 클래스 FOO1, FOO2, Foo3 모든 IFoo가 구현하는 가정 해 봅시다 IFoo 인터페이스 주어

물건은 IFoo의 일부 방법을 사용하거나 더 새로운 클래스의 경우 DoStuff()를 사용하여 수행 할 수 있습니다. 사실, IFoo에서 DoStuff()가 "잊어 버린 것"처럼 보일 수 있습니다.

IFoo2를 구현 한 새로운 클래스 FooX (FooY, ...)가 있으며, 여기에는 DoStuff() 메서드가 있습니다.

IFoo 개체를 허용하고 해당 개체에 "할 일"을 할 수 있어야합니다.

//Let us assume foos = new IFoo[] {new Foo1(), new Foo2(), new Foo3(), new FooX()}; 

void MyMethod(IFoo[] foos){ 
    foreach(foo in foos){ 
     //DoStuff is not defined in IFoo 
     foo.DoStuff(); 
    } 
} 

그래서, 난 그냥도 FooX를 들어, 기존의 클래스

public static DoStuff(this IFoo self){ 
    self.Do(); 
    self.Stuff(); 
} 

불행하게도,이 확장 방법은 항상 호출

에 대한 IFoo에 확장 메서드 DoStuff()를 정의로 생각했다.

내가 그러나

public static DoSomeStuff(this IFoo self){ 
    if(self is IFoo2) { 
     (self as IFoo2).DoStuff() 
    } else { 
     self.Do(); 
     self.Stuff(); 
    } 
} 

void MyMethod(IFoo[] foos){ 
    foreach(foo in foos){    
     foo.DoSomeStuff(); 
    } 
} 

같은 일을 할 수 방법은 현재 아직, 레거시 프로젝트에 IFoo2 알고있는 MyMethod라는. IFoo2를 사용하지 않고도 해결책을 찾을 수 있습니까?

+0

'불행하게도,이 확장 방법을 항상 심지어 FooX.'를 들어,라고 왜 문제가 있다는 것입니다? – Servy

+1

입니다. 왜냐하면 최신 클래스의 DoStuff()가 Do()로 구현 될 필요가 없기 때문입니다. 물건(); –

+0

그러면 의미 상으로 확장되는'IFoo'가 아니며 확장하지 않아야합니다. 또한'IFoo' 인스턴스를 기대하는 메소드에 전달해야합니다. 왜냐하면 그 계약을 제공 할 수 없기 때문입니다. – Servy

답변

0

변수의 유형이 IFoo 인 경우 확장 방법 DoStuff이 호출됩니다. 이 문제를 해결하는 일반적인 방법은 마지막 단락에서 제안한 것 또는 최신 인터페이스 메서드를 호출 할 곳에서 IFoo 대신 IFoo2을 사용하도록하는 것입니다.

+0

다른 모든 제안은 더 나쁘다. 그래서 유일한 대답은 할 수 없다는 것이다. –

0

현재 구현하지 않은 클래스에 대해 Do 및 Stuff 메소드를 구현하는 추상 하위 클래스를 만들 수 있습니다. 당신이 다음이 추상 클래스

public class Foo: IFoo 
{ 
    // interface implementation required 
} 

becomes: 

public class Foo: abstractFoo 
{ 
    // interface implementation NOT required 
} 
+0

어떻게 개선 될까요? 단지 수와 톤의 코드를 끊고 (인터페이스에 따라 다름) 아무 것도 추가하지 않습니다. – Servy

+0

주어진 예제에서 작동 할 것이라고 생각합니다. 그러나 당신이 맞습니다. 그것은 많은 의존성을 깨뜨릴 수 있습니다. 당신이 많은 코드를 가지고 있지 않아도 작동 할 수있는 솔루션을 보여주고 자 노력 중이고, 몇 가지를 다시 디자인 할 수 있습니다. – martijn

+0

* 주어진 예제에서 어떻게 작동합니까? 이것이 인터페이스가 달성하지 못하는 것은 무엇입니까? 그것은 * 인터페이스를 사용하는 기능을 제거하고, 추가하지 않습니다. 이는 문제에 대한 설명이 주어지면 해결할 수없는 레거시 앱이기 때문에 불가능했을 수도있는 변경 사항입니다. 그런 변화를 가져라. – Servy

2

에서 상속 할 수있는 경우

public abstract class abstractFoo : IFoo 
{ 
    public virtual void Do() {} 
    public virtual void Stuff(){} 
} 

당신은 그렇게, IFoo 인터페이스를 확장하지 않아야합니다. 그것은 Interface Segregation principle 휴식입니다.

이러한 개체가 코드에서 정확히 동일한 엔터티를 나타내면 다른 인터페이스를 사용하면 안됩니다. IFoo 인터페이스를 구현하지만 동일한 계약을 나타내는 두 번째 인터페이스를 만들지 않는 클래스의 기능을 확장하려면 확장 메서드를 만들 수 있습니다. 그러나 IFoo contract-refactor 레거시 객체를 변경하려는 경우 (누락 된 구현 추가)

+0

사실,하지만 IFoo2 고유의 IFoo는 아니지만 FooX는 IFoo와 IFoo2를 구현하지만 문제는 거의 동일합니다. –

+0

DoStuff() 메소드가 위에서 말했듯이 : "Do(); SomeOther(); Stuff();" 이 추가 방법을 위해 IFoo 인터페이스를 확장하는 것이 무슨 문제입니까? 최악의 경우 레거시 객체를 요구할 것입니다 : "Do(); Stuff()"그리고 그것은 IFoo 인터페이스를 확장하는 것보다 훨씬 낫습니다. –

+0

IFoo를 소유하고 있지 않으므로 IFoo에 메서드를 추가 할 수 없습니다. –

0

IMHO FooX은 시작하기 전에 IFoo을 구현하면 안되며 현재 arquitecture에 대한 몇 가지 재검토가 이루어져야합니다.

그렇다면 정확히 어떤 한계를 알고 있는지 알지 못하면 IFooX을 래퍼를 통해 보낼 수 있습니까?다음과 같은 뭔가 :

public class FooXWrapper<T>: IFoo where T: FooX 
{ 
    readonly T foo; 
    bool doCalled; 

    public FooWrapper(T foo) 
    { 
     this.foo = foo; 
    } 

    public void Do() 
    { 
     doCalled = true; 
    } 

    public void Stuff() 
    { 
     if (!doCalled) 
      throw new InvalidOperationException("Must call Do"); 

     foo.DoStuff(); 
    } 
} 

그 추한 해킹,하지만 주어진 상황은 ...