1. 서론
Java공부를 하다 보면 꼭들어보는 단어 '객체지향'
내가 간단하게 정리한 객체지향은 "어떤 프로젝트를 개발하였을때 서로 연결되어 있지 않고 로봇 처럼 각각의 부품들을 조립하여 한 프로젝트가 완성되는 것"
2. 객체지향의 설계원칙(SOLID)
여기서 또 많이 듣는 원칙 객체 지향의 설계원칙(SOLID) 다섯 가지의 원칙을 앞 글자만 딴 법칙
- SRP(단일 책임의 원칙 : Single Responsibility Prinicple)
한 클래스는 하나의 책임만 가져야 한다
- OCP(개방폐쇄의 원칙 : Open Close Principle)
확장에는 열려있고, 수정에는 닫혀있어야 한다
- LSP(리스코브 치환의 원칙 : The Liskov Substitution Principle)
상위 타입은 하위타입으로 대체 할 수 있어야 한다
- ISP(인터페이스 분리의 원칙 : Interface Segregation Principle)
인터페이스 내에 메소드는 최소한 일수록 좋다(하나의 일반적인 인터페이스 보다 여러개의 구체적인 인터페이스가 낫다)
- DIP(의존성역전의 원칙 : Dependency Imversion Prinicple)
구체적인 클래스보다 상위클래스, 인터페이스, 추상 클래스와 같이 변하지 않을 가능성이 높은 클래스와 관계를 맺어라
면접 준비 하면서 달달 외웠지만... 실전 적용0% 예제 없이는 이해 못하는 사람...
이번에 인강들으면서 감이 쫌 잡아서 정리한다
인강 -> 인프런 스프링 핵심원리 - 기본편(김영한)
3. 예제
주문 할인 서비스를 개발하고자 한다.
근데 조건이 있다.
1. 회원 등급이 vip만 할인 해줌
2. 고정금액 할인, 퍼센테이지 할인 두가지 할인 방법이 존재 -> 뭐 선택할지 모름
3. 또 다른 할인 방법이 생길수도, 할인을 안 해줄수도 있음
DiscountPolicy
public interface DiscountPolicy {
int discount(Member member, int price);
}
할인 정책의 인터페이스!!! 구현 객체가 아니다!!
우리는 회원 등급에 따라 할인율이 달라지니 회원 정보와 물건의 원 가격을 알아야 함.
FixDiscountPolicy, RateDiscountPolicy
public class RateDiscountPolicy implements DiscountPolicy{
private int discountPercent = 10;
@Override
public int discount(Member member, int price) {
if (member.getGrade() == Grade.VIP) return price * discountPercent / 100;
else return 0;
}
}
public class FixDiscountPolicy implements DiscountPolicy{
private int DiscountFixAmount = 1000;
@Override
public int discount(Member member, int price) {
if( member.getGrade() == Grade.VIP) return DiscountFixAmount;
else return 0;
}
}
위에서 설정한 인터페이스를 받아 Override 하여 구현한다
RateDiscountPolicy -> 회원 등급이 vip면 10% 할인
FixDiscountPolicy ->회원 등급이 vip면 천원 할인
OrderServiceImpl
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository = new MemoryMemberRepository();
private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
@Override
public Order crateOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
}
주문 구현하는 클래스 이것도 인터페이스에 추상화 시켜 @Override하여 구현해준다.
주문할때 회원 정보, 아이템 이름, 아이템 가격과 할인 금액은 우리가 만든 discoutPolicy 정책을 활용하여 계산해준다.
근데...여기서 문제점이 생김..
문제점
private final MemberRepository memberRepository = new MemoryMemberRepository();
private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
이 두줄을 보면
MemberRepository와 DiscountPolicy를 보면 내가 정함....
위에 우리가 말했던 SOLID 설계원칙에 대입해보자
SRP -> 이 클래스는 주문해주는 클래스여서 주문만!! 해주는 클래스인데 MemberRepository도 뭘 선택할까 클래스가 정하고 DiscountPolicy도 뭘 선택할까 클래스가 정함...하나의 클래스가 여러가지 담당하고 있음...탈락
OCP -> 확장에는 열려있음 근데 수정에도 열려있음.. 왜냐면 가격 정책 변경할때 마다 이 클래스를 수정해줘야 함...탈락
DIP -> 변하지 않을 클래스(추상클래스) 관계 맺지 않음..아주 구체적인 할인 클래스와(FixDiscountPolicy) 관계 맺음...탈락
결론적으로는 이 클래스가 할인 정책을 변경하면 안됨!
이 클래스는 할인정책을 뭘 선택 하든 상관 없고 뭐가 들어올지도 몰라야 한다.
그래서 구현 클래스를 연결해주고 선택하는 설정 클래스를 만들어주어야 함!!
4. 해결
AppConfig
public class AppConfig {
public MemberService memberService(){
return new MemberServiceImpl(memberRepository());
}
private MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
public OrderService orderService(){
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
public DiscountPolicy discountPolicy(){
return new RateDiscountPolicy();
}
}
구현 클래스를 연결해주고 선택하는 설정 클래스
이 클래스를 통해 OrderService를 이용한다.
OrderService를 이용할때 생성자를 통해 내가 무슨 정책을 사용할것인가!!! 를 선택해서 보내줌!
정책 변경을 수정할 일이 있다? 그럼 AppConfig 수정하면 됨
OrderServiceImpl
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 crateOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
}
수정된 OrderService를 보자
생성자를 통해 구체적인 회원 저장소와 할인 정책을 가지고 온다.
여기에서 할인정책을 뭘 쓸껀지 정하지 않는다 무슨 정책을가지고 오던지 다 ㄱㅊ
아까 문제점이였던 SOLID 할인 정책을 다시 생각해보자
SRP -> 주문만 해준다 주문 외에 해야 할일? 없음
OCP -> 수정에도 닫혀있다. 할인 정책변경할일이 있으면 이 클래스에서 수정하지 않고 AppConfig에서 수정함
DIP -> 인터페이스 (memeberRepository, discountPolicy)와 연결되어 있음 구체적인 RateDiscount, FixDiscount와 연결되어 있지 않음
5. 테스트
public class OrderServiceTest {
MemberService memberService;
OrderService orderService;
@BeforeEach //테스트 하기 전에 실행 함
public void beforeEach(){
AppConfig appConfig = new AppConfig();
memberService = appConfig.memberService();
orderService = appConfig.orderService();
}
@Test
void createOrder() {
Long memberId = 1L;
Member member = new Member(memberId, "memberA", Grade.VIP);
memberService.join(member);
Order order = orderService.crateOrder(memberId, "ItemA", 1000);
Assertions.assertThat(order.getDiscountPrice()).isEqualTo(1000);
}
}
Junit을 사용하여 테스트 해보았다
Appconfig가 무슨 정책을 사용할지 회원레파지토리를 뭘 사용할지 정하기 때문에
memberService와 orderService는 Appcofig를 통해 만들어야 한다!
통과 완
출저 : 인프런 스츠링의 핵심원리 - 기본편 (김영한)
'Java' 카테고리의 다른 글
[JAVA] Collection(List, Set) (0) | 2023.02.13 |
---|---|
toLowerCase(), to UpperCase(), isUpperCase(), isLowerCase() (0) | 2023.01.01 |
String to CharArray (1) | 2023.01.01 |