클래스에 너무 많은 인터페이스가 있습니까? [닫은]


28

아마도 23 개의 인터페이스를 구현하는 클래스를 갖는 코드 냄새 또는 안티 패턴이라고 생각할 것입니다. 실제로 반 패턴이라면 무엇이라고 부릅니까? 아니면 단순히 단일 책임 원칙을 따르지 않습니까?


3
어느 쪽이든 의심 스럽다. 만약 클래스가 23 개의 메소드를 가지고 있고 모든 클래스가 많은 메소드를 필요로하지는 않지만 모든 클래스가 그렇게 많은 메소드를 필요로하지는 않지만 불가능하지는 않습니다. )

문제의 클래스는 일종의 엔티티이며 인터페이스는 대부분 속성에 관한 것입니다. 이 중 113 개가 있으며 4 가지 방법 만 있습니다.
조나스 엘프 스트 ö

6
정적으로 컴파일 된 언어로 오리 타이핑에 인터페이스가 본질적으로 사용된다고 생각했습니다. 뭐가 문제 야? :)
ashes999

2
단지 질문 :이 엔티티의 각 인스턴스에 대해 113 개의 속성이 모두 채워져 있습니까? 아니면 속성이 채워진 다른 컨텍스트에서 사용되는 일종의 "공통 엔티티"입니까?
David

1
"클래스"를 구성하는 것에 대한 개념적인 이해로 돌아가 봅시다. 수업은 명확하게 정의 된 책임과 역할을 가진 하나의 것입니다. 이런 방식으로 23 인터페이스 개체를 정의 할 수 있습니까? 수업을 적절히 요약 한 (Joycean이 아닌) 단일 문장을 게시 할 수 있습니까? 그렇다면 23 개의 인터페이스가 정상입니다. 그렇지 않다면 너무 크고 너무 복잡합니다.
Stephen Gross

답변:



23

이 반 패턴은 Jack of All Trades 또는 Too Many Hats라고합니다.


2
스위스 군용 나이프는 어떻습니까?
FrustratedWithFormsDesigner

이 용어는 이미 너무 많은 메소드가있는 인터페이스에 사용되는 것 같습니다 :-) 여기에서도 의미가 있습니다.
Jalayn

1
@FrustratedWithFormsDesigner 스위스 아미 나이프는 30 개의 속성을 포함하는 IValue라는 인터페이스를 갖는 나쁜 맛을 결코 갖지 못합니다.
Jonas Elfström

3
너무 많은 모자에 +1 ... 멀티 헤드 신을
Newtopian

1
스위스 군용 칼은 재사용의 모델입니다. 즉, 단일 "블레이드"는 may 메소드를 수행 할 수 있으므로 아마도 유추 할 수 있습니다!
James Anderson

17

이름을 주어야한다면 히드라 라고 부릅니다 .

히드라

그리스 신화에서 Lernaean Hydra (그리스어 : Λερναία Ὕδρα)는 많은 머리를 가진 파충류의 특성을 가진 고대의 이름없는 뱀 같은 초 음성 수수였으며, 시인은 꽃병 화가보다 더 많은 머리를 언급했습니다 페인트를 바르고 각 머리를 자르면 두 번 더 자랐고 독한 호흡이 너무 심해 그녀의 발자국까지도 치명적이었습니다.

특히 중요한 것은 머리가 많을뿐만 아니라 점점 더 많이 자라며 그로 인해 죽일 수 없다는 사실입니다. 그것은 이런 종류의 디자인에 대한 나의 경험이었습니다. 개발자는 화면에 맞지 않을 때까지 점점 더 많은 인터페이스를 방해하고 있습니다. 그런 다음 프로그램 디자인과 가정에서 너무 세분화되어 분할하는 것은 희망이없는 전망입니다 (실제로 시도하면 실제로 종종 간극을 메우기 위해 더 많은 인터페이스가 필요 합니다.

"Hydra"로 인한 임박한 운명의 초기 징후 중 하나는 다음과 같이 정신 상태를 검사하지 않고 종종 다른 인터페이스와 인터페이스를 자주 캐스팅하는 것입니다.

public void CreateWidget(IPartLocator locator, int widgetTypeId)
{
    var partsNeeded = locator.GetPartsForWidget(widgetTypeId);
    return ((IAssembler)locator).BuildWidget(partsNeeded);
}

문맥에서 벗어나면이 코드에 대해 비린내가있는 것이 분명하지만, 이런 일이 발생할 가능성은 객체가 자라는 더 많은 "헤드"와 함께 올라갑니다. 개발자 는 항상 같은 생물을 다루고 있다는 것을 직관적으로 알고 있기 때문입니다 .

23 개의 인터페이스를 구현하는 객체에 대한 유지 관리 작업은 항상 행운 입니다 . 프로세스에서 부수적 인 손상을 일으키지 않을 가능성은 거의 없습니다.


16

하나님 개체가 마음에 온다; 모든 것을하는 방법을 아는 단일 객체. 이는 두 가지 주요 설계 방법론의 "응집력"요구 사항을 준수하지 않기 때문입니다. 23 개의 인터페이스가있는 개체가있는 경우 사용자에게 23 가지의 다른 요소가되는 방법을 알고있는 개체가 있으며이 23 가지의 다른 요소는 단일 작업 또는 시스템 영역에 있지 않을 수 있습니다.

SOLID에서이 객체의 작성자는 분명히 인터페이스 분리 원칙을 따르려고 시도했지만 이전 규칙을 위반했습니다. 단일 책임 원칙. "SOLID"인 이유가 있습니다. 디자인에 대해 생각할 때 항상 S가 우선하며 다른 모든 규칙이 따릅니다.

GRASP에서 이러한 클래스의 작성자는 "높은 응집력"규칙을 무시했습니다. GRASP는 SOLID와 달리 개체에 단일 책임이 없지만 최대 2 ~ 3 개의 밀접한 관련 책임이 있어야합니다.


나는 하나님이 어떤 인터페이스에 의해 구속되지 않기 때문에 이것이 하나님의 물체 반 패턴인지 확신 할 수 없다. 너무 많은 인터페이스에 제약을 받으면 하나님의 전능성을 줄일 수 있습니다. ;) 아마도 이것은 키메라 객체입니까?
FrustratedWithFormsDesigner

하나님은 많은 얼굴을 가지고 있으며 많은 사람들에게 많은 것들이 될 수 있습니다. 결합 된 인터페이스의 기능으로 인해 객체가 "모든 것을 알 수있게"만드는 것이 실제로 신을 제한하지는 않습니까?
KeithS

1
인터페이스를 만든 것이 인터페이스를 수정하지 않는 한. 그런 다음 인터페이스 변경에 맞게 하나님을 다시 구현해야 할 수도 있습니다.
FrustratedWithFormsDesigner

3
수업을 만든 사람은 저라고 생각합니다.
조나스 엘프 스트 ö

"당신"의 사용은 개발자 팀을 언급하면서 더 많이 사용됩니다. 과거 또는 현재 해당 팀의 누군가가이 클래스를 만들었습니다. 그래도 편집하겠습니다.
KeithS

8

23은 단지 숫자입니다! 가장 가능성이 높은 시나리오에서는 경보를 발할 수있을만큼 높습니다. 그러나 우리가 묻는다면 태그를 "반 패턴"이라고 부르기 전에 가장 많은 수의 메소드 / 인터페이스는 무엇입니까? 5 또는 10 또는 25입니까? 10이 좋으면 11도-가 될 수 있고 그 이후의 정수가 될 수 있기 때문에 숫자는 실제로 답이 아닙니다.

실제 문제는 복잡성에 관한 것입니다. 그리고 우리는 긴 코드가 어떤 조치에 의한 방법이나 클래스의 큰 크기의 수는 실제로는 것을 지적해야 하지 복잡성의 정의. 예, 코드가 클수록 (방법 수가 많을수록) 새로운 초보자를 읽고 이해 하기어렵습니다 . 또한 잠재적으로 다양한 기능, 많은 수의 예외 및 다양한 시나리오에 대한 상당히 진화 된 알고리즘을 처리합니다. 이것은 그것이 복잡하다는 것을 의미하지는 않습니다-소화하기가 어렵습니다.

반면에 몇 시간 안에 읽고 읽을 수있는 비교적 작은 크기의 코드는 여전히 복잡 할 수 있습니다. 코드가 불필요하게 복잡하다고 생각할 때입니다.

"복잡한"을 정의하기 위해 객체 지향 디자인의 모든 지혜를 여기에 넣을 수 있지만 "너무 많은 방법"이 복잡성을 나타내는 경우를 보여주기 위해 여기서 제한합니다.

  1. 상호 지식. (일명 커플 링) 사물이 클래스로 쓰여질 때, 우리 모두는 그것이 "좋은"객체 지향 코드라고 생각합니다. 그러나 다른 클래스에 대한 가정은 본질적으로 필요한 캡슐화를 깨뜨립니다. 알고리즘의 내부 상태에 대한 자세한 정보를 "누설"하는 메소드가있는 경우-애플리케이션은 서빙 클래스의 내부 상태에 대한 주요 가정으로 빌드됩니다.

  2. 반복 횟수가 너무 많음 (에 속함) 이름은 비슷하지만 작업과 모순되거나 유사한 기능을 가진 이름과 모순되는 방법. 많은 시간 코드는 응용 프로그램마다 약간 다른 인터페이스를 지원하도록 발전합니다.

  3. 역할너무 많음 클래스가 부가 기능을 계속 추가하고 사람들이 좋아하는만큼 계속 확장 할 때 클래스가 실제로는 두 클래스라는 것을 알기 만합니다. 놀랍게도 이것들은 모두 진짜로 시작합니다요구 사항과 다른 클래스는 없습니다. 이것을 고려하면, 거래의 세부 사항을 알려주는 Transaction 클래스가 있습니다. 지금까지는 좋아 보인다. 이제 누군가 "트랜잭션 시간"(UTC 사이 등)에서 형식 변환이 필요하다. 나중에 사람들은 특정 날짜가 특정 날짜에 있는지 무효화 트랜잭션을 검증하기위한 규칙을 추가한다. -전체 스토리를 쓰지는 않지만 결국 트랜잭션 클래스는 전체 캘린더를 빌드하고 사람들은 "캘린더 만"부분을 사용하기 시작합니다! 이것은 "카운터"가 제공 할 수있는 기능을 갖기 위해 "트랜잭션 클래스"를 인스턴스화하는 이유는 매우 복잡합니다.

  4. (In) API의 일관성 book_a_ticket ()을 할 때-티켓을 예약합니다! 얼마나 많은 검사와 프로세스가 발생했는지에 관계없이 매우 간단합니다. 이제 시간 흐름에 영향을주기 시작하면 복잡해집니다. 일반적으로 "검색"및 가능한 사용 가능 / 사용 불가능한 상태를 허용 한 후, 앞뒤로 줄이려면 티켓 안에 일부 컨텍스트 포인터를 저장하기 시작한 다음 티켓 예약 을 참조하십시오 . 검색 기능 만이 유일한 기능은 아닙니다. 이러한 "부서 기능"이 많은 후에 상황이 악화됩니다. 이 과정에서 book_a_ticket ()의 의미는 book_that_ticket ()을 의미합니다! 그리고 상상할 수 없을 정도로 복잡 할 수 있습니다.

아마도 당신은 매우 진화 된 코드에서 볼 수있는 많은 상황이있을 것입니다. 많은 사람들이 시나리오를 추가 할 수 있다고 확신합니다. "너무 많은 방법"이 이해가되지 않거나 당신이 분명히 생각하는 것을하지 않는 경우가 있습니다. 이것은 안티 패턴입니다.

저의 개인적인 경험은 합리적으로 시작되는 프로젝트가 저절로 쌓여 있거나 나빠질 수있는 많은 것들이 여전히 서로 다른 클래스들과 커플 링 증가로 나뉘어져 있다는 것입니다. 대부분 10 개의 클래스가 필요하지만 4 개의 클래스 만있는 것은 다용도 혼란스럽고 많은 수의 메소드가있을 가능성이 높습니다. 그것을 하나님과 용이라고 부르십시오. 이것이 나쁜 것입니다.

그러나 당신은 깔끔하게 일관된 매우 큰 클래스를 접하게되며 30 가지의 메소드를 가지고 있으며 여전히 매우 깨끗합니다. 그들은 좋을 수 있습니다.


해당 클래스에는 23 개의 인터페이스가 있으며 여기에는 113 개의 공용 속성과 4 개의 메서드가 포함됩니다. 그들 중 일부만이 읽기 전용입니다. C #에는 자동 구현 속성이 있으므로 메서드와 속성이 서로 다릅니다. msdn.microsoft.com/en-us/library/bb384054.aspx
Jonas Elfström

7

클래스에는 정확한 수의 인터페이스가 있어야합니다. 그 이상도 이하도 아닌.

모든 인터페이스가 해당 클래스에서 유용한 목적을 제공하는지 여부를 보지 않고 "너무 많은"을 말하는 것은 불가능합니다. 객체가 인터페이스를 구현한다고 선언하면 클래스가 컴파일 될 것으로 예상되는 경우 해당 메소드를 구현해야합니다. (나는 당신이보고있는 클래스가 있다고 가정한다.) 인터페이스의 모든 것이 구현되고 그러한 구현이 클래스의 내부와 관련하여 무언가를 수행한다면, 구현이 거기에 있으면 안된다고 말하기는 어려울 것이다. 그 기준을 충족하는 인터페이스가 어디에 속하지 않을지 생각할 수있는 유일한 경우는 아무도 사용하지 않는 외부입니다.

일부 언어에서는 Java extends키워드 와 같은 메커니즘을 사용하여 인터페이스를 "하위 클래스 화"할 수 있으며 , 작성한 사람은이를 알지 못할 수 있습니다. 또한 23 개 모두가 충분히 모여서 집계하는 것이 의미가 없을 수도 있습니다.


나는이 질문이 언어에 구애받지 않는다고 생각했다. 실제 코드는 C #입니다.
Jonas Elfström

C #을 수행하지 않았지만 비슷한 형태의 인터페이스 상속을 지원하는 것으로 보입니다.
Blrfl

예, 인터페이스는 다른 인터페이스 등을 "상속"할 수 있습니다.
Jonas Elfström

많은 토론에서 인스턴스 가 수행해야하는 것이 아니라 클래스 가 수행해야하는 것에 중점을 둔다는 점이 흥미 롭습니다 . 공통 섀시에 부착 된 다양한 이미징 및 오디오 센서가있는 로봇은 위치와 방향이 있고 시력과 소리를보고 듣고 연관시킬 수있는 엔티티로 모델링해야합니다. 이러한 기능을 뒷받침하는 모든 실제 논리는 다른 클래스에있을 수 있지만 로봇 자체는 "어떤 물체가 앞쪽에 있는지 확인", "영역에 소음이 있는지 확인"또는 "앞에있는 물체가 다음과 같은지 확인하십시오"와 같은 방법을 지원해야합니다. 감지 된 소리를 생성합니다. "
supercat

특정 로봇은 기능을 특정 방식으로 서브 시스템으로 세분화해야하지만, 로봇을 사용하는 실제 코드는 특정 로봇 내의 기능이 어떻게 세분화되는지 알 필요가 없어야합니다. "눈"과 "귀"가 별도의 물체로 외부 세계에 노출되었지만 해당 센서가 공유 확장 암에있는 경우 외부 코드는 "귀"를 요청하여지면에서 소리를 동시에들을 수 있습니다. 그것은 "눈"에게 장애물 위를 보도록 요청했습니다.
supercat

1

일반적인 "bean"의 경우와 마찬가지로 각 속성에 대해 "getter"/ "setter"쌍이 있다고 생각되는 것처럼 들리며 이러한 모든 메소드를 인터페이스로 승격시킵니다. " 빈이있다 " 고 부르는 것은 어떻 습니까 ? 또는 너무 많은 콩의 영향으로 잘 알려진 Rabelaisian "Flatulence"가 더 많습니다.


C #에 있으며 자동 구현 속성이 있습니다. 이 23 개의 인터페이스는 모두 113 개의 이러한 속성을 포함합니다.
Jonas Elfström

1

인터페이스가 여러 환경에서 일반 객체를 사용할 때 종종 증가합니다. C #에서 IComparable, IEqualityComparer 및 IComparer의 변형은 고유 한 설정에서 정렬을 허용하므로 모든 설정을 구현하게 될 수 있습니다. 일반적으로 강력한 형식의 버전과 일반이 아닌 버전을 구현할 수 있기 때문에 일부가 한 번 이상 또한 하나 이상의 제네릭을 구현할 수 있습니다.

시나리오를 예로 들어 봅시다. 다른 것을 구입할 수있는 크레딧을 구매할 수있는 웹샵 (스톡 사진 사이트에서 종종이 체계를 사용)을 가정 해 봅시다. 클래스 "Valuta"와 클래스 "Credits"가 동일한 기본 형식을 가질 수 있습니다. Valuta에는 "Currency"속성에 대해 걱정하지 않고 계산을 수행 할 수있는 멋진 연산자 오버로드 및 비교 루틴이 있습니다 (예 : 파운드를 달러에 추가). 크레딧은 더 단순하지만 다른 고유 한 동작이 있습니다. 이들을 서로 비교할 수 있기를 원하면 IComparable뿐만 아니라 IComparable과 다른 비교 인터페이스의 변형 인터페이스를 모두 구현할 수 있습니다 (기본 클래스이든 다른 곳이든 공통 구현을 사용하더라도).

직렬화, ISerializable, IDeserializationCallback을 구현할 때 구현됩니다. 그런 다음 실행 취소 다시 실행 스택을 구현합니다. IClonable이 추가됩니다. IsDirty 기능 : IObservable, INotifyPropertyChanged. 사용자가 문자열을 사용하여 값을 편집 할 수 있도록 허용 : IConvertable ... 목록은 계속할 수 있습니다 ...

현대 언어에서는 이러한 측면을 분리하여 핵심 클래스 외부의 자체 클래스에 배치하는 데 도움이되는 다른 트렌드가 있습니다. 그런 다음 외부 클래스 또는 측면은 어노테이션 (속성)을 사용하여 대상 클래스와 연관됩니다. 외부 측면 클래스를 다소 일반적으로 만드는 것이 종종 가능합니다.

속성 (주석)의 사용은 반영 될 수 있습니다. 한 가지 단점은 사소한 (초기) 성능 손실입니다. (종종 정서적) 단점은 캡슐화와 같은 원칙을 완화해야한다는 것입니다.

항상 다른 솔루션이 있지만 모든 멋진 솔루션에는 트레이드 오프 또는 캐치가 있습니다. 예를 들어, ORM 솔루션을 사용하려면 모든 특성을 가상으로 선언해야합니다. 직렬화 솔루션은 클래스의 기본 생성자를 요구할 수 있습니다. 의존성 주입을 사용하는 경우 단일 클래스에서 23 개의 인터페이스를 구현할 수 있습니다.

내 눈에는 23 개의 인터페이스가 정의상 나쁘지 않아도됩니다. 그 배후에는 잘 계획된 계획이 있거나 반성이나 극단적 인 캡슐화 신념의 사용을 피하는 것과 같은 원칙 결정이있을 수 있습니다.

작업을 전환하거나 기존 아키텍처를 구축해야 할 때마다. 내 충고는 먼저 완전히 알게되고 모든 것을 너무 빨리 리팩토링하지 마십시오. 원래 개발자의 의견을 듣고 (아직 있다면) 자신이 본 것에 대한 생각과 아이디어를 찾아보십시오. 질문을 할 때, 그것을 분해하기 위해 그렇게하지 말고 배우십시오 ... 그렇습니다. 모든 사람은 자신의 황금 망치를 가지고 있지만, 더 많은 망치를 모을수록 동료 동료들과 쉽게 지낼 수 있습니다.


0

"너무 많은"주관적입니다 : 프로그래밍 스타일? 공연? 표준 준수? 전례? 평범한 편한 느낌 / 자신감?

코드가 올바르게 작동하고 유지 관리 성 문제가없는 한 23은 새로운 표준이 될 수 있습니다. 언젠가 나는 "23 개의 인터페이스로 훌륭한 작업을 할 수 있습니다. Jonas Elfström"을 참조하십시오.


0

제한은 23 또는 5 또는 7과 같이 23보다 작은 수 여야한다고 말하고 싶습니다.
그러나 해당 인터페이스가 상속하는 인터페이스 수나 기본 클래스로 구현 된 인터페이스 수는 포함되지 않습니다.

(따라서 N + 모든 상속 된 인터페이스 수, N <= 7)

만약 당신의 클래스가 너무 많은 인터페이스를 구현한다면 아마도 신 클래스 일 것 입니다.

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