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

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