728x90

좋은 객체 지향 프로그래밍 SOLID


용어 개념
SRP 단일 책임 원칙 = 하나의 클래스는 하나의 책임을 가짐
OCP 개방 및 폐쇄 원칙 = 확장에는 열려 있으나, 변경에는 닫힘
LSP 리스코프 치환 원칙 = 객체는 프로그램의 정확성을 지켜야
ISP 인터페이스 분리 원칙 = 여러 개의 인터페이스가 범용 하나보다 우수함 
DIP 의존관계 역전 원칙 = 추상화에 의존, 구체화에 의존 x

 

실무에서는 비즈니스 요구사항이 굉장히 자주 바뀌기 때문에 OCP와 DIP를 준수하는 것이 변경사항에 대비하기 유리하다고 한다. 

* 아래 코드는OCP와 DIP를 위반한 예제 코드다. 

public class OrderServiceImpl implements OrderService {
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
 private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
}

 

실제 주문서비스를 기능하기 위한 OrderServiceImpl(구현체)는 OrderService(인터페이스)를 상속받아 생성된다.

이때, 할인 정책을 도입하기 위해 DIscountPolicy(인터페이스)에 의존관계를 가지며 private final을 통해 외부에서 고정된 값으로 실제 할인정책(구현체)를 사용한다.

-> //주석 처리를 통해 Fix에서 Rate 정책으로 변경하려는 코드임을 알 수 있다.

OCP와 DIP 위반


2편에서 객체에 대해 설명하며 사용했던 그림이다.

 

JAVA는 객체 지향 프로그래밍? Jav Spring 프레임워크의 기본 (2)

들어가며 안녕하세요, Spring 프레임워크와 관련해 시리즈로 글을 쓰기로 마음을 먹었는데,, 사실 인프런의 김영한 선생님 커리큘럼을 수강중이라 수강 후기에 가까울 것 같습니다. ㅎㅎ 이전 글

wooltech.tistory.com

OrderServiceImpl을 운전자라고 치자.

DiscountPolicy가 자동차 역할, FixDiscountPolicy와 RateDiscountPolicy는 K3, 아반떼, 모델3라고 할 수 있다.

 

운전자는 어딘가로 이동하겠다는 목적을 위해 자동차를 이용하고자 한다. 이때 자동차를 타고 이동하는 것이 중요한 것이지 어떤 기종을 타는지는 목적을 이루는데 중요하지 않다.

그러나, 위 예제 코드에서는 K3를 타도록 지정되어 있는 것과 같다. - DIP 위반!

 

그리고 외부 사정으로 인해 K3를 이용하기 어려워 아반떼로 변경되는 상황에서 운전자에게 변경 정보를 입력해 아반떼를 찾아 타게 만들어야 하는 것이다. - OCP위반 !

Config 구성 클래스


운전자가 자동차 운전(실행)에만 집중할 수 있도록 자동차 설정은 Config라는 구성클래스를 통해 따로 관리해야한다.

package hello.core.order;
import ...

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);
 	}
}

 

package hello.core;
import ...

public class AppConfig {
	public MemberService memberService() {
		return new MemberServiceImpl(new MemoryMemberRepository());
	}
 	public OrderService orderService() {
 	return new OrderServiceImpl(
 		new MemoryMemberRepository(),
 		new FixDiscountPolicy());
 	}
}
  • 위 코드는 OrderService메서드에서 OrderServiceImpl 구현체를 생성하고 생성자 주입을 통해 FixDiscountPolicy 구현체를 주입하고 있다.
  • 아래 코드는 수정된 OrderServiceImpl 클래스다. DiscountPolicy만 의존관계를 갖고 나머지는 생성자 주입을 통해 구현체를 관리한다.

OrderServiceImpl과 Appconfig 클래스는 각각 역할을 구분하여 책임을 부여하고 있다. 이를 통해 각 클래스가 어떤 역할을 하는지 명확하게 확인할 수 있고 변경사항에도 대비할 수 있다.

 

Config클래스의 추가적인 기능 설명

더보기

Spring에서 Configuration 클래스는 애플리케이션의 구성 정보를 포함하고, 빈(Bean) 설정과 관련된 것들을 정의하는 역할을 합니다. 이 클래스는 Java 기반으로 작성되며, @Configuration 어노테이션을 사용하여 선언됩니다.

주요 목적은 다음과 같습니다:

  1. 빈(Bean) 설정: @Bean 어노테이션을 사용하여 빈 객체를 정의합니다. 빈은 스프링 컨테이너에서 관리되는 객체로, 필요한 곳에서 주입(Dependency Injection) 받을 수 있습니다.
  2. 환경 설정 및 프로퍼티 설정: 데이터베이스 연결 정보, 외부 서비스 URL 등과 같은 환경 설정 정보를 정의할 수 있습니다.
  3. 조건부 빈 설정: 특정 조건에 따라 빈을 설정하거나 제외할 수 있습니다.
  4. 다른 Configuration 클래스와의 조합: 여러 개의 Configuration 클래스를 작성하고, 조합하여 하나의 애플리케이션 컨텍스트를 구성할 수 있습니다.
  5. AOP(Aspect-Oriented Programming) 설정: AspectJ와 같은 AOP를 사용하기 위한 설정을 추가할 수 있습니다.
  6. 컴포넌트 스캔 제어: @ComponentScan 어노테이션을 통해 어느 패키지에서 컴포넌트를 스캔할지 설정할 수 있습니다.

+ Recent posts