[Java] Spring과 SOLID - 2편
웹/백엔드, Spring

[Java] Spring과 SOLID - 2편

이전 게시글에 이어서 OCP와 DIP에 대해서 설명하겠습니다.

목차

  • OCP, DIP정의
  • 위반 사례
  • 해결방안

 

정의

OCP(Open/closed priciple) : 확장에는 열려있으며 변경에는 닫혀있습니다.

DIP(Dependency inversion priciple) : 추상화에만 의존해야지, 구체화에 의존하면 안 됩니다.

 

이 정의만 봐서는 잘 이해가 안 됩니다. 따라서 코드를 가지고 설명하겠습니다.

 

회원 도메인입니다.

기능은 보시는 대로고, 회원 정보를 메모리에 저장 할 수도 있고, DB나 외부 시스템을 사용할 수 있기 때문에 미확정입니다.

그림 1

 

클래스 다이어그램입니다.

그림 2

그럼 위의 클래스 다이어그램의 MemberServiceImpl을 구현해봅시다.

public class MemberServiceImple implements MemberService{
	
    private final MemberRepository memberRepository = new MemoryMemberRepository();
	
    public void join(){...}
    public Member findById(){...}
}

 

 

DIP 위반 상황

위의 코드에서 문제점이 뭘까요?

바로 아래 부분이 잘못됐습니다. MemberServiceImple에서는 추상화에만 의존해야 합니다. 하지만 아래 코드는 MemoryMemberRepository 같은 구현체에도 의존을 합니다. 

따라서 DIP위반입니다.

private final MemberRepository memberRepository = new MemoryMemberRepository();

즉, 결론은 [그림 2]가 아니라 [그림 3]처럼 돼있기 때문에 DIP오류입니다.

그림 3


OCP 위반 상황

그렇다면 프로젝트 중간에 기획자가 회원 저장소를 메모리가 아니라 DB를 사용한다고 해봅시다.

이때 코드는 아래처럼 바뀌게 됩니다

 

private final MemberRepository memberRepository = new DbMemberRepository();

방금처럼 플젝을 하다가 기획이 바뀌게 되어 확장을 하게 되는 상황이 발생했습니다.

이것은 어떤 것을 위반한 걸까요?

OCP위반입니다. 확장에는 열려있지만 코드를 바꾸었기 때문입니다.

 


해결법

자. 그러면 위 같은 문제점을 어떻게 해결할까요?

다음처럼 바꾸면 됩니다.

public class MemberServiceImple implements MemberService{
	
    private final MemberRepository memberRepository;
	
    public void join(){...}
    public Member findById(){...}
}

하지만 문제는 있죠.

객체를 선언만 해주고 할당을 안 했기 때문에 null point exception이 발생합니다.

해결법은 다른 누군가가 MemoryMemberRepository, DbMemberRepository의 구현 객체를 대신 생성하고 주입하는 방법입니다.


AppConfig등장

우선 MemberServiceImple의 생성자 만들기

public class MemberServiceImple implements MemberService{
	
    private final MemberRepository memberRepository;
	
    MemberServiceImple(MemberRepository memberRepository){
    	this.memberRepository = new memberRepository();
    }
    
    public void join(){...}
    public Member findById(){...}
}

그리고 AppConfing를 통해 구현체를 주입합니다.

public class AppConfig {
  public MemberService memberService() {
  	return new MemberServiceImpl(new MemoryMemberRepository());
  }
}

사용 클래스

public class MemberApp {
  public static void main(String[] args) {
    AppConfig appConfig = new AppConfig();
    MemberService memberService = appConfig.memberService();
  }
}

위의 코드를 요약하면 그림 4처럼 됩니다. AppConfig클래스를 생성함으로써, 확장을 해도 코드를 변경하지 않아서 OCP를 만족하고, MemberServiceImpl이 MemoryMemberRepository구현체에 접근하지 않아서 DIP도 위반하지 않습니다.

그림 4

결론

AppConfig의 등장으로 사용 영역과 구성 영역으로 구분이 됐습니다.


출처

김영한. 스프링 핵심 원리 - 기본편