본문 바로가기

개발공부/원티드 챌린지 정리

9월 백엔드 챌린지 정리 : 클린 코드 (4)

728x90

ISP : Interface Segregation Principle

인터페이스도 작게 쪼개자

객체는 자신이 사용하는 기능(메서드)에만 의존해야 한다는 원칙에 따라 분리하는 것과 마찬가지로, 인터페이스는 그 인터페이스를 사용하는 클라이언트(객체)를 기준으로 분리해야 한다. 클래스(인터페이스)가 가진 책임의 크기를 줄일 수 있고, 관심사의 분리를 해낼 수 있으며 확장성을 가져갈 수 있기 때문에 필요하다

A 인터페이스를 상속하는 B와 C 클래스가 있다. B는 A의 모든 메서드를 사용하기 때문에 상속에 문제가 없지만, C의 경우 A의 모든 메서드를 사용할 필요가 없을 때, C는 사용하지도 않는 메서드와 의존성을 맺는 것이다.

이러한 경우, ISP를 위반하면서, A의 a-1 메서드(C가 사용하지 않는 메서드)에 변경이 일어날 경우, C에서도 변경해야 하며, 컴파일과 배포를 다시 수행해야 한다

DIP : Dependency Inversion Principle

추상화 또는 인터페이스에 의존해라

객체 A에서 기능 구현을 위해 객체 B의 메서드 C를 사용해야 하는 경우, A는 B를 알고 있어야 하기 때문에 B를 의존해야 한다. 이 떄, 구체화된 객체를 의존하는 것이 아닌 추상화된 클래스와 의존 관계를 맺어야 한다는 것이 DIP다

의존관계를 맺을 때 변하기 쉬운 것과 관계를 맺는 것이 아니라 변하지 않는 것에 의존해야 한다는 뜻이며, 이를 구분하는 것은 다음을 생각해보면 편하다.

"아이"는 "장난감"을 가지고 놀아야 하는데, 그렇다면 "인형"이나 "퍼즐"과 의존관계를 맺는 것이 아니라 "장난감"과 의존관계를 맺어야 한다

 

왜 중요할까? : 변화에 유연하게 대처할 수 있는 구조를 설계하도록 돕는 원칙이며, OCP를 뒷받침한다

 

적용을 위한 규칙

1) 상위 타입은 하위 타입에 의존해서는 안된다

2) 추상화는 세부 사항에 의존해서는 안된다

 

* 잘못 구현된 예시

간편결제 서비스를 간략하게 표현한 PayService로, 적절한 할인을 위해서는 결제 수단을 다양하게 활용해야 한다. 그러나 위와 같이 삼성페이와 의존관계를 맺는 경우 DIP를 위반하게 된다

package com.wanted.preonboarding.clean.code.solid.dip;

public class PayService {
    SamsungPay samsungPay; // 의존 저수준 객체
		ApplyPay applyPay; // 의존 저수준 객체
		Payco payco; // 의존 저수준 객체
		Naverpay naverPay;

    public PayService(SamsungPay s, ApplyPay a, Payco p, Naverpay n){
        samsungPay = s;
        applyPay = a;
        payco = p;
				naverPay = n;
    }

    public void pay(String type, int toAmount){
				
        System.out.println("간편결제 서비스 호출");
				switch(type){
					case "samsung":
						if(samsungPay != null)
							samsungPay.pay(toAmount);
					break;
					case "apply":
						applyPay.pay(toAmount);
					break;
					case "payco":
						payco.pay(toAmount);
					break;
					case "naverpay":
						naverPay.pay(toAmount);
					break;
					default:
						throwsa NotfoundException("지원하지 않는 결제수단 입니다");
    }


}
package com.wanted.preonboarding.clean.code.solid.dip;

public class SamsungPay {

    public boolean connect(){
        if(!auth())
            return false;
        // To Do Connection;
        return true;
    }
    private boolean auth(){
        return true;
    }
    public void pay(int amount){
        System.out.println("PAY");
    }
}

 

* 올바르게 구현된 예시

PayService는 Payment와 의존관계를 맺음으로, 할인을 위해 다양한 하위 타입 결제수단을 활용할 수 있다

package com.wanted.preonboarding.clean.code.solid.dip;

public interface Payment {
    public boolean pay(int amount);
}
package com.wanted.preonboarding.clean.code.solid.dip;

public class PayService {
    Payment payment; // 의존 저수준 객체

    public PayService(Payment p){ // 구현체 생성하고 결정할 때는 switch 써!
        payment = p;
    }

    public void pay(int toAmount){
        System.out.print("간편결제 서비스 호출");
        payment.pay(toAmount);
    }
}
package com.wanted.preonboarding.clean.code.solid.dip;

public class SamsungPay implements Payment {

    public boolean connect(){
        if(!auth())
            return false;
        // To Do Connection;
        return true;
    }
    private boolean auth(){
        return true;
    }
    @Override
    public boolean pay(int amount){
        System.out.println("PAY");
        return true;
    }
}
728x90