마커 인터페이스의 목적은 무엇입니까?
답변:
이것은 "Mitch Wheat"의 응답을 기반으로 한 약간의 접선입니다.
일반적으로 사람들이 프레임 워크 디자인 지침을 인용하는 것을 볼 때마다 항상 다음과 같이 언급하고 싶습니다.
일반적으로 대부분의 경우 프레임 워크 디자인 지침을 무시해야합니다.
이것은 프레임 워크 디자인 가이드 라인의 문제 때문이 아닙니다. .NET 프레임 워크는 환상적인 클래스 라이브러리라고 생각합니다. 이러한 환상의 대부분은 프레임 워크 디자인 지침에서 비롯됩니다.
그러나 설계 지침은 대부분의 프로그래머가 작성한 대부분의 코드에 적용되지 않습니다. 그들의 목적은 라이브러리 작성을보다 효율적으로 만드는 것이 아니라 수백만 명의 개발자가 사용하는 대규모 프레임 워크를 만들 수 있도록하는 것입니다.
많은 제안이 다음과 같은 작업을 수행하도록 안내 할 수 있습니다.
.net 프레임 워크는 정말 큽니다. 너무 커서 누군가가 그것의 모든 측면에 대해 상세한 지식을 가지고 있다고 가정하는 것은 절대적으로 불합리합니다. 사실, 대부분의 프로그래머가 이전에 사용한 적이없는 프레임 워크의 일부를 자주 접한다고 가정하는 것이 훨씬 안전합니다.
이 경우 API 디자이너의 주요 목표는 다음과 같습니다.
프레임 워크 디자인 지침은 개발자가 이러한 목표를 달성하는 코드를 만들도록합니다.
즉, 코드 복제를 의미하는 경우에도 상속 계층을 피하거나 공유 도우미를 사용하는 것보다 모든 예외 발생 코드를 "진입 점"으로 푸시하는 것과 같은 작업을 수행해야합니다 (디버거에서 스택 추적이 더 의미가 있음). 다른 유사한 것들의.
이러한 지침에서 마커 인터페이스 대신 속성 사용을 제안하는 주된 이유는 마커 인터페이스를 제거하면 클래스 라이브러리의 상속 구조에 훨씬 더 접근하기 쉽기 때문입니다. 30 개 유형과 6 개 계층의 상속 계층이있는 클래스 다이어그램은 15 개 유형과 2 개 계층의 계층 구조를 가진 클래스 다이어그램에 비해 매우 어렵습니다.
실제로 API를 사용하는 개발자가 수백만 명이거나 코드 기반이 정말 큰 경우 (예 : 100,000 개 이상의 LOC) 이러한 지침을 따르는 것이 많은 도움이 될 수 있습니다.
5 백만 명의 개발자가 API를 배우는 데 60 분을 소비하지 않고 15 분 동안 API를 배우는 데 소비하면 결과적으로 428 인년이 절약됩니다. 그것은 많은 시간입니다.
그러나 대부분의 프로젝트에는 수백만 명의 개발자 또는 10 만 이상의 LOC가 포함되지 않습니다. 4 명의 개발자와 약 50K loc이있는 일반적인 프로젝트에서는 가정 집합이 많이 다릅니다. 팀의 개발자는 코드 작동 방식을 훨씬 더 잘 이해할 것입니다. 즉, 고품질 코드를 신속하게 생성하고 버그 수와 변경에 필요한 노력을 줄이기 위해 최적화하는 것이 훨씬 더 합리적입니다.
.net 프레임 워크와 일치하는 코드를 개발하는 데 1 주를 소비하고 변경하기 쉽고 버그가 적은 코드를 작성하는 데 8 시간을 소비하면 다음과 같은 결과가 발생할 수 있습니다.
4,999,999 명의 다른 개발자가 비용을 흡수하지 않으면 일반적으로 가치가 없습니다.
예를 들어 마커 인터페이스에 대한 테스트는 단일 "is"표현식으로 귀결되며 속성을 찾는 코드가 줄어 듭니다.
그래서 제 조언은 :
virtual protected
템플릿 메서드 DoSomethingCore
를 명명하는 DoSomething
것은 그다지 추가 작업이 아니고 템플릿 메서드 라는 것을 분명히 전달합니다 ... IMNSHO, API를 고려하지 않고 애플리케이션을 작성하는 사람들 ( But.. I'm not a framework developer, I don't care about my API!
)은 정확히 많은 중복을 작성하는 사람들입니다 ( 또한 문서화되지 않고 일반적으로 읽을 수없는) 코드입니다.
마커 인터페이스는 런타임에 특정 인터페이스를 구현하는 것으로 클래스의 기능을 표시하는 데 사용됩니다.
인터페이스 디자인 및 .NET 형 디자인 가이드 라인 - 인터페이스 디자인은 C #으로 속성을 사용 찬성 마커 인터페이스의 사용을 억제하지만, @Jay Bazuzi 지적, 속성보다 마커 인터페이스를 확인하기 쉽습니다 :o is I
그래서이 대신 :
public interface IFooAssignable {}
public class FooAssignableAttribute : IFooAssignable
{
...
}
.NET 지침에서는 다음과 같이 권장합니다.
public class FooAssignableAttribute : Attribute
{
...
}
[FooAssignable]
public class Foo
{
...
}
다른 모든 대답은 "피해야한다"고 말 했으므로 그 이유를 설명하는 것이 유용 할 것입니다.
첫째, 마커 인터페이스가 사용되는 이유 :이를 구현하는 객체를 사용하는 코드가 해당 인터페이스를 구현하는지 여부를 확인하고 해당하는 경우 객체를 다르게 처리 할 수 있도록하기 위해 존재합니다.
이 접근 방식의 문제점은 캡슐화가 중단된다는 것입니다. 이제 개체 자체가 외부에서 사용되는 방식을 간접적으로 제어 할 수 있습니다. 더욱이, 그것은 그것이 사용될 시스템에 대한 지식을 가지고 있습니다. 마커 인터페이스를 적용함으로써, 클래스 정의는 그것이 마커의 존재를 확인하는 어딘가에서 사용될 것으로 예상하고 있음을 제안합니다. 사용되는 환경에 대한 암묵적인 지식을 가지고 있으며 사용 방법을 정의하려고합니다. 이것은 캡슐화의 개념에 위배됩니다. 왜냐하면 그것은 완전히 자체 범위 밖에 존재하는 시스템의 일부 구현에 대한 지식을 가지고 있기 때문입니다.
실질적인 수준에서 이것은 이식성과 재사용 성을 감소시킵니다. 클래스가 다른 응용 프로그램에서 다시 사용되는 경우 인터페이스도 복사되어야하며 새 환경에서는 의미가 없어 완전히 중복 될 수 있습니다.
따라서 "마커"는 클래스에 대한 메타 데이터입니다. 이 메타 데이터는 클래스 자체에서 사용되지 않으며 특정 방식으로 개체를 처리 할 수 있도록 외부 클라이언트 코드에만 의미가 있습니다. 클라이언트 코드에만 의미가 있기 때문에 메타 데이터는 클래스 API가 아닌 클라이언트 코드에 있어야합니다.
는 "마커 인터페이스"와 정상적인 인터페이스의 차이는 방법과 인터페이스가 어떻게 외부 세계에 알 수 있다는 것입니다 수 빈 인터페이스가 어떻게 외부 세계를 말하고 의미하는 반면에 사용할 수 있어야 사용할 수 있습니다.
IConstructableFromString<T>
클래스를 T
구현할 수 있다고 명시되어있는 IConstructableFromString<T>
경우 ...
public static T ProduceFromString(String params);
인터페이스의 동반 클래스는 메소드를 제공 할 수 있습니다 public static T ProduceFromString<T>(String params) where T:IConstructableFromString<T>
. 클라이언트 코드에와 같은 메서드가있는 경우 클라이언트 코드 T[] MakeManyThings<T>() where T:IConstructableFromString<T>
를 수정하지 않고도 클라이언트 코드와 함께 작동 할 수있는 새로운 유형을 정의 할 수 있습니다. 메타 데이터가 클라이언트 코드에 있으면 기존 클라이언트에서 사용할 새 유형을 만들 수 없습니다.
T
과 그것을 사용하는 클래스 IConstructableFromString<T>
는 인터페이스에 일부 동작을 설명하는 메서드가 있으므로 마커 인터페이스가 아닙니다.
ProduceFromString
위의 예제에서 정적 메서드를 검색하고 실행하는 실제 프로세스 에는 관련이 없습니다. 인터페이스는 필요한 기능을 구현하기 위해 어떤 클래스가 예상되어야 하는지를 나타내는 마커로 사용된다는 점을 제외하고는 어떤 방식 으로든 인터페이스.
마커 인터페이스는 언어가 구별 된 공용체 유형을 지원하지 않을 때 때때로 필요한 악이 될 수 있습니다.
형식이 A, B 또는 C 중 하나 여야하는 인수를 예상하는 메서드를 정의한다고 가정합니다. 많은 함수 우선 언어 (예 : F # )에서 이러한 형식은 다음과 같이 명확하게 정의 할 수 있습니다.
type Arg =
| AArg of A
| BArg of B
| CArg of C
그러나 C #과 같은 OO 우선 언어에서는 불가능합니다. 여기에서 비슷한 것을 달성하는 유일한 방법은 인터페이스 IArg를 정의하고 A, B 및 C를 "표시"하는 것입니다.
물론 "객체"유형을 인수로 받아들이면 마커 인터페이스 사용을 피할 수 있지만 표현력과 유형 안전성을 잃게됩니다.
차별적 인 공용체 유형은 매우 유용하며 적어도 30 년 동안 기능 언어로 존재 해 왔습니다. 이상하게도 오늘날까지 모든 주류 OO 언어는이 기능을 무시했습니다. 실제로 함수형 프로그래밍과는 아무런 관련이 없지만 유형 시스템에 속합니다.
Foo<T>
는 모든 유형에 대해 별도의 정적 필드 집합 T
을 가지기 때문에 제네릭 클래스에 a를 처리하는 대리자를 포함하는 정적 필드를 포함 T
하고 클래스가있는 모든 유형을 처리하는 함수로 해당 필드를 미리 채우는 것은 어렵지 않습니다. 함께 일해야합니다. 유형에 대한 제네릭 인터페이스 제약 조건을 사용 T
하면 제공된 유형이 실제로 유효한지 확인할 수는 없지만 제공된 유형이 적어도 유효하다고 주장하는지 컴파일러 시간에 확인합니다.
마커 인터페이스는 비어있는 인터페이스입니다. 클래스는이 인터페이스를 어떤 이유로 사용할 메타 데이터로 구현합니다. C #에서는 다른 언어에서 마커 인터페이스를 사용하는 것과 같은 이유로 클래스를 마크 업하는 데 더 일반적으로 특성을 사용합니다.
마커 인터페이스를 사용하면 모든 하위 클래스에 적용되는 방식으로 클래스에 태그를 지정할 수 있습니다. "순수한"마커 인터페이스는 아무것도 정의하거나 상속하지 않습니다. 더 유용한 유형의 마커 인터페이스는 다른 인터페이스를 "상속"하지만 새 멤버를 정의하지 않는 인터페이스 일 수 있습니다. 예를 들어, "IReadableFoo"인터페이스가있는 경우 "Foo"처럼 동작하는 "IImmutableFoo"인터페이스도 정의 할 수 있지만이를 사용하는 모든 사람에게 값을 변경하지 않을 것이라고 약속합니다. IImmutableFoo를 허용하는 루틴은 IReadableFoo처럼 사용할 수 있지만 루틴은 IImmutableFoo를 구현하는 것으로 선언 된 클래스 만 허용합니다.
"순수한"마커 인터페이스에 대한 많은 용도는 생각할 수 없습니다. 내가 생각할 수있는 유일한 방법은 EqualityComparer (of T) .Default가 IDoNotUseEqualityComparer를 구현 한 모든 형식에 대해 Object.Equals를 반환하는 경우입니다. 형식도 IEqualityComparer를 구현 한 경우에도 마찬가지입니다. 이것은 Liskov Substitution Principle을 위반하지 않고 봉인되지 않은 불 변형을 가질 수있게합니다 : 만약 형이 동등성 테스트와 관련된 모든 메소드를 봉인한다면, 파생 형은 추가 필드를 추가하고 그것들을 변경 가능하게 할 수 있지만 그러한 필드의 변이는 t는 기본 유형 방법을 사용하여 표시됩니다. 봉인되지 않은 불변 클래스를 가지고 EqualityComparer.Default의 사용을 피하거나 IEqualityComparer를 구현하지 않는 파생 클래스를 신뢰하는 것은 끔찍하지 않을 수 있습니다.
이 두 가지 확장 방법은 Scott이 속성보다 마커 인터페이스를 선호한다고 주장하는 대부분의 문제를 해결합니다.
public static bool HasAttribute<T>(this ICustomAttributeProvider self)
where T : Attribute
{
return self.GetCustomAttributes(true).Any(o => o is T);
}
public static bool HasAttribute<T>(this object self)
where T : Attribute
{
return self != null && self.GetType().HasAttribute<T>()
}
이제 다음이 있습니다.
if (o.HasAttribute<FooAssignableAttribute>())
{
//...
}
대:
if (o is IFooAssignable)
{
//...
}
Scott이 주장한 것처럼 API를 빌드하는 데 첫 번째 패턴이 두 번째 패턴에 비해 5 배 더 오래 걸리는지 알 수 없습니다.