인터페이스 분리 원리가 구체적인 방법에 적용됩니까?


10

인터페이스 분리 원칙에 따르면 클라이언트가 사용하지 않는 메소드에 의존해서는 안되므로 클라이언트는 인터페이스 메소드에 대해 빈 메소드를 구현해서는 안되며, 그렇지 않으면이 인터페이스 메소드를 다른 인터페이스에 배치해야합니다.

그러나 구체적인 방법은 어떻습니까? 모든 고객이 사용하지 않는 방법을 분리해야합니까? 다음 클래스를 고려하십시오.

public class Car{
    ....

    public boolean isQualityPass(){
        ...
    }

    public int getTax(){
        ...
    }

    public int getCost(){
        ...
    }
}

public class CarShop{
    ...
    public int getCarPrice(int carId){
        Car car=carList[carId];
        int price=car.getTax() + car.getCost()... (some formula);
        return price;
    }
}

위의 코드에서 CarShop은 isQualityPass ()를 새 클래스로 분리하면 Car에서 isQualityPass () 메소드를 전혀 사용하지 않습니다.

public class CheckCarQualityPass{
    public boolean isQualityPass(Car car){
    }
}

CarShop의 커플 링을 줄이기 위해? isQualityPass ()에 추가 종속성이 필요한 경우 한 번 생각하기 때문에 예를 들면 다음과 같습니다.

public boolean isQualityPass(){
    HttpClient client=...
}

CarShop은 HttpClient를 실제로 사용하지 않더라도 HttpClient에 의존합니다. 따라서 내 질문은 인터페이스 분리 원칙에 따라 모든 클라이언트가 사용하지 않는 구체적인 방법을 분리하여 커플 링을 줄이기 위해 클라이언트가 실제로 사용할 때만 클라이언트에 의존하도록해야합니까?


2
보통 "품질"을 통과 한 자동차를 알고 있습니까? 아니면 자체적으로 캡슐화 할 수있는 비즈니스 규칙일까요?
Laiv

2
단어로 인터페이스 ISP에서 알 수 있듯이 그것은 관하여 인터페이스 . 따라서 Car클래스에 모든 사용자가 알기를 원하지 않는 메소드가있는 경우 클래스가 구현하는 인터페이스 를 하나 이상 작성 Car하여 인터페이스 컨텍스트에서 유용한 메소드 만 선언하십시오.
Timothy Truckle

@Laiv 우리는 곧 그보다 훨씬 더 많은 차량을 보게 될 것입니다. ;)
통합 모델링 샌드위치

1
자동차는 제조사가 알고 싶어하는 것을 알게됩니다. 폭스 바겐은 내가 말하는 것에 대해 알고있다 :-)
Laiv

1
인터페이스를 언급했지만 예제에 인터페이스가 없습니다. 우리는 자동차를 인터페이스로 바꾸는 방법과 해당 인터페이스에 포함시킬 방법에 대해 이야기하고 있습니까?
Neil

답변:


6

귀하의 예에서에 CarShop의존하지 않으며 isQualityPass메소드에 대해 빈 구현을 강요하지 않습니다. 관련된 인터페이스조차 없습니다. 따라서 "ISP"라는 용어는 여기에 맞지 않습니다. 그리고 같은 isQualityPass방법이 Car추가 책임이나 종속성으로 과부하되지 않고 객체 에 잘 맞는 방법 이라면 괜찮습니다. 메소드를 사용하지 않는 클라이언트가 하나 있기 때문에 클래스의 공용 메소드를 다른 위치로 리팩터링 할 필요가 없습니다.

그러나 어떤 클라이언트가 메소드를 사용하든 사용하지 않든 관계없이 도메인 클래스를 Car직접 의존하는 것은 HttpClient좋은 생각이 아닙니다. 논리를 별도의 클래스로 옮기는 CheckCarQualityPass것을 "ISP"라고하는 것이 아니라 "관심 분리"라고 합니다. 재사용 가능한 자동차 객체의 문제는 아마도 직접적으로는 아니더라도 외부 HTTP 호출을해서는 안됩니다. 이로 인해 재사용 성이 제한되고 테스트 가능성이 너무 높아집니다.

isQualityPass다른 클래스로 쉽게 이동할 수없는 경우 대안은 생성시 삽입 Http되는 추상 인터페이스 IHttpClient를 통해 Car또는 전체 "QualityPass"검사 전략 (Http 요청이 캡슐화 된 상태)을 Car오브젝트에 삽입하여 호출하는 것입니다. . 그러나 IMHO는 두 번째로 가장 좋은 솔루션입니다. 왜냐하면 전체 복잡성을 줄이는 대신 복잡성을 증가시키기 때문입니다.


isQualityPass 메소드를 해결하기위한 전략 패턴은 어떻습니까?
Laiv

@Laiv : 기술적으로는 이것이 효과가 있지만 (내 편집 참조) 더 복잡한 Car객체가됩니다. 솔루션에 대한 나의 첫 번째 선택은 아닐 것입니다 (적어도이 예의 맥락에서는 아닙니다). 그러나 "실제"코드에서는 더 의미가있을 수 있습니다.
Doc Brown

6

따라서 내 질문은 인터페이스 분리 원칙에 따라 모든 클라이언트가 사용하지 않는 구체적인 방법을 분리하여 커플 링을 줄이기 위해 클라이언트가 실제로 사용할 때만 클라이언트에 의존하도록해야합니까?

인터페이스 분리 원칙은 필요하지 않은 것에 대한 액세스를 금지하는 것이 아닙니다. 필요없는 것에 대한 접근을 주장하지 않는 것입니다.

인터페이스는이를 구현하는 클래스가 소유하지 않습니다. 그것들은 그것들을 사용하는 객체에 의해 소유됩니다.

public class CarShop{
    ...
    public int getCarPrice(int carId){
        Car car=carList[carId];
        int price=car.getTax() + car.getCost()... (some formula);
        return price;
    }
}

여기서 사용되는 것은 getTax()getCost()입니다. 주장하는 것은를 통해 액세스 할 수있는 모든 것 Car입니다. 문제는 주장 이 필요하지 않은 Car액세스를 주장한다는 의미 isQualityPass()입니다.

이것은 고칠 수 있습니다. 구체적으로 고정 할 수 있는지 묻습니다. 할 수 있습니다.

public class CarShop{
    ...
    public int getCarPrice(int carId){
        CarLiability carLiability=carLiabilityList[carId];
        int price=carLiability.getTax() + carLiability.getCost()... (some formula);
        return price;
    }
}

그 코드 중 어느 것도 CarLiability인터페이스인지 구체적인 클래스 인지 알 수 없습니다 . 좋은 일입니다. 알고 싶지 않습니다.

인터페이스 인 경우 Car구현할 수 있습니다. 비록 때문에 ISP에 위배되지 것 isQuality()입니다 Car CarShop그것을 주장하지 않습니다. 이건 괜찮아.

구체적이라면 isQuality()존재하지 않거나 다른 곳으로 이동 한 것일 수 있습니다 . 이건 괜찮아.

또한 작업을 위임 CarLiability하는 콘크리트 래퍼 일 수도 있습니다 Car. 너무 오래로 CarLiability노출하지 않습니다 isQuality()다음 CarShop괜찮습니다. 물론 이것은 단지 길 CarLiability을 따라갈 수 있고 Car, ISP와 동일한 방법으로 따라야하는 방법을 찾아 내야합니다 CarShop.

즉, ISP isQuality()Car인해 제거 할 필요가 없습니다 . 묵시적 필요성은 isQuality()필요 하지 않기 CarShop때문에 제거 해야하므로 요구 CarShop해서는 안됩니다.


4

인터페이스 분리 원리가 구체적인 방법에 적용됩니까?

그러나 구체적인 방법은 어떻습니까? 모든 고객이 사용하지 않는 방법을 분리해야합니까?

실제로는 아닙니다. 에서 숨길 수있는 다른 방법이 Car.isQualityPass있습니다 CarShop.

1. 액세스 수정 자

로부터 데메테르의 법칙의 관점, 우리는 고려할 수 CarCardShop일하지 않는 친구 . 다음에하는 것이 합법적입니다.

package com.my.package.domain.model
public class Car{
    ...
    protected boolean isQualityPass(){...}
}

package com.my.package.domain.services
public class CarShop{
    ...
}

두 구성 요소가 서로 다른 패키지에 있음을 유의하십시오. 이제 보호 된 행동에 CarShop대한 가시성이 없습니다 . (위의 예가 그렇게 단순 해 보인다면 미리 실례합니다).Car

2. 인터페이스 분리

ISP는 우리가하지 구체적인 클래스로 추상화 작업을한다는 전제에서 작동합니다. ISP 구현 및 역할 인터페이스에 대해 잘 알고 있다고 가정하겠습니다 .

실제 Car구현 에도 불구 하고 ISP를 실행하는 데 방해가되는 것은 없습니다.

//role interfaces 
public interface Billable{
   public int getCosts();
   public int getTaxs();
}

//role interfaces
public interface QualityAssurance{
   public boolean isQualityPass();
}

public class Car implements Billable, QualityAssurance{
   ...
}

public class CarShop {
  ...
  public int getPrice(Billable billable){
     return billable.getCosts() * billable.getTaxs();
  }
}

내가 여기서 한 일. 나는 사이의 상호 작용 좁혀 CarCarShop스루 청구 인터페이스 역할 . getPrice서명 변경에 유의하십시오. 나는 의도적으로 주장을 수정했다. 사용 가능한 역할 인터페이스CarShop 중 하나에 만 "연결 / 결합"되어 있음을 분명히하고 싶었습니다 . 실제 구현을 따를 수는 있었지만 실제 구현 세부 정보를 알지 못하고 실제 클래스 에 대한 실제 액세스 권한 (가시성)이 두렵습니다 . 있는 경우 ISP가 수행하는 모든 작업은 개발자의 손에 따라 Billable 인터페이스 만 사용하여 캐스팅하고 작업하기 때문에 쓸모가 없게됩니다 . 우리가 아무리 체계적이든, 유혹은 항상 거기에있을 것입니다.getPrice(String carId)

3. 단일 책임

내가 만약이 의존성 사이에 말할 수있는 위치에 아니에요 두려워 Car하고 HttpClient적절한이지만, 내가 @DocBrown에 동의, 그것은 보람 설계 검토하는 것이 몇 가지 경고를 발생시킵니다. Demeter 's Law 나 ISP는이 시점에서 귀하의 설계를 "더 나은"것으로 만들지 않습니다. 문제를 숨기고 해결하지는 않습니다.

가능한 솔루션으로 DocBrown 전략 패턴 을 제안했습니다 . 나는 그 패턴이 복잡성을 더한다는 것에 동의했지만, 재 설계도 가능할 것이라고 생각한다 . 트레이드 오프 (trade-off)이고, 더 많은 디커플링 (decoupling)을 원할수록 더 많은 움직이는 부분 (보통)이 있습니다. 어쨌든, 나는 재 설계에 동의하는 것이 좋습니다.

합산

아니요, 구체적인 메서드를 외부 클래스로 옮길 필요가 없으므로 액세스 할 수 없습니다. 수많은 소비자가있을 수 있습니다. 새로운 소비자 가 등장 할 때마다 모든 구체적인 방법을 외부 클래스로 옮기시겠습니까 ? 나는 당신이하지 않기를 바랍니다.

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