2009-12-23 8 views
4

이것은 수용 가능한 디자인입니까 ??추상 클래스 디자인

추상 클래스

public abstract class SomethingBase 
{ 
    public abstract int Method1(int number1); 
    public abstract int Method2(int number2); 
} 

public class Class1 : SomethingBase 
{ 
    public override int Method1(int number1) 
    { 
     //implementation 
    } 

    //i dont want this method in this class 
    public override int Method2(int number2) 
    { 
     throw new NotImplementedException(); 
    } 
} 

public class Class2 : SomethingBase 
{ 

    //i dont want this method in this class 
    public override int Method1(int number1) 
    { 
    throw new NotImplementedException(); 
    } 

    public override int Method2(int number2) 
    { 
    //implementation 
    } 
} 

내 클래스 1의 방법 항목 및 Class2의에 대한 방법 2 없으며 VICA 구절이 필요하면 내가 상황을 의미한다. 실제로 메서드는 파생 클래스에서 서로를 제외합니다.

+2

'NotSupportedException' 또는'InvalidOperationException'을 던지는 것이이 컨텍스트에서 더 적합합니다. – Regent

답변

9

그것은 받아 들일 수있는 디자인이 아닙니다. 훨씬 더 수용하기 위해서 추상 클래스에 두 개의 인터페이스를 구현할 수 있습니다. 하나는 Method1()이고 다른 하나는 Method2()입니다. 인터페이스 객체를 사용하여 간단히 전달할 수 있습니다.

Class2 정의를 사용하지 않으려는 경우 클라이언트가 구현되지 않은 메소드를 호출 할 위험이 있습니다. 그래서 인터페이스를 사용하고 대신이 인터페이스를 사용하는 것이 좋습니다.

+1

단어에 대한 내 마음의 단어를 읽습니다. – Alon

4

나는 당신의 디자인에 근본적으로 잘못된 것이 있다고 생각합니다. 두 개의 파생 된 객체는 두 클래스 사이의 공통성을 진정으로 나타내는 기본 클래스에서만 파생되어야합니다. 귀하의 예제에서 기본 클래스는 Class1과 Class2 사이의 공통성을 나타내지 않습니다.

3

이 허용되는 디자인입니까 ??

아니요 일부 상황 (예 : FCL의 ReadOnlyCollection)에서 허용 될 수 있지만 가능하면 피해야합니다.

두 개의 인터페이스를 사용할 수 있습니다. 하나는 방법 1이고 다른 하나는 방법 2입니다.

파생 클래스가 기본 클래스의 계약을 강화해서는 안된다는 규칙을 항상 따라야합니다. 프로그램에서 어디서나 기본 클래스와 교환 할 수 있어야합니다.

2

우선, 추상 클래스에서 멤버 변수를 정의하지 않으면 인터페이스 만 사용하게 될 것입니다.

두 번째로 나는 아마도 추상적 인 클래스에서 하나의 메소드를 가지게 될 것이고 각각의 클래스는 Method1과 Method2의 시그니처가 동일하게 보이는 것처럼 다르게 (그들 자신의 특정 로직으로) 구현할 것이다.

3

기본 클래스 계약을 위반하므로 허용되는 디자인이 아닙니다. 대신이 인터페이스에 코드를 하나의 인터페이스를 가질 수

public void SomeFunction(SomethingBase x){ 
    x. Method2(1); // This will fail for Class1 but will pass for Class2. This is faulty. 
} 

클라이언트 코드에 다음과 같은 예를 들어 가지고는 방법 항목과 두 번째 인터페이스는 방법 2를 포함 포함되어 있습니다.

interface IM1{ 
    int Method1(int number1); 
} 

interface IM2{ 
    int Method2(int number2); 
} 

class Class1 : IM1{ 
    int Method1(int number1){ 
      // Implement your logic 
    } 
} 

class Class2 : IM2{ 
    int Method2(int number2){ 
      // Implement your logic 
    } 
} 

// Client Code 
    public void SomeFunction(IM2 x){ 
     x. Method2(1); // This will work as this is the only thing this client wants. 
    } 
3

가능한 경우 인터페이스를 사용하여 시연 한 디자인을 피해야한다는 진술에 절대 동의합니다.

하지만 때로는 피하거나 예측하기가 어렵습니다.

당신이 언급 한 접근법이 - Stream 클래스의 고전적인 예입니다. 일부 어린이는 몇 가지 추상 속성 및 메서드에 대해 NotSupportedException을 던집니다. (DeflateStream 클래스의 Position 속성).) 하지만 그 디자인을 올바르게 구현하려면 가상의 CanMethod1 속성이나 Method1Supported() 메서드가 있어야합니다. (. TypeConverter에서와 같이 - 일반적으로 주로이 방법은 몇 가지 상황에서만 지원되는 경우, 더 복잡한 경우가있을 때)

4

그것의 휴식 Liskov's Substitution Principle (일명 LSP) :

하자 Q (X S는 I 또한 로 생각할

T.

의 서브 타입 임) 형 T. 나서 Q (Y)의 x 형 S의 개체 Y 마찬가지이어야 객체에 대한 증명 속성 될 IS- A
규칙. 클래스 B가 클래스 A에서 파생되면 클래스 A와 동일한 기능을 모두 지원해야하며 해당 프로세스의 동작을 중단하지 않아야합니다. 어떤 것이 클래스 A 객체를 소비하는 경우, 그 대신에 클래스 B 객체를 전달할 수 있어야합니다.

예를 들어, 귀하의 기본 클래스 는-A사각형 가정 해 봅시다, 당신은 그것을에서 광장 클래스를 파생. 직사각형의 너비가 너비와 같지 않을 수 있기 때문에 LSP이 부러졌습니다. 반면 Square은 길이가 같지 않을 수 있습니다.

그러나 귀하의 경우 기본 클래스가 추상이므로 LSP은 완벽하게 적합하지 않지만 귀하가 이해하고있는 부분을 이해하고 있다고 생각합니다.

1

대다수는 동의하지 않지만 리젠트는 상향 조정했습니다. 자신의 입장을지지하고 확장하고 싶다.

NotSupportedException을 사용하는 것이 좋지 않은 것은 아닙니다. 장단점을 모두 고려해야합니다.

거의 비슷하지만 거의 동일한 메소드 세트를 사용하여 다양한 종류의 클래스를 보유하고 있다면 어떻게 될까요? 스트림이 좋은 예입니다. 모든 메소드를 인터페이스로 정렬하려고하면 1) 모든 인터페이스에 대해 직관적으로 명확하고 명료하게 이름을 고안하는 추가 작업을 제공합니다. 2) 인터페이스의 사용자가 모든 작업을 이해하도록합니다.

하나 또는 두 개의 메소드에서 NotSupportedException을 던지면 복잡한 클래스 및 클래스 계층을 연구하는 클래스 라이브러리 사용자에게 더 명확 해집니다.