본문으로 바로가기
반응형

 

 

좋은 객체 지향 프로그래밍을 구현하기 위해선

SOLID 원칙을 따라야 한다.

https://healthdevelop.tistory.com/entry/spring28

 

[Spring] SOLID 원칙 | 객체 지향 설계의 5가지 원칙

좋은 객체 지향 설계를 하기 위해선 SOLID 원칙을 따라야 한다는 말을 들어봤을 것이다. ● SOLID 원칙이란? 클린코드로 유명한 로버트 마틴이 좋은 객체 지향 설계의 5가지 원칙을 정리한 것이다.

healthdevelop.tistory.com

 

 

 

 

위 원칙에서 까다로우면서 중요한 점은 바로 OCP(개방-폐쇄 원칙) 이다.

 

"프로그램을 수정하지 않고 확장을 해야 한다"

 

문장만 보면 어떻게 하라는 건지 감이 잡히질 않는다.

 

 

 

 

이러한 문제점을 

Spring 에선 Configuration(설정 정보)을 활용하여 해결할 수 있다.

 

 

 

 

 

 

설명보단 예시를 먼저 알아보는 것이 이해에 도움이 되기에,

고객이 주문을 하는 서비스를 예시를 통해 알아보자.

 

 

 

 

 

 

 


● 고객 - 주문 시스템  

 

 

 

ex) 클라이언트(고객)이 주문을 하는 시스템을 구현하려고 한다. 

    이때 회원 등급에 따라 차등 할인도 적용하려고 한다.

    고객의 등급이 vip면, 1000원을 할인해주거나 10%를 할인해주는 정책 중

    어느 것을 적용할지는 정해지지 않았다.

    그래서 두 개의 정책을 코드로 구현해놓고, 정책이 정해지면 그 정책에 따라 코드를 대입하기로 했다.

 

 

 

 

 

Repository, Service, Member 등의 관련 코드는 아래에서 찾을 수 있다.

https://github.com/leesh125/hello-Core-SpringBoot

 

GitHub - leesh125/hello-Core-SpringBoot

Contribute to leesh125/hello-Core-SpringBoot development by creating an account on GitHub.

github.com

    

 

 

 

 

 

 

 

○ 할인 정책 관련 코드

 

 

 

DicountPolicy.interface

package hello.core.discount;
import hello.core.member.Member;

public interface DiscountPolicy {

 	int discount(Member member, int price);
}

 

 

등급에 따라 할인된 가격을 return 해주는

할인 정책 관련 인터페이스를 먼저 작성한다.

 

 

 

 

 

 

 

 

 

 

FixDicountPolicy.java(고정 금액 할인 : 1000원 고정 할인)

package hello.core.discount;
import hello.core.member.Grade;
import hello.core.member.Member;
public class FixDiscountPolicy implements DiscountPolicy {

	private int discountFixAmount = 1000; //1000원 할인
    
 	@Override
 	public int discount(Member member, int price) {
 	if (member.getGrade() == Grade.VIP) {
 		return discountFixAmount;
 	} else {
 		return 0;
 	}
 	}	
}

 

DiscountPolicy 인터페이스를 상속받아

고정 할인 정책 클래스를 구현하였다.

 

 

 

 

 

 

 

 

 

 

RateDicountPolicy.java(비율 금액 할인 : 총 구매액의 10% 할인)

package hello.core.discount;
import hello.core.member.Grade;
import hello.core.member.Member;

public class RateDiscountPolicy implements DiscountPolicy {
	
    private int discountPercent = 10; //10% 할인
 	
    @Override
 	public int discount(Member member, int price) {
 		if (member.getGrade() == Grade.VIP) {
 			return price * discountPercent / 100;
 		} else {
 			return 0;
 		}
 }

 

DiscountPolicy 인터페이스를 상속받아

10% 할인 정책 클래스를 구현하였다.

 

 

 

 

 

아직 정책이 정해지지 않아 고정 할인 정책을 임의로 적용하여

주문 서비스 클래스를 작성해보자

 

 

 

 

 

 

 

 

 

 

OrderServiceImpl.java

package hello.core.order;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;

public class OrderServiceImpl implements OrderService {
	
    private final MemberRepository memberRepository = new MemoryMemberRepository();
 	private final DiscountPolicy discountPolicy = new FixDiscountPolicy(); // 할인 정책
	
    @Override
 	public Order createOrder(Long memberId, String itemName, int itemPrice) {
 		Member member = memberRepository.findById(memberId);
 		int discountPrice = discountPolicy.discount(member, itemPrice);
 		return new Order(memberId, itemName, itemPrice, discountPrice);
 	}
}

 

 

만약 여기서 10% 할인 정책으로 바꾸고 싶다면 어떻게 하면 될까?

 

주석 친 부분에 

new FixDiscountPolicy() 를   ->  new RateDiscountPolicy()로 바꿔주면 된다.

 

 

 

전체 코드를 수정할 필요 없이 이 정도면 SOLID 원칙을 잘 지키고 구현한 것 같다.

 

 

 

하지만 위 코드는 OCP 원칙을 위반했다.

그 이유는 아래와 같다.

 

 

OrderServiceImpl은 클라이언트가 직접 이용하는(요청하는) 코드이다.

 

할인 정책이 바뀐다면, 고객이 직접 정책을 정해주어야 한다.

 

즉, 고객은 서비스를 이용만 해야 한다.

정책을 정해주는 것은 우리 개발자들이 해야 하는 일이다.

 

 

 

또한 DIP(의존관계 역전 원칙)도 위반하고 있다.

 

 

OrderServiceImpl은 DicsountPolicy(인터페이스)에 의존함과 동시에

FixDicountPolicy(구현 클래스)도 의존하고 있다.

 

 

 

 

 

 

이러한 문제점들을 해결하기 위해서

스프링에선 @Configuration을 제공해준다.

 

 

 

하지만 지금은 순수 자바 코드로 설정을 먼저 하고자 한다. 

 

 

 

 

 

 

 


● AppConfig  

 

 

 

 

package hello.core;

import hello.core.discount.FixDiscountPolicy;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;

public class AppConfig {

 	public MemberService memberService() {
 		return new MemberServiceImpl(new MemoryMemberRepository());
 	}
    
 	public OrderService orderService() {
 		return new OrderServiceImpl(new MemoryMemberRepository(),new FixDiscountPolicy());
 	}
}

 

AppConfig를 작성하여 설정 정보들을 주입해준다.

 

이전에는 Service에서 구현체를 생성하고 주입하였지만,

그 역할을 대신해주는 설정 정보 코드를 별도로 작성해주는 것이다.

 

 

 

 

 

 

 

 

 

 

OrderServiceImpl.java

package hello.core.order;

import hello.core.discount.DiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;

public class OrderServiceImpl implements OrderService {
	
    private final MemberRepository memberRepository;
 	private final DiscountPolicy discountPolicy;
    
 	public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy 
	discountPolicy) {
 		this.memberRepository = memberRepository;
 		this.discountPolicy = discountPolicy;
	}
    
 	@Override
 	public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
 		int discountPrice = discountPolicy.discount(member, itemPrice);
 		return new Order(memberId, itemName, itemPrice, discountPrice);
 	}
}

 

OrderServiceImpl도 수정해주었다.

 

기존에 새로운 객체를 직접 할당했지만, 이제는 단지 interface에만 의존하고 있다.

 

 

 

 

 

이로써 SOLID 원칙을 준수하는 좋은 코드를 작성하였다.

 

 

 

 

 

반응형