JiwonDev

#3 ์Šคํ”„๋ง ํ•ต์‹ฌ ์›๋ฆฌ - ๊ธฐ์กด์˜ ๋ถˆํŽธํ•œ ์„ค๊ณ„ ๋ฐฉ์‹

by JiwonDev

ํ•ด๋‹น ๊ธ€์€ ๊น€์˜ํ•œ๋‹˜์˜ ์ธํ”„๋Ÿฐ ๊ฐ•์˜๋ฅผ ๊ณต๋ถ€ํ•˜๊ณ  ์ •๋ฆฌํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

 

๊น€์˜ํ•œ ์˜ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜

๊ด€์‹ฌ ์žˆ๋Š” ๊ฐ•์˜๊ฐ€ ์žˆ๋‹ค๋ฉด ์ง€๊ธˆ ๋‹น์žฅ ์‹œ์ž‘ํ•˜์„ธ์š”! ์ธํ”„๋Ÿฐ์€ ์–ธ์ œ๋‚˜ ๋‹น์‹ ์˜ ์„ฑ์žฅ์„ ์‘์›ํ•ฉ๋‹ˆ๋‹ค. - ํ•™์Šตํ•˜๊ธฐ | ์ธํ”„๋Ÿฐ...

www.inflearn.com

์Šคํ”„๋ง ์—†์ด ์ˆœ์ˆ˜ํ•œ ์ž๋ฐ”์ฝ”๋“œ๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž.

 

# ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐํš์ž์˜ ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž. ์š”๊ตฌ์‚ฌํ•ญ ์ค‘์—๋Š” ์•„์ง ํ™•์ •๋‚˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜, ๊ฐœ๋ฐœ ๋„์ค‘ ๋ณ€๊ฒฝํ•  ์ˆ˜๋„ ์žˆ๋Š” ๋‚ด์šฉ๋“ค์ด ํฌํ•จ๋˜์–ด ์žˆ๋‹ค.

ํ™•์ •๋‚œ๊ฑด ์—†์ง€๋งŒ, ์‹œ๊ฐ„์€ ์—†๋‹ค. ์šฐ๋ฆฌ๋Š” ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ•ด์•ผ ํ•œ๋‹ค.

# ํšŒ์›

# ํšŒ์› ๋„๋ฉ”์ธ ์„ค๊ณ„

์šฐ์„ , ๋น„์ฆˆ๋‹ˆ์Šค์˜ ํ•ต์‹ฌ์ธ ํšŒ์›์˜ ๋„๋ฉ”์ธ์— ๋Œ€ํ•ด ์„ค๊ณ„๋ฅผ ์‹œ์ž‘ํ•ด๋ณด์ž.

๋„๋ฉ”์ธ์ด ๋ญ์ฃ 

๋”๋ณด๊ธฐ

ํ•ด๊ฒฐํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฌธ์ œ ๋ฐ ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋„๋ฉ”์ธ(Domain)์ด๋ผ๊ณ  ํ‘œํ˜„ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์„œ์  ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค๊ณ ์ž ํ•œ๋‹ค๋ฉด, ๋„๋ฉ”์ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

๋„๋ฉ”์ธ[์ฃผ๋ฌธ, ํšŒ์›, ๊ฒฐ์ œ, ๋ฐฐ์†ก]์˜ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ํ‘œํ˜„ํ•œ ๊ทธ๋ฆผ

์ด๋ ‡๊ฒŒ ๋งŒ๋“  ๋„๋ฉ”์ธ ๋ชจ๋ธ์„ ๋ฐ”ํƒ•์œผ๋กœ ๊ตฌ์ฒดํ™”ํ•˜์—ฌ ํด๋ž˜์Šค ๋‹ค์ด์–ด๊ทธ๋žจ๊ณผ ๊ฐ์ฒด ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๋งŒ๋“ ๋‹ค. 

ํด๋ž˜์Šค ๋‹ค์ด์–ด๊ทธ๋žจ. ๊ฐ์ฒด๋ฅผ Entity, ๊ฐ์ฒด๊ฐ€ ๊ฐ€์ง„ ๊ฐ’๋“ค์„ Value๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

ํ˜„์žฌ ์ฃผ์–ด์ง„ ํšŒ์› ๋„๋ฉ”์ธ์˜ ์š”๊ตฌ์‚ฌํ•ญ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • ํšŒ์›์„ ๊ฐ€์ž…ํ•˜๊ณ  ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค
    ⇒ ํšŒ์›๊ฐ€์ž…๊ณผ ์กฐํšŒ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” [ํšŒ์› ์„œ๋น„์Šค] ๋ฅผ ๋งŒ๋“ ๋‹ค.
  • ํšŒ์›์€ ์ผ๋ฐ˜๊ณผ VIP ๋‘ ๊ฐ€์ง€ ๋“ฑ๊ธ‰์ด ์žˆ๋‹ค.
    ⇒ ํšŒ์› ๋„๋ฉ”์ธ์€ ๋“ฑ๊ธ‰ ์ •๋ณด๋ฅผ ๊ฐ€์ง„๋‹ค. Enum ์„ ์ด์šฉํ•˜์—ฌ ๋“ฑ๊ธ‰์„ ๋‚˜ํƒ€๋‚ด๋Š” ํƒ€์ž…์„ ๋งŒ๋“ ๋‹ค.
  • ํšŒ์› ๋ฐ์ดํ„ฐ ์ž์ฒด DB๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๊ณ , ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ์—ฐ๋™ํ•  ์ˆ˜ ์žˆ๋‹ค.(๋ฏธํ™•์ •)
    ⇒ ํšŒ์› ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ์—ญํ• , [ํšŒ์› ์ €์žฅ์†Œ] ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ๋“ ๋‹ค.

ํ˜‘๋ ฅ๊ด€๊ณ„, ์—ญํ• ๊ณผ ์ฑ…์ž„์„ ์ž˜ ์ƒ๊ฐํ•ด์„œ ๊ตฌํ˜„ํ•˜์ž.


์ด๋ ‡๊ฒŒ ๋งŒ๋“  ๋„๋ฉ”์ธ์„ ์ด์šฉํ•˜์—ฌ ๊ฐ์ฒด & ํด๋ž˜์Šค ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๋งŒ๋“ค์–ด๋ณด์ž.

  • [ํšŒ์› ์„œ๋น„์Šค]๋Š” MemberService ์ธํ„ฐํŽ˜์ด์Šค(์—ญํ• )์„ ๋งŒ๋“ค๊ณ  MemberServiceImpl๋กœ ๊ตฌํ˜„ํ•œ๋‹ค.
  • [ํšŒ์› ์ €์žฅ์†Œ]๋Š” MemberRepository ์ธํ„ฐํŽ˜์ด์Šค(์—ญํ• )์„ ๋งŒ๋“ค๊ณ  ๊ฐ ์ €์žฅ์†Œ๋ณ„ ๊ตฌํ˜„์ฒด๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค. 

์ž๋ฐ” ํด๋ž˜์Šค์˜ ๊ด€๊ณ„๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ทธ๋ฆผ

 


๋„๋ฉ”์ธ๊ณผ ํด๋ž˜์Šค ๋‹ค์ด์–ด๊ทธ๋žจ์ด ์™„์„ฑ๋˜์—ˆ๋‹ค๋ฉด, ์‹ค์ œ ์„œ๋ฒ„์—์„œ ๊ฐ์ฒด๊ฐ„ ์ฐธ์กฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋˜๋Š”์ง€๋ฅผ ๊ทธ๋ฆฐ๋‹ค. ์ด๋ฅผ ๊ฐ์ฒด ๋‹ค์ด์–ด๊ทธ๋žจ์ด๋ผ ํ•˜๋ฉฐ ์‹ค์ œ ์„œ๋น„์Šค๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ ๋˜๋Š”์ง€ ํ•œ๋ˆˆ์— ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

  • ํด๋ผ์ด์–ธํŠธ๋Š” ํšŒ์›์„œ๋น„์Šค๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ํšŒ์› ์„œ๋น„์Šค๋Š” ํšŒ์› ์ €์žฅ์†Œ๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๋งŒ๋“ค์–ด์ง„ ๊ฐ์ฒด๋“ค์ด ์‹ค์ œ ์„œ๋น„์Šค์—์„œ ๋™์ž‘ํ•˜๋Š” ๊ทธ๋ฆผ

์ด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ง์ ‘ ์ž‘์„ฑํ•œ ์ž๋ฐ”์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

๋”๋ณด๊ธฐ
  • ๋„๋ฉ”์ธ (Member, Grade)
public enum Grade {
    BASIC,
    VIP
}
public class Member {
    private Long id;
    private String name;
    private Grade grade;

    public Member(Long id, String name, Grade grade) {
        this.id = id;
        this.name = name;
        this.grade = grade;
    }

    // ... getter setter ์ƒ๋žต ...
}

 

  • ์„œ๋น„์Šค (MemberService, MemberServiceImpl)
public interface MemberService {
    void join(Member member);

    Member findMember(Long memberId);
}
public class MemberServiceImpl implements MemberService {
    public final MemberRepository memberRepository = new MemoryMemberRepository();

    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }

    @Override
    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}

 

  • ์ €์žฅ์†Œ (MemberRepository, MemoryMemberRepository)
public interface MemberRepository {
    void save(Member member);

    Member findById(Long memberId);
}
public class MemoryMemberRepository implements MemberRepository {
    public static Map<Long, Member> store = new HashMap<>();

    @Override
    public void save(Member member) {
        store.put(member.getId(), member);
    }

    @Override
    public Member findById(Long memberId) {
        return store.get(memberId);
    }
}

 


# ํšŒ์› ๋„๋ฉ”์ธ ์‹คํ–‰๊ณผ ํ…Œ์ŠคํŠธ

์ž‘์„ฑํ•œ ์ฝ”๋“œ๋“ค์ด ์ž˜ ์ž‘๋™ํ•˜๋Š”์ง€ ์„œ๋น„์Šค ๊ณ„์ธต๋ถ€ํ„ฐ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์„œ ํ™•์ธํ•ด๋ณด์ž.

import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;

// ์•ฑ ์ง„์ž…์ 
public class MemberApp {
    public static void main(String[] args) {
        MemberService memberService = new MemberServiceImpl();
        Member member = new Member(1L, "memberA", Grade.VIP);
        memberService.join(member);

        Member findMember = memberService.findMember(1L);
        System.out.println("new member = " + member.getName());
        System.out.println("findMember = " + findMember.getName());
    }
}
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

// JUnit์„ ์ด์šฉํ•œ ํ…Œ์ŠคํŠธ
class MemberServiceTest {
    MemberService memberService = new MemberServiceImpl();

    @Test
    void join() {
        //given
        Member member = new Member(1L, "memberA", Grade.VIP);

        //when
        memberService.join(member);
        Member findMember = memberService.findMember(1L);

        //then
        Assertions.assertThat(member).isEqualTo(findMember);
    }
}

 


# ํšŒ์› ๋„๋ฉ”์ธ ํ‰๊ฐ€ํ•˜๊ธฐ

ํšŒ์› ๋„๋ฉ”์ธ์˜ ์„ค๊ณ„์™€ ํ…Œ์ŠคํŠธ๋ฅผ ์„ฑ๊ณตํ–ˆ๋‹ค. ์ž ๊ทธ๋ ‡๋‹ค๋ฉด ํ•ด๋‹น ์ฝ”๋“œ๋Š” ๊ฐ์ฒด์ง€ํ–ฅ์—์„œ ๋งํ•˜๋Š” SOLID ์›์น™์˜ OCP, DIP๋ฅผ ๋งŒ์กฑํ•˜๊ณ  ์žˆ๋Š”๊ฐ€? ๋‹คํ˜•์„ฑ์ด ์ž˜ ์„ค๊ณ„๋œ ์ฝ”๋“œ์ผ๊นŒ?

  • ํšŒ์› ์ €์žฅ์†Œ๋กœ MemberRepository๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋‚˜์ค‘์— ์ €์žฅ์†Œ๋ฅผ ๋ณ€๊ฒฝํ•  ๋•Œ OCP ์›์น™์ด ์ง€์ผœ์งˆ ์ˆ˜ ์žˆ๋Š”๊ฐ€?
  • DIP๊ฐ€ ์ž˜ ์ง€์ผœ์ง€๊ณ  ์žˆ๋Š”๊ฐ€?

์•„๋‹ˆ๋‹ค. ์˜์กด๊ด€๊ณ„๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค์™€ ๊ตฌํ˜„์ฒด์— ๋ชจ๋‘ ์žˆ๋‹ค. ์ผ๋‹จ์€ ๋„˜์–ด๊ฐ€๋„๋ก ํ•˜๊ณ , ์ฃผ๋ฌธ๊ณผ ํ• ์ธ์„ ์„ค๊ณ„ํ•ด๋ณด์ž.

์ €์žฅ์†Œ ๊ตฌํ˜„์ฒด์˜ ๋ณ€๊ฒฝ์ด ํด๋ผ์ด์–ธํŠธ(MemberService) ์ฝ”๋“œ์— ์˜ํ–ฅ์„ ๋ผ์นœ๋‹ค.

 


# ์ฃผ๋ฌธ๊ณผ ํ• ์ธ

# ์ฃผ๋ฌธ๊ณผ ํ• ์ธ ๋„๋ฉ”์ธ ์„ค๊ณ„

  • ํšŒ์›์€ ์ƒํ’ˆ์„ ์ฃผ๋ฌธํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํ• ์ธ ์ •์ฑ…์€ ๋ชจ๋“  VIP๋Š” 1000์›์„ ํ• ์ธํ•ด์ฃผ๋Š” ๊ณ ์ • ๊ธˆ์•ก ํ• ์ธ์„ ์ ์šฉํ•ด๋‹ฌ๋ผ.(๋ณ€๊ฒฝ๊ฐ€๋Šฅ)
  • ํ• ์ธ ์ •์ฑ…์€ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค. ํšŒ์‚ฌ์˜ ๊ธฐ๋ณธ ํ• ์ธ ์ •์ฑ…์„ ์•„์ง ์ •ํ•˜์ง€ ๋ชปํ–ˆ๋‹ค.
    ์˜คํ”ˆ ์ง์ „๊นŒ์ง€ ๊ณ ๋ฏผ์„ ๋ฏธ๋ฃจ๊ณ  ์‹ถ๋‹ค. ์ตœ์•…์˜ ๊ฒฝ์šฐ ํ• ์ธ์„ ์ ์šฉํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค. (๋ฏธํ™•์ •)

์ฃผ๋ฌธ๊ณผ ํ• ์ธ์— ๋Œ€ํ•œ ์ •์ฑ…๊ณผ ์š”๊ตฌ์‚ฌํ•ญ์„ ๋ถ„์„ํ•ด๋ณด์ž. ํšŒ์›์€ ์ฃผ๋ฌธ์„ ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ํšŒ์›์˜ ๋“ฑ๊ธ‰์— ๋”ฐ๋ผ ์ƒํ’ˆ์— ํ• ์ธ์ด ์ ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ์ •๋ฆฌํ•˜๋ฉด ์•„๋ž˜ 4๊ฐ€์ง€๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

  • ์ฃผ๋ฌธ ์ƒ์„ฑ: ํด๋ผ์ด์–ธํŠธ๋Š” ์ฃผ๋ฌธ ์„œ๋น„์Šค์— ์ฃผ๋ฌธ ์ƒ์„ฑ์„ ์š”์ฒญํ•œ๋‹ค.
  • ํšŒ์› ์กฐํšŒ: ํ• ์ธ์„ ์œ„ํ•ด์„œ ํšŒ์› ๋“ฑ๊ธ‰์„ ์•Œ์•„์•ผํ•˜๊ธฐ์— ํšŒ์›์„ ์กฐํšŒํ•œ๋‹ค.
  • ํ• ์ธ ์ ์šฉ: ํšŒ์› ๋“ฑ๊ธ‰์— ๋”ฐ๋ฅธ ํ• ์ธ์—ฌ๋ถ€์™€ ํ• ์ธ ๊ธˆ์•ก์„ ์•Œ๊ธฐ ์œ„ํ•ด ํ•ด๋‹น ์ฑ…์ž„์„ ํ• ์ธ ์ •์ฑ… ์—ญํ• ์— ์œ„์ž„ํ•œ๋‹ค.
  • ์ฃผ๋ฌธ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜: ์ฃผ๋ฌธ ์„œ๋น„์Šค๋Š” ํ• ์ธ ๊ฒฐ๊ณผ๋ฅผ ํฌํ•จํ•œ ์ฃผ๋ฌธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

* ์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” DB์— ์ €์žฅํ•˜๊ฒ ์ง€๋งŒ, ์˜ˆ์ œ ์ฝ”๋“œ์—์„œ๋Š” ์ƒ๋žตํ•˜์˜€๋‹ค.

๋งŒ๋“ค์–ด๋…ผ ์„ค๊ณ„๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํด๋ž˜์Šค์˜ ๊ตฌ์„ฑ๋„๋ฅผ ๊ทธ๋ ค๋ณด์ž. ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์—ฐ๊ฒฐํ•˜๋Š”๊ฒŒ ์•„๋‹Œ ์—ญํ• (์ธํ„ฐํŽ˜์ด์Šค)์™€ ๊ตฌํ˜„(๊ฐ์ฒด)๋ฅผ ๋ถ„๋ฆฌํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ "์—ญํ• ๊ณผ ๊ตฌํ˜„์„ ๋ถ„๋ฆฌ"ํ•ด์„œ ์„ค๊ณ„ํ•˜์—ฌ์•ผ [ํšŒ์› ์ €์žฅ์†Œ]๋‚˜ [ํ• ์ธ ์ •์ฑ…]์„ ์œ ์—ฐํ•˜๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค. 

๋„๋ฉ”์ธ๊ณผ ํด๋ž˜์Šค๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๊ฐ์ฒด๊ฐ„์˜ ์ฐธ์กฐ ๊ด€๊ณ„๋ฅผ ๊ทธ๋ฆฐ๋‹ค.

์ €์žฅ์†Œ๋ฅผ ๋ฐ”๊พธ๊ฑฐ๋‚˜ ํ• ์ธ ์ •์ฑ…์„ ๋ฐ”๊พธ๋”๋ผ๋„ '์ฃผ๋ฌธ ์„œ๋น„์Šค'๋ฅผ ๋ณ€๊ฒฝํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ์ฆ‰ ์—ญํ• ๋“ค์˜ ํ˜‘๋ ฅ๊ด€๊ณ„๋ฅผ ๊ทธ๋Œ€๋กœ ์žฌ์‚ฌ์šฉ ํ•  ์ˆ˜์žˆ๋‹ค.

์ฃผ๋ฌธ๊ณผ ํ• ์ธ์ •์ฑ…์— ๊ด€๋ จํ•˜์—ฌ ๋„๋ฉ”์ธ๊ณผ ์—ญํ• (์ธํ„ฐํŽ˜์ด์Šค)์„ ์ฐจ๋ก€๋Œ€๋กœ ์ƒ์„ฑํ•ด๋ณด์ž. ์‹ค์ œ ์„ค๊ณ„์—์„œ๋Š” '์ƒํ’ˆ'๋„ ๋„๋ฉ”์ธ์œผ๋กœ ๋งŒ๋“ค์–ด ๊ฐ์ฒด ์„ค๊ณ„๋ฅผ ํ•ด์•ผํ•˜์ง€๋งŒ ์ด๋ฒˆ ์˜ˆ์ œ์—์„œ๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ ์ƒํ’ˆ์€ ์ด๋ฆ„๊ณผ ๊ฐ€๊ฒฉ๋งŒ ๋ฐ์ดํ„ฐ๋กœ ๋ฐ›๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค.

๋”๋ณด๊ธฐ

์ฃผ๋ฌธ๋„๋ฉ”์ธ (Order)

package hello.core.discount;

public class Order {
    private Long memberId;
    private String itemName;
    private int itemPrice;
    private int discountPrice;

    public Order(Long memberId, String itemName, int itemPrice, int discountPrice) {
        this.memberId = memberId;
        this.itemName = itemName;
        this.itemPrice = itemPrice;
        this.discountPrice = discountPrice;
    }
    
     public int calculatePrice() {
        return itemPrice - discountPrice;
    }

    public Long getMemberId() {
        return memberId;
    }

    public void setMemberId(Long memberId) {
        this.memberId = memberId;
    }

    public String getItemName() {
        return itemName;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }

    public int getItemPrice() {
        return itemPrice;
    }

    public void setItemPrice(int itemPrice) {
        this.itemPrice = itemPrice;
    }

    public int getDiscountPrice() {
        return discountPrice;
    }

    public void setDiscountPrice(int discountPrice) {
        this.discountPrice = discountPrice;
    }

    @Override
    public String toString() {
        return "Order{" +
                "memberId=" + memberId +
                ", itemName='" + itemName + '\'' +
                ", itemPrice=" + itemPrice +
                ", discountPrice=" + discountPrice +
                '}';
    }
}

 ์ฃผ๋ฌธ ์„œ๋น„์Šค ์—ญํ• , ์ธํ„ฐํŽ˜์ด์Šค(OrderService)

public interface OrderService {
    Order createOrder(Long memberId, String itemName, int itemPrice);
}

ํ• ์ธ ์ •์ฑ… ์„œ๋น„์Šค ์—ญํ•  (DiscountPolicy)

public interface DiscountPolicy {
    /**
     * @return ํ• ์ธ ๋Œ€์ƒ ๊ธˆ์•ก
     */
    int discount(Member member, int price);
}

 

์ธํ„ฐํŽ˜์ด์Šค ์„ค๊ณ„๋ฅผ ์™„๋ฃŒํ–ˆ๋‹ค๋ฉด, ๋ฐ”๋กœ ์•„๋ž˜์˜ ๊ตฌํ˜„์ฒด๋ฅผ ๋งŒ๋“ค์–ด๋„ ๋˜์ง€๋งŒ, ์ž ๊น ๋„˜์–ด๊ฐ€์„œ ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋ถ€ํ„ฐ ์ž‘์„ฑํ•ด๋ณด์ž. ์ด๋ ‡๊ฒŒ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•˜๋ฉด ์–ด๋–ค ์ด์ ์ด ์žˆ์„๊นŒ?

//DiscountPolicy ๊ตฌํ˜„์ฒด
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;
        }
        return 0;
    }
}
//OrderSevice ๊ตฌํ˜„์ฒด
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 discount = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discount);
    }
}

# ์ฃผ๋ฌธ๊ณผ ํ• ์ธ ๋„๋ฉ”์ธ ๊ตฌํ˜„๊ณผ ํ…Œ์ŠคํŠธ

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์‹คํ–‰์˜ˆ์‹œ

@DisplayName("OderService ํด๋ž˜์Šค์˜")
class OrderServiceTest {
    private Member basicMember;
    private Member vipMember;

    private OrderService orderService;
    private MemberService memberService;

    @BeforeEach
    void setup() {
        orderService = new OrderServiceImpl();
        memberService = new MemberServiceImpl();

        basicMember = new Member(1L, "BASIC", Grade.BASIC);
        vipMember = new Member(2L, "VIP", Grade.VIP);

        memberService.join(basicMember);
        memberService.join(vipMember);
    }

    @Nested
    @DisplayName("createOrder ๋ฉ”์†Œ๋“œ๋Š”")
    class Describe_createOrder {
        @Nested
        @DisplayName("์ผ๋ฐ˜ ๋“ฑ๊ธ‰์˜ ํšŒ์›์ด 10000์›์งœ๋ฆฌ ๋ฌผ๊ฑด์˜ ์ฃผ๋ฌธ์„ ์ƒ์„ฑํ•  ๊ฒฝ์šฐ ๊ฐ€๊ฒฉ์€")
        class Context_with_create_order_from_basic_member {
            @DisplayName("์ œํ’ˆ์˜ ๋ฌผ๊ฑด ๊ฐ€๊ฒฉ ๊ทธ๋Œ€๋กœ ์ƒ์„ฑ๋œ๋‹ค.")
            @Test
            void it_is_original_price() {
                //when
                Order order = orderService.createOrder(basicMember.getId(), "item", 10000);

                //then
                assertThat(order.calculatePrice()).isEqualTo(10000);
            }
        }

        @Nested
        @DisplayName("VIP๋“ฑ๊ธ‰์˜ ํšŒ์›์ด 10000์›์งœ๋ฆฌ ๋ฌผ๊ฑด์˜ ์ฃผ๋ฌธ์„ ์ƒ์„ฑํ•  ๊ฒฝ์šฐ ๊ฐ€๊ฒฉ์€")
        class Context_with_create_order_from_vip_member {
            @DisplayName("์ œํ’ˆ์˜ ๋ฌผ๊ฑด ๊ฐ€๊ฒฉ ๊ทธ๋Œ€๋กœ ์ƒ์„ฑ๋œ๋‹ค.")
            @Test
            void it_is_discounted_price() {
                //when
                Order order = orderService.createOrder(vipMember.getId(), "item", 10000);

                //then
                assertThat(order.calculatePrice()).isEqualTo(9000);
            }
        }
    }
}

 


# ์ด ์ฝ”๋“œ๋Š” OCP์™€ DIP๋ฅผ ์ง€ํ‚ค๊ณ  ์žˆ๋Š” ์„ค๊ณ„์ผ๊นŒ?

์•„๋ž˜์™€ ๊ฐ™์ด ์ƒˆ๋กœ์šด ํ• ์ธ ์ •์ฑ…์„ ๋งŒ๋“ ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

์•…๋• ๊ธฐํš์ž: ์„œ๋น„์Šค ์˜คํ”ˆ ์ง์ „์— ํ• ์ธ ์ •์ฑ…์„ ์ง€๊ธˆ์ฒ˜๋Ÿผ ๊ณ ์ • ๊ธˆ์•ก ํ• ์ธ์ด ์•„๋‹ˆ๋ผ ์ข€ ๋” ํ•ฉ๋ฆฌ์ ์ธ ์ฃผ๋ฌธ ๊ธˆ์•ก ๋‹น ํ• ์ธํ•˜๋Š” ์ •๋ฅ % ํ• ์ธ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์–ด์š”.

์˜ˆ๋ฅผ ๋“ค์–ด์„œ ๊ธฐ์กด ์ •์ฑ…์€ VIP๊ฐ€ 10000์›์„ ์ฃผ๋ฌธํ•˜๋“  20000์›์„ ์ฃผ๋ฌธํ•˜๋“  ํ•ญ์ƒ 1000์›์„ ํ• ์ธํ–ˆ๋Š”๋ฐ, ์ด๋ฒˆ์— ์ƒˆ๋กœ ๋‚˜์˜จ ์ •์ฑ…์€ 10%๋กœ ์ง€์ •ํ•ด๋‘๋ฉด ๊ณ ๊ฐ์ด 10000์› ์ฃผ๋ฌธ์‹œ 1000์›์„ ํ• ์ธํ•ด์ฃผ๊ณ , 20000์› ์ฃผ๋ฌธ์‹œ์— 2000์›์„ ํ• ์ธํ•ด์ฃผ๋Š” ๊ฑฐ์—์š”!

์ˆœ์ง„ ๊ฐœ๋ฐœ์ž: ์ œ๊ฐ€ ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ณ ์ • ๊ธˆ์•ก ํ• ์ธ์€ ์•„๋‹ˆ๋ผ๊ณ  ํ–ˆ์ž–์•„์š”. (์ข‹์ง€์•Š์€ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜์˜ ์˜ˆ1)

์•…๋• ๊ธฐํš์ž: ์• ์ž์ผ ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ ์„ ์–ธ ๋ชฐ๋ผ์š”? “๊ณ„ํš์„ ๋”ฐ๋ฅด๊ธฐ๋ณด๋‹ค ๋ณ€ํ™”์— ๋Œ€์‘ํ•˜๊ธฐ๋ฅผ”

์ˆœ์ง„ ๊ฐœ๋ฐœ์ž: … 

์• ์ž์ผ ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ์ด๋ž€?

 

์œ„์—์„œ ๊ตฌํ˜„๊ณผ ์—ญํ• ์„ ๊ตฌ๋ถ„ํ•ด๋†จ๊ธฐ์— ๋‹จ์ˆœํžˆ ์ƒ๊ฐํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ƒˆ๋กœ์šด ํ• ์ธ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ , ํ…Œ์ŠคํŠธ ํ•ด๋ณธ ๋’ค ์ ์šฉ์‹œ์ผœ์ฃผ๋ฉด ๋œ๋‹ค. ๊ธฐ์กด์˜ ๊ฐœ๋ฐœ๋ฐฉ์‹๋ณด๋‹ค๋Š” ํ›จ์”ฌ ๊ฐ„๋‹จํ•˜๊ธด ํ•˜๋‹ค. ํ•œ์ค„๋งŒ ๋ฐ”๊พธ๋ฉด ๋˜๋‹ˆ๊นŒ

public class OrderServiceImpl implements OrderService {
    
    private final MemberRepository memberRepository = new
            MemoryMemberRepository();

	// ํ• ์ธ์ •์ฑ… ๊ตฌํ˜„๋ถ€ ๋ณ€๊ฒฝ!
//  private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
    private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
    
    @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);
    }
}

@ ์ˆจ๊ฒจ์ง„ ๋ฌธ์ œ์ 

  • ์šฐ๋ฆฌ๋Š” ์—ญํ• ๊ณผ ๊ตฌํ˜„์„ ์ถฉ์‹คํ•˜๊ฒŒ ๋ถ„๋ฆฌํ–ˆ๋‹ค. OK
  • ๋‹คํ˜•์„ฑ๋„ ํ™œ์šฉํ•˜๊ณ , ์ธํ„ฐํŽ˜์ด์Šค์™€ ๊ตฌํ˜„ ๊ฐ์ฒด๋ฅผ ๋ถ„๋ฆฌํ–ˆ๋‹ค. OK

  • OCP, DIP ๊ฐ™์€ ๊ฐ์ฒด์ง€ํ–ฅ ์„ค๊ณ„ ์›์น™์„ ์ถฉ์‹คํžˆ ์ค€์ˆ˜ํ–ˆ๋‹ค..?
    -> ๊ทธ๋ ‡๊ฒŒ ๋ณด์ด์ง€๋งŒ ์‚ฌ์‹ค์€ ์•„๋‹ˆ๋‹ค.

  • DIP: ์˜์กด์„ฑ. ์ฃผ๋ฌธ ํด๋ผ๋Š”( OrderServiceImpl )๋Š” DiscountPolicy ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์˜์กดํ•˜๋‹ˆ ์ง€ํ‚จ๊ฑฐ ์•„๋‹Œ๊ฐ€?
    - ํด๋ž˜์Šค๋ฅผ ๋‹ค์‹œ๋ณด์ž. ์ถ”์ƒ(์ธํ„ฐํŽ˜์ด์Šค) ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ตฌ์ฒด(๊ตฌํ˜„) ํด๋ž˜์Šค์—๋„ ์˜์กดํ•˜๊ณ  ์žˆ๋‹ค.

    -> ์ถ”์ƒ(์ธํ„ฐํŽ˜์ด์Šค) ์˜์กด: DiscountPolicy
    -> ๊ตฌ์ฒด(๊ตฌํ˜„) ํด๋ž˜์Šค: FixDiscountPolicy , RateDiscountPolicy

  • OCP: ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ํ™•์žฅํ•  ์ˆ˜ ์žˆ๊ฒŒ ์„ค๊ณ„ํ•ด์•ผ ํ•œ๋‹ค.
    ์ง€๊ธˆ ์ฝ”๋“œ๋Š” ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•ด์„œ ๋ณ€๊ฒฝํ•˜๋ฉด, ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ์— ์˜ํ–ฅ์„ ์ค€๋‹ค! ๋”ฐ๋ผ์„œ OCP๋ฅผ ์œ„๋ฐ˜ํ•œ๋‹ค.

๊ฒฐ๊ตญ ๊ตฌํ˜„์ฒด ์ฝ”๋“œ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ๋‹ค๋ฅธ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผํ•จ์€ ๋ณ€ํ•จ์—†๋‹ค. ์กฐ๊ธˆ ๋” ํŽธํ•ด์กŒ์„ ๋ฟ, ์—ญํ• ๊ณผ ๊ตฌํ˜„์ด ๋ถ„๋ฆฌ๋˜์ง€ ์•Š์œผ๋ฉด OCP์™€ DIP๊ฐ™์€ ๊ฐ์ฒด์ง€ํ–ฅ ์›์น™์„ ์ง€ํ‚ค์ง€ ๋ชปํ•œ๋‹ค๋Š” ๋ง์ด๋‹ค.


@ ์•„๋‹ˆ ๊ทธ๋Ÿฌ๋ฉด ์–ด๋–ป๊ฒŒ OCP, DIP๋ฅผ ๊ตฌํ˜„ํ•ด์š”?

์ •๋ง ๊ฐ„๋‹จํ•˜๋‹ค. ์ธํ„ฐํŽ˜์ด์Šค(์—ญํ• )์ด ์•„๋‹Œ ๊ตฌํ˜„๋ถ€๋ถ„์„ ์ฝ”๋“œ์—์„œ ์ œ๊ฑฐํ•˜๋ฉด ๋œ๋‹ค. ๊ทธ๋Ÿผ DIP๋ฅผ ์‰ฝ๊ฒŒ ์ง€ํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

//private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
private final DiscountPolicy discountPolicy;

??????????? ๊ทธ๋Ÿผ ๊ตฌํ˜„์ฒด๊ฐ€ ์—†๋Š”๋ฐ ์–ด๋–ป๊ฒŒ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œ์ผœ์š”? null์„ ์‹คํ–‰์‹œ์ผœ์š”?


@ ํ•ด๊ฒฐ๋ฐฉ์•ˆ

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ๋ˆ„๊ตฐ๊ฐ€ ํด๋ผ์ด์–ธํŠธ์ธ 'OrderServiceImpl'์— 'DiscountPolicy' ์˜ ๊ตฌํ˜„ ๊ฐ์ฒด๋ฅผ ๋Œ€์‹  ์ƒ์„ฑํ•˜๊ณ , ์ฃผ์ž…ํ•ด์ฃผ๋ฉด ๋œ๋‹ค. ์ด๊ฒƒ์ด ๋ฐ”๋กœ ๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ์ด๋‹ค.

 

๊ธ€์ด ๊ธธ์–ด์ ธ, ๋‹ค์Œ ๊ธ€์— ๊ณ„์† ์ด์–ด์„œ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ