C #에서 확장 메서드가있는 인터페이스 대신 추상 클래스를 사용하는 경우


70

"Abstract class"와 "interface"는 비슷한 개념으로, 인터페이스가 두 가지 중 더 추상적 인 개념입니다. 차별화 요소 중 하나는 추상 클래스가 필요할 때 파생 클래스에 대한 메소드 구현을 제공한다는 것입니다. 그러나 C #에서는 최근에 확장 메서드가 도입되어이 차별화 요소가 줄어들어 인터페이스 메서드에 대한 구현을 제공 할 수 있습니다. 또 다른 차별화 요소는 클래스가 하나의 추상 클래스 만 상속 할 수 있다는 것입니다 (즉, 다중 상속이 없음). 그러나 여러 인터페이스를 구현할 수 있습니다. 따라서 인터페이스의 제약이 적고 유연합니다. 따라서 C #에서 확장 메서드가있는 인터페이스 대신 언제 추상 클래스를 사용해야합니까?

인터페이스 + 확장 메소드 모델의 주목할만한 예는 LINQ이며, 여기서는 IEnumerable여러 가지 확장 메소드를 통해 구현 되는 모든 유형에 대해 조회 기능이 제공됩니다 .


2
그리고 인터페이스에서도 '속성'을 사용할 수 있습니다.
굴샨

1
일반적인 OOD 질문이 아닌 C #과 비슷합니다. (대부분의 다른 OO 언어에는 확장 방법이나 속성이 없습니다.)
Péter Török


4
확장 메소드는 추상 클래스가 해결하는 문제를 완전히 해결하지 못하므로 그 이유는 Java 또는 다른 유사한 단일 상속 언어와 다르지 않습니다.
Job

3
추상 클래스 메소드 구현에 대한 필요성이있다 되지 확장 방법으로 감소되었다. 확장 메서드는 개인 멤버 필드에 액세스 할 수 없으며 가상으로 선언되어 파생 클래스에서 재정의 될 수도 없습니다.
zooone9243

답변:


63

구현할 클래스의 일부가 필요할 때 내가 사용한 가장 좋은 예는 템플릿 메소드 패턴입니다.

public abstract class SomethingDoer
{
    public void Do()
    {
        this.DoThis();
        this.DoThat();
    }

    protected abstract void DoThis();
    protected abstract void DoThat();
}

따라서 Do ()가 호출 될 때 수행 될 단계를 정의 할 수 있습니다. 파생 클래스는 추상 메소드를 구현해야하지만 Do () 메소드는 구현하지 않아야합니다.

확장 법은 반드시 방정식의 "클래스의 일부 여야 함"부분을 만족하지는 않습니다. 또한 iirc, 확장 메소드는 범위 외의 공개 메소드가 될 수 없습니다.

편집하다

이 질문은 내가 원래 신용했던 것보다 더 흥미 롭습니다. 추가 조사를 통해 Jon Skeet 은 인터페이스 + 확장 방법을 사용하여 SO와 같은 질문 에 대답 했습니다. 또한 잠재적 단점 은 이런 방식으로 설계된 객체 계층에 대해 반사를 사용하는 것입니다.

개인적으로, 나는 현재 일반적인 관행을 바꾸는 것의 이점을 보는 데 어려움을 겪고 있지만, 그렇게 할 때 단점이 거의 없습니다.

유틸리티 클래스를 통해이 방법으로 여러 언어로 프로그래밍 할 수 있습니다. 확장 기능은 구문 설탕을 제공하여 메소드가 클래스에 속하는 것처럼 보이게합니다.


나를 이길 ..
Giorgi

1
그러나 인터페이스와 확장 방법으로도 같은 일을 할 수 있습니다. 추상 기본 클래스에서 Do()정의하는 메소드는 확장 메소드로 정의됩니다. 파생 클래스 정의에 남겨진 메소드 DoThis()는 기본 인터페이스의 멤버가됩니다. 또한 인터페이스를 통해 여러 구현 / 상속을 지원할 수 있습니다. 그리고 이것 -odetocode.com/blogs/scott/archive/2009/10/05/…
Gulshan

@Gulshan : 하나 이상의 방법으로 작업을 수행 할 수 있기 때문에 선택한 방법이 유효하지 않습니다. 인터페이스를 사용하면 이점이 없습니다. 추상 클래스 자체 인터페이스입니다. 여러 구현을 지원하기 위해 인터페이스가 필요하지 않습니다.
ThomasX

또한 인터페이스 + 확장 모델에는 단점이있을 수 있습니다. 예를 들어 Linq 라이브러리를 보자. 훌륭하지만 해당 코드 파일에서 한 번만 사용해야하는 경우 코드 파일의 모든 IEnumerable에서 IntelliSense의 전체 Linq 라이브러리를 사용할 수 있습니다. IDE 성능이 상당히 느려질 수 있습니다.
KeithS

-1 인터페이스보다 추상 클래스가 템플릿 메소드에 더 적합한 방법을 보여주지 않았기 때문에
Benjamin Hodgson

25

그것은 모든 관하여 무엇을 당신이 모델에 원하는 :

추상 클래스는 상속에 의해 작동합니다 . 다만 특별한 기본 클래스 인, 그들은 어떤 모델 이다-A -relationship.

예를 들어, 개 동물이므로

class Dog : Animal { ... }

실제로 일반적인 모든 동물을 만드는 것은 이치에 맞지 않기 때문에 (이것은 어떤 모양입니까?)이 Animal기본 클래스를 abstract만들지 만 여전히 기본 클래스입니다. 그리고 Dog수업은 Animal.

반면에 인터페이스 는 다른 이야기입니다. 상속을 사용하지 않지만 다형성 ( 상속으로 구현할 수도 있음)을 제공합니다. 그들은 is-a 관계를 모델링하지 않지만 더 많은 관계를 지원 합니다.

예를 들어 다른 것과 비교할 수IComparable 있는 물체를 예로 들어 보자 .

클래스의 기능은 구현하는 인터페이스에 의존하지 않으며 인터페이스는 기능에 액세스하는 일반적인 방법을 제공합니다. 우리는 여전히 수 DisposeGraphics또는 FileStream그들을 구현하지 않고 IDisposable. 인터페이스는 관한 함께 방법.

원칙적으로 클래스의 동작을 변경하지 않고도 확장과 마찬가지로 인터페이스를 추가하고 제거하여 액세스를 강화할 수 있습니다. 객체가 무의미 해 지므로 기본 클래스를 빼앗을 수는 없습니다!


언제베이스 클래스 추상을 유지하기로 하시겠습니까?
Gulshan

1
@Gulshan : 어떤 이유로 든 기본 클래스의 인스턴스를 실제로 만드는 것은 의미가 없습니다. 치와와 또는 백상어를 "생성"할 수는 있지만 더 이상 전문화 없이는 동물 을 만들 수 없으므로 Animal추상적입니다. 이를 통해 abstract파생 클래스에 특화된 함수 를 작성할 수 있습니다 . 또는 프로그래밍 용어로 더-아마를 만드는 것은 의미가 UserControl없지만 a Button a UserControl이므로 같은 종류의 클래스 /베이스 클래스 관계를 갖습니다.
Dario

추상 메소드가 있다면 추상 클래스보다. 그리고 동물을위한 "가기"와 같이 그것들을 구현할 가치가 없을 때 가지고있는 추상적 방법들. 물론 때로는 추상 클래스보다 일반적인 클래스의 객체를 허용하지 않으려는 경우가 있습니다.
Dainius 2016 년

문법적으로 그리고 의미 적으로 "B는 A"라고 말하는 의미가 있다면 A는 추상 클래스 여야한다는 규칙은 없습니다. 클래스를 피할 수 없을 때까지 인터페이스를 선호해야합니다. 인터페이스의 구성 등을 통해 공유 코드를 수행 할 수 있습니다. 이상한 상속 트리를 사용하여 자신을 모퉁이에 그리는 것을 쉽게 할 수 있습니다.
sara

3

방금 몇 년 동안 추상 클래스를 사용하지 않았다는 것을 깨달았습니다.

추상 클래스는 인터페이스와 구현 사이에서 다소 이상한 짐승입니다. 내 거미줄을 괴롭히는 추상 수업이 있습니다. 필자는 인터페이스 뒤의 구현을 어떤 식 으로든 "노출"해서는 안된다고 주장한다.

인터페이스를 구현하는 클래스간에 공통적 인 동작이 필요하더라도 추상 클래스가 아닌 독립적 인 도우미 클래스를 사용합니다.

나는 인터페이스를 갈 것이다.


2
-1 : 나이가 확실하지 않지만 디자인 패턴, 특히 템플릿 방법 패턴을 배우고 싶을 수도 있습니다. 디자인 패턴을 사용하면 더 나은 프로그래머가되고 추상 클래스에 대한 무지를 끝낼 수 있습니다.
Jim G.

따끔 거림에 대해서도 마찬가지입니다. C # 및 Java의 첫 번째 기능인 클래스는 형식을 만들고 즉시 하나의 구현에 영원히 연결하는 기능입니다.
Jesse Millikan

2

IMHO, 나는 여기에 개념이 혼합되어 있다고 생각합니다.

클래스는 특정 종류의 상속을 사용하지만 인터페이스는 다른 종류의 상속을 사용합니다.

두 종류의 상속은 중요하고 유용하며 각각 장단점이 있습니다.

처음부터 시작합시다.

인터페이스가있는 이야기는 C ++로 오래 시작되었습니다. 1980 년대 중반, C ++이 개발되었을 때, 다른 종류의 인터페이스라는 아이디어는 아직 실현되지 않았습니다 (시간이 come 것입니다).

C ++에는 클래스가 사용하는 상속 유형 인 구현 상속이라는 한 가지 유형의 상속 만있었습니다.

GoF Book 권장 사항을 적용 할 수 있도록 : "구현이 아닌 인터페이스를위한 프로그램으로"C ++은 C #의 인터페이스가 수행 할 수있는 (그리고 유용한) 인터페이스를 수행 할 수 있도록 추상 클래스와 다중 구현 상속을 지원했습니다. .

C #에서는 새로운 유형의 인터페이스와 새로운 유형의 상속, 인터페이스 상속을 도입하여 "구현이 아닌 인터페이스를위한 프로그래밍"을 지원하는 두 가지 방법을 제공합니다. 인터페이스 상속 (구현 상속이 아닌)으로 다중 상속을 지원합니다.

따라서 C # 인터페이스의 아키텍처 이유는 GoF 권장 사항입니다.

이것이 도움이되기를 바랍니다.

종류, Gastón


1

인터페이스에 확장 메소드를 적용하면 공통 인터페이스 만 공유 할 수있는 클래스에서 공통 동작을 적용 할 때 유용합니다. 이러한 클래스는 이미 존재할 수 있으며 다른 방법을 통해 확장에 닫힐 수 있습니다.

새로운 디자인의 경우 인터페이스에서 확장 방법을 사용하는 것이 좋습니다. 유형 계층의 경우 확장 메서드는 수정을 위해 전체 계층을 열지 않고도 동작을 확장하는 수단을 제공합니다.

그러나 확장 메서드는 개인 구현에 액세스 할 수 없으므로 계층의 최상위에서 개인 구현을 공용 인터페이스 뒤에 캡슐화 해야하는 경우 추상 클래스가 유일한 방법입니다.


아니요, 유일한 안전한 방법은 아닙니다. 더 안전한 방법이 있습니다. 이것이 확장 방법입니다. 클라이언트는 전혀 감염되지 않습니다. 그래서 인터페이스 + 확장 방법을 언급했습니다.
굴샨

@Ed James 나는 "강력하지 않다"라고 생각한다.
Gulshan

@Ed James asp.mvc에서 확장 방법은 처음부터 사용되었습니다. 그것에 대해 어떻게 생각하세요?
굴샨

0

C #에서는 다중 상속이 지원되지 않으므로 인터페이스 개념이 C #에 도입 된 것 같습니다.

추상 클래스의 경우 다중 상속도 지원되지 않습니다. 따라서 추상 클래스 또는 인터페이스를 사용하도록 결정할 수 있습니다.


정확하게 말하자면 C #에서 "인터페이스"라고하는 순수한 추상 클래스입니다.
Johan Boulé

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.