인터페이스 분리 원리에 대한 두 가지 모순 된 정의 – 어느 것이 맞습니까? 구현을 제공하도록 강요하지 않고,)

ISP에서 기사를 읽을 때 ISP에 대해 두 가지 상반되는 정의가있는 것 같습니다.

첫 번째 정의에 따르면 ( 1 , 2 , 3 참조) ISP는 인터페이스를 구현하는 클래스가 필요하지 않은 기능을 구현하도록 강요해서는 안된다고 말합니다. 따라서 뚱뚱한 인터페이스IFat

interface IFat
{
     void A();
     void B();
     void C();
     void D();
}

class MyClass: IFat
{ ... }

더 작은 인터페이스로 분할되어야 ISmall_1하고ISmall_2

interface ISmall_1
{
     void A();
     void B();
}

interface ISmall_2
{
     void C();
     void D();
}

class MyClass:ISmall_2
{ ... }

이 방법 때문에 내 MyClass유일한 방법이 필요 (구현 할 수 D()C()도에 대한 더미 구현을 제공하도록 강요하지 않고,) A(), B()C():

그러나 두 번째 정의에 따르면 ( 1 , 2 , Nazar Merza의 답변 참조 ) ISP는 MyClient호출하는 메소드 가 필요하지 않은 MyService메소드를 인식해서는 안된다고 MyService말합니다. 즉, 및 MyClient의 기능 만 필요한 경우 대신C()D()

class MyService
{
    public void A();
    public void B();
    public void C();
    public void D();
}

/*client code*/
MyService service = ...;
service.C();
service.D();

MyService's메소드를 클라이언트 별 인터페이스 로 분리해야합니다 .

public interface ISmall_1
{
     void A();
     void B();
}

public interface ISmall_2
{
     void C();
     void D();
}

class MyService:ISmall_1, ISmall_2
{ ... }

/*client code*/
ISmall_2 service = ...;
service.C();
service.D();

따라서 이전 정의에서 ISP의 목표는 ” IFat 인터페이스를 구현하는 클래스의 수명을보다 쉽게 ​​만드는 것 “이고, 후자의 경우 ISP의 목표는 ” MyService의 메소드를 호출하는 클라이언트의 삶을 더 쉽게 만드는 것 “입니다.

ISP의 두 가지 정의 중 실제로 올바른 것은 무엇입니까?

@MARJAN VENEMA

1.

따라서 IFat를 더 작은 인터페이스로 분할하려고 할 때 멤버의 응집력에 따라 ISmallinterface를 결정해야하는 방법이 있습니다.

동일한 인터페이스 내에 응집성있는 방법을 사용하는 것이 합리적이지만 ISP 패턴을 사용하면 클라이언트의 요구가 인터페이스의 “응집성”보다 우선한다고 생각했습니다. 다시 말해, ISP를 통해 특정 인터페이스에 필요한 방법을 동일한 인터페이스에 포함시켜야한다고 생각했습니다. 비록 응집력을 위해 해당 인터페이스에 포함되어야하는 방법도 동일한 인터페이스에 포함시켜야합니까?

따라서 전화해야 할 클라이언트가 CutGreens많았지 만 GrillMeatISP 패턴을 준수 하지 않으 려면 두 가지 방법이 응집력이 뛰어나더라도 CutGreens내부 에만 배치해야 ICook하지만 내부 에 배치해서는 안됩니다 GrillMeat!

2.

나는 당신의 혼란이 첫 번째 정의에서 숨겨진 가정에 기인한다고 생각합니다. 구현 클래스는 이미 단일 책임 원칙을 따르고 있습니다.

“SRP를 따르지 않는 클래스를 구현”함으로써 구현하는 클래스 IFat또는 ISmall_1/ 를 구현하는 클래스를 참조하고 ISmall_2있습니까? 구현하는 클래스를 참조한다고 가정 IFat합니까? 그렇다면 왜 SRP를 아직 따르지 않았다고 가정합니까?

감사



답변

둘 다 맞다

ISP (Interface Segregation Principle)의 목적은 인터페이스를 작고 집중적으로 유지하는 것입니다. 모든 인터페이스 멤버는 매우 높은 응집력을 가져야합니다. 두 가지 정의는 모두 “전략 자 마스터”인터페이스를 피하기위한 것입니다.

인터페이스 분리와 SRP (Single Responsibility Principle)의 목표는 작고 응집력이 높은 소프트웨어 구성 요소를 보장하는 것입니다. 그들은 서로를 보완합니다. 인터페이스 분리는 인터페이스가 작고 집중적이며 응집력이 뛰어나도록합니다. 단일 책임 원칙에 따라 수업은 소규모, 집중력 및 응집력이 보장됩니다.

첫 번째로 언급 한 정의는 구현 자에 중점을두고, 두 번째는 클라이언트에 중점을 둡니다. @ user61852와 달리 구현자가 아닌 인터페이스의 사용자 / 호출자가됩니다.

나는 당신의 혼란이 첫 번째 정의에서 숨겨진 가정에 기인한다고 생각합니다. 구현 클래스는 이미 단일 책임 원칙을 따르고 있습니다.

클라이언트에게 인터페이스의 호출자 인 두 번째 정의는 의도 한 목표를 달성하는 더 좋은 방법입니다.

분리

귀하의 질문에 귀하는 다음과 같이 진술합니다.

이 방법으로 MyClass는 A (), B () 및 C ()에 대한 더미 구현을 제공하지 않고도 필요한 메소드 (D () 및 C ()) 만 구현할 수 있습니다.

그러나 그것은 세상을 뒤집어 놓고 있습니다.

  • 인터페이스를 구현하는 클래스는 구현중인 인터페이스에 필요한 것을 지시하지 않습니다.
  • 인터페이스는 구현 클래스가 제공해야하는 메소드를 나타냅니다.
  • 인터페이스의 호출자는 실제로 인터페이스를 제공하기 위해 인터페이스에 필요한 기능과 구현자가 제공해야하는 기능을 지시하는 것입니다.

따라서 IFat더 작은 인터페이스 로 분할 할 때 ISmall멤버가 얼마나 응집력이 있는지에 따라 인터페이스를 결정 해야하는 방법이 있습니다.

이 인터페이스를 고려하십시오.

interface IEverythingButTheKitchenSink
{
     void DoDishes();
     void CleanSink();
     void CutGreens();
     void GrillMeat();
}

어떤 방법을 사용 ICook하시겠습니까? 저것과 다른 몇 가지 일을하지만 다른 방법과는 다른 수업을하기 때문에 CleanSink함께 합 시겠습니까 GrillMeat? 또는 다음과 같은 두 개의보다 응집력있는 인터페이스로 분리 하시겠습니까?

interface IClean
{
     void DoDishes();
     void CleanSink();
}

interface ICook
{
     void CutGreens();
     void GrillMeat();
}

인터페이스 선언 참고

인터페이스 정의는 독립된 단위로 작성하는 것이 바람직하지만, 호출자 또는 구현 자와 함께 있어야하는 경우 실제로 호출자와 함께 있어야합니다. 그렇지 않으면 호출자는 인터페이스의 목적을 완전히 상실하는 구현 자에 즉시 의존합니다. 참조 : 기본 클래스와 동일한 파일에서 인터페이스 선언, 좋은 습관입니까? 프로그래머들에게 왜 우리는 그것들을 구현하는 클래스가 아닌 그것들을 사용하는 클래스와 인터페이스를 배치해야 하는가? StackOverflow에.


답변

Gang of Four 문서에 사용 된 “client”라는 단어를 서비스 소비자와 같은 “client”와 혼동합니다.

Gang of Four 정의에서 의도 한 “클라이언트”는 인터페이스를 구현하는 클래스입니다. 클래스 A가 인터페이스 B를 구현하는 경우 A는 B의 클라이언트라고 말합니다. 그렇지 않으면 “클라이언트”(소비자에서와 같이)가 “클라이언트”가 사용하지 않는 인터페이스를 구현하도록 강요해서는 안됩니다. 아무것도 구현하지 않습니다. 이 문구는 “클라이언트”를 “구현 자”로 볼 때만 의미가 있습니다.

“클라이언트”가 큰 인터페이스를 구현하는 다른 클래스의 메소드를 “소비”(호출)하는 클래스를 의미 한 경우, 관심있는 두 가지 메소드를 호출하고 나머지를 무시함으로써 나머지 클래스와의 연결을 끊기에 충분합니다. 사용하지 않는 방법.

원칙의 정신은 관련된 메소드 세트에만 관심이있을 때 전체 인터페이스를 준수하기 위해 더미 메소드를 구현해야하는 “클라이언트”(인터페이스를 구현하는 클래스)를 피하는 것입니다.

또한 가능한 한 적은 양의 커플 링을 사용하여 한곳에서 변경하면 영향이 적습니다. 인터페이스를 분리하면 커플 링이 줄어 듭니다.

이 문제는 인터페이스가 너무 많은 경우에 하나의 인터페이스 대신 여러 인터페이스로 나누어야하는 메소드가있을 때 나타납니다.

두 코드 예제 모두 괜찮습니다 . “클라이언트”는 “다른 클래스가 제공하는 서비스 / 방법을 소비 / 호출하는 클래스”를 의미한다고 가정합니다.

나는 당신이 준 세 가지 링크에서 설명 된 개념에서 모순을 찾지 않습니다.

SOLID 대화에서 “클라이언트”가 구현 자임을 분명히하십시오 .


답변

ISP는 클라이언트가 알아야 할 것보다 서비스에 대해 더 많이 알지 못하도록하는 것입니다 (예 : 관련없는 변경으로부터 서비스를 보호). 두 번째 정의가 정확합니다. 내 독서에 따르면,이 세 기사 중 하나만 다른 것을 제안하고 ( 첫 번째 기사 ) 그것은 명백한 잘못입니다. (편집 : 아니오, 잘못이 아니라 오도의 소지가 있습니다.)

첫 번째 정의는 LSP와 훨씬 밀접하게 연결되어 있습니다.


답변