1. 소개

이 예제에서는 SOLID 원칙 중 하나인 인터페이스 분리 원칙에 대해 설명합니다 . "SOLID"에서 "I"를 나타내는 인터페이스 분리는 단순히 더 큰 인터페이스를 더 작은 것으로 분할해야 함을 의미합니다.

따라서 클래스를 구현할 때 원치 않는 메서드를 구현할 필요가 없도록 합니다.

2. 인터페이스 분리 원칙

이 원칙 은 Robert C. Martin에 의해 처음 정의되었습니다 .

이 원칙의 목표는 애플리케이션 인터페이스를 더 작은 인터페이스로 나누어 더 큰 인터페이스를 사용하는 부작용을 줄이는 것입니다 . 각 클래스 또는 인터페이스가 단일 목적 을 수행하는 단일 책임 원칙 과 유사합니다 .

정확한 애플리케이션 설계와 올바른 추상화는 인터페이스 분리 원칙의 핵심입니다. 응용 프로그램의 설계 단계에서 더 많은 시간과 노력이 필요하고 코드 복잡성이 증가할 수 있지만 결국에는 유연한 코드를 얻게 됩니다.

이후 섹션에서 원칙을 위반한 몇 가지 예를 살펴본 다음 원칙을 올바르게 적용하여 문제를 해결합니다.

3. 샘플 인터페이스 및 구현

BankPayment 구현에서 사용하는 결제 인터페이스 가 있는 상황을 살펴보겠습니다 .

public interface Payment { 
    void initiatePayments();
    Object status();
    List<Object> getPayments();
}

그리고 구현:

public class BankPayment implements Payment {

    @Override
    public void initiatePayments() {
       // ...
    }

    @Override
    public Object status() {
        // ...
    }

    @Override
    public List<Object> getPayments() {
        // ...
    }
}

단순화를 위해 이러한 메서드의 실제 비즈니스 구현은 무시하겠습니다.

이것은 매우 분명합니다. 지금까지 BankPayment 를 구현하는 클래스에는 Payment 인터페이스 의 모든 메소드가 필요합니다 . 따라서 원칙에 위배되지 않습니다.

4. 인터페이스 오염

이제 시간이 흐르고 더 많은 기능이 추가됨에 따라 LoanPayment 서비스를 추가해야 합니다. 이 서비스도 일종의 지불  이지만 몇 가지 작업이 더 있습니다.

이 새로운 기능을 개발하기 위해 결제 인터페이스에 새로운 방법을 추가합니다.

public interface Payment {
 
    // original methods
    ...
    void intiateLoanSettlement();
    void initiateRePayment();
}

다음으로 LoanPayment 구현이 있습니다.

public class LoanPayment implements Payment {

    @Override
    public void initiatePayments() {
        throw new UnsupportedOperationException("This is not a bank payment");
    }

    @Override
    public Object status() {
        // ...
    }

    @Override
    public List<Object> getPayments() {
        // ...
    }

    @Override
    public void intiateLoanSettlement() {
        // ...
    }

    @Override
    public void initiateRePayment() {
        // ...
    }
}

이제 Payment  인터페이스가 변경되고 더 많은 메소드가 추가되었으므로 모든 구현 클래스는 이제 새 메소드를 구현해야 합니다. 문제는 이를 구현하는 것이 원치 않으며 많은 부작용을 초래할 수 있다는 것입니다. 여기에서 LoanPayment 구현 클래스는 실제 필요 없이 initialPayments ()  를 구현해야 합니다. 따라서 원칙을 위반합니다.

따라서 BankPayment  클래스 는 어떻게 됩니까 ?

public class BankPayment implements Payment {

    @Override
    public void initiatePayments() {
        // ...
    }

    @Override
    public Object status() {
        // ...
    }

    @Override
    public List<Object> getPayments() {
        // ...
    }

    @Override
    public void intiateLoanSettlement() {
        throw new UnsupportedOperationException("This is not a loan payment");
    }

    @Override
    public void initiateRePayment() {
        throw new UnsupportedOperationException("This is not a loan payment");
    }
}

BankPayment 구현이 이제 새 메서드를 구현했습니다 그리고 그것들이 필요하지 않고 논리가 없기 때문에 UnsupportedOperationException 을 던질 뿐입니다 . 여기에서 우리는 원칙을 위반하기 시작합니다.

다음 섹션에서는 이 문제를 해결하는 방법을 살펴보겠습니다.

5. 원칙 적용

마지막 섹션에서 우리는 의도적으로 인터페이스를 오염시키고 원칙을 위반했습니다. 이 섹션에서는 원칙을 위반하지 않고 대출 지불에 대한 새로운 기능을 추가하는 방법을 살펴보겠습니다.

각 지불 유형에 대한 인터페이스를 분석해 보겠습니다. 현재 상황:

클래스 다이어그램과 이전 섹션의 인터페이스를 참조하여 두 구현 모두에서 status()getPayments()  메서드가 필요하다는 점에 유의하십시오. 반면, initialPayments () 는 BankPayment 에서만 필요하고, initialLoanSettlement () 및 initialRePayment () 메서드는 LoanPayment 전용입니다 .

정렬이 완료되면 인터페이스를 분해하고 인터페이스 분리 원칙을 적용해 보겠습니다. 따라서 이제 공통 인터페이스가 있습니다.

public interface Payment {
    Object status();
    List<Object> getPayments();
}

그리고 두 가지 유형의 결제를 위한 두 가지 추가 인터페이스:

public interface Bank extends Payment {
    void initiatePayments();
}
public interface Loan extends Payment {
    void intiateLoanSettlement();
    void initiateRePayment();
}

BankPayment 로 시작하는 각 구현은 다음과 같습니다 .

public class BankPayment implements Bank {

    @Override
    public void initiatePayments() {
        // ...
    }

    @Override
    public Object status() {
        // ...
    }

    @Override
    public List<Object> getPayments() {
        // ...
    }
}

마지막으로 개정된 LoanPayment 구현:

public class LoanPayment implements Loan {

    @Override
    public void intiateLoanSettlement() {
        // ...
    }

    @Override
    public void initiateRePayment() {
        // ...
    }

    @Override
    public Object status() {
        // ...
    }

    @Override
    public List<Object> getPayments() {
        // ...
    }
}

이제 새 클래스 다이어그램을 검토해 보겠습니다.

보시다시피 인터페이스는 원칙을 위반하지 않습니다. 구현은 빈 메서드를 제공할 필요가 없습니다. 이것은 코드를 깨끗하게 유지하고 버그의 가능성을 줄입니다.

6. 결론

이 예제에서 우리는 인터페이스 분리 원칙을 따르지 않고 이 편차로 인해 발생하는 문제를 처음으로 확인하는 간단한 시나리오를 살펴보았습니다. 그런 다음 이러한 문제를 피하기 위해 원리를 올바르게 적용하는 방법을 보여주었습니다.

수정할 수 없는 오염된 레거시 인터페이스를 처리하는 경우 어댑터 패턴 이 유용할 수 있습니다.

인터페이스 분리 원칙은 응용 프로그램을 설계하고 개발할 때 중요한 개념입니다. 이 원칙을 준수하면 여러 책임이 있는 부풀려진 인터페이스를 피하는 데 도움이 됩니다. 이는 결국 단일 책임 원칙을 따르는 데에도 도움이 됩니다.

 

항상 그렇듯이 코드는 GitHub 에서 사용할 수 있습니다 .

Generic footer banner