ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

๐Ÿšช ๋“ค์–ด๊ฐ€๊ธฐ์— ์•ž์„œ... 

๋จผ์ €, ์ด ํฌ์ŠคํŠธ์—์„œ๋Š” ๋‚ด๊ฐ€ ์–ผ๋งˆ๋‚˜ ๋””์ž์ธ ํŒจํ„ด์— ๋Œ€ํ•ด์„œ ์—ด์‹ฌํžˆ ๊ณต๋ถ€ํ–ˆ๋Š”์ง€, ์–ผ๋งˆ๋‚˜ ๋งŽ์€ ๋””์ž์ธ ํŒจํ„ด์„ ์•„๋Š”์ง€ ๋ณด๋‹ค ๋””์ž์ธ ํŒจํ„ด์œผ๋กœ ์–ด๋–ค ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ ํ–ˆ๋Š”์ง€์— ํฌ์ปค์Šค๋ฅผ ๋งž์ถฐ ๋ณด๊ณ  ์‹ถ๋‹ค.

์™œ๋ƒํ•˜๋ฉด ๋””์ž์ธ ํŒจํ„ด์„ ์ž˜ ์•Œ๊ณ  ํ•™์Šตํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜์ง€๋งŒ, ๋””์ž์ธ ํŒจํ„ด์œผ๋กœ ์–ด๋–ค ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ ํ–ˆ๋Š”์ง€๊ฐ€ ๋” ์ค‘์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค 

 

๋””์ž์ธ ํŒจํ„ด ๊ฐ๊ฐ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์„ค๋ช…์€ ๋…ธ์…˜์— ์ƒ์„ธํžˆ ๊ธฐ๋ก์„ ํ•ด ๋†“์•˜๋‹ค..! ๐Ÿ˜Š

 

๋””์ž์ธ ํŒจํ„ด์— ๋Œ€ํ•ด์„œ ํ•™์Šตํ•˜๊ณ  ์ •๋ฆฌํ•œ ๋…ธ์…˜ ๋งํฌ

 

23๊ฐ€์ง€ ๋””์ž์ธ ํŒจํ„ด์— ๋Œ€ํ•ด์„œ | Notion

๋””์ž์ธ ํŒจํ„ด์ด๋ž€

kaput-trombone-343.notion.site

1. ๋””์ž์ธ ํŒจํ„ด ์ด๋ž€

๋””์ž์ธ ํŒจํ„ด์ด๋ž€ ์†Œํ”„ํŠธ์›จ์–ด ๋””์ž์ธ ๊ณผ์ •์—์„œ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋“ค์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์„์„ ๋งํ•œ๋‹ค.

SOLID ์›์น™์— ๋”ฐ๋ผ ๊ฐ์ฒด์ง€ํ–ฅ์ ์œผ๋กœ ํ”„๋กœ๊ทธ๋ž˜๋ฐํ•˜๋Š” ๋ฐ ์žˆ์–ด์„œ ์—ฌ๋Ÿฌ ์ƒํ™ฉ์— ๋Œ€ํ•œ ๊ณตํ†ต์ ๋“ค์„ ๋ชจ์•„ ๋†“์€๊ฒƒ์ด ๋””์ž์ธ ํŒจํ„ด์ด๋ผ๊ณ  ํ•œ๋‹ค.

 

๋””์ž์ธ ํŒจํ„ด์„ ์ฒ˜์Œ์— ๋‚˜๋„ ์ ‘ํ–ˆ์„ ๋•Œ๋Š”, ์ด๋Ÿฐ ํŒจํ„ด์ด ์ด์ „๋ถ€ํ„ฐ ์กด์žฌํ–ˆ๊ตฌ๋‚˜?! ๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

ํ•˜์ง€๋งŒ ํ•™์Šต์„ ํ•˜๊ณ  ๋ณด๋‹ˆ, ๊ณ ๋Œ€ ๋งŽ์€ ๊ฐœ๋ฐœ์ž ๋ถ„๋“ค์ด ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ์„ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉฐ ๋งˆ์ฃผ์นœ ๋ฌธ์ œ๋“ค์— ๋Œ€ํ•œ ํ•ด๊ฒฐ ์ฑ… ์ค‘ ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์ธ์ •ํ•œ Best Practice๋ฅผ ์ •๋ฆฌํ•œ ๊ฒƒ์œผ๋กœ, 1994๋…„ GoF(Gang of Four)๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” Erich Gamma, Richard Helm, Ralph Johnson, John Vissides๊ฐ€ ์ง‘ํ•„ํ•œ GoF ๋””์ž์ธ ํŒจํ„ด  ์ฑ…์„ ํ†ตํ•ด ์ •๋ฆฌ๋˜๊ณ  ๋ฐœ์ „๋˜์—ˆ๋‹ค๊ณ  ํ•œ๋‹ค.

 

๋””์ž์ธ ํŒจํ„ด์€ ๊ฐ์ฒด์ง€ํ–ฅ์˜ ํŠน์„ฑ๊ณผ ๊ฐ์ฒด์ง€ํ–ฅ ์„ค๊ณ„ ์›์น™(SOLID)๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„๋œ ์„ค๊ณ„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์ฃผ๋Š” ํŒจํ„ด์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•™์Šต์ด ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๋Š๊ผˆ๋‹ค..!

 

2. ๋‚˜๋Š” ๋””์ž์ธ ํŒจํ„ด์œผ๋กœ ์–ด๋–ค ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ ํ–ˆ๋Š”๊ฐ€?

โœจ 1๏ธโƒฃ ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ํŒจํ„ด(Factory Method Pattern) ์ ์šฉ ์‚ฌ๋ก€

๐Ÿ“ข ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ํŒจํ„ด์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ์„ค๋ช…

ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ํŒจํ„ด์ด๋ž€ ๋ถ€๋ชจ ํด๋ž˜์Šค์—์„œ ๊ฐ์ฒด๋“ค์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•˜๊ณ , ์ž์‹ ํด๋ž˜์Šค๋“ค์ด ์ƒ์„ฑ๋  ๊ฐ์ฒด๋“ค์˜ ์œ ํ˜•์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ์ƒ์„ฑํŒจํ„ด์„ ๋งํ•œ๋‹ค.

์ถœ์ฒ˜: https://refactoring.guru/ko/design-patterns/factory-method

์œ„ ๊ตฌ์กฐ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ, Creator๊ฐ€ ์ตœ์ƒ์œ„ ํŒฉํ† ๋ฆฌ ํด๋ž˜์Šค๋กœ ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”์ƒํ™”ํ•ด ConcreteCreatorA, ConcreteCreatorB๊ณผ ๊ฐ™์€ ์„œ๋ธŒ ํด๋ž˜์Šค๊ฐ€ Product๋ฅผ ๊ตฌํ˜„ํ•˜๋„๋ก ํ•ด ๊ฐ์ฒด๊ฐ„์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ  ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•ด์ค€๋‹ค.

 

๐Ÿ“ ์–ธ์ œ ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ํŒจํ„ด์„ ์‚ฌ์šฉํ–ˆ๋Š”๊ฐ€?

๋ฐ๋ธŒ์ฝ”์Šค ์ตœ์ข…ํ”„๋กœ์ ํŠธ Space Club ํ”„๋กœ์ ํŠธ์—์„œ ์•Œ๋ฆผ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ ํ•˜๊ณ  ์žˆ์„ ๋•Œ๋‹ค.

๋จผ์ €, ์•Œ๋ฆผ ๊ธฐ๋Šฅ ๊ตฌํ˜„์€ ์˜์กด์„ฑ ์‚ฌ์ดํด์„ ์ค„์ด๊ธฐ ์œ„ํ•ด์„œ ์ด๋ฒคํŠธ publish/listen ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌ ํ–ˆ๋‹ค.

๋”ฐ๋ผ์„œ notification ํŒจํ‚ค์ง€๊ฐ€ ์•„๋‹Œ ์™ธ๋ถ€์—์„œ ๋ฉ”์ผ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด,  ApplicationEventPublisher ๊ฐ์ฒด์˜ publishEvent() ๋ฉ”์„œ๋“œ์˜ ์ธ์ž๋กœ MailEvent ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์„œ ๋„˜๊ฒจ ์ฃผ๋ฉด ๋˜์—ˆ๋‹ค.

 

๊ทธ๋Ÿฐ๋ฐ MailEvent ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด ๋ฉ”์ผ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š”๋ฐ, ์•Œ๋ฆผ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ๋Š” ๋‘ ๊ฒฝ์šฐ ์˜€๋‹ค.

1. ํšŒ์›๊ฐ€์ž…์ด ์™„๋ฃŒ ๋˜์—ˆ์„ ๊ฒฝ์šฐ ์›ฐ์ปด ๋ฉ”์ผ

2. ์‚ฌ์šฉ์ž๊ฐ€ ํ–‰์‚ฌ ์‹ ์ฒญ์„ ํ•˜๊ณ  ๊ด€๋ฆฌ์ž๊ฐ€ ์Šน์ธ or ํ–‰์‚ฌ ์ทจ์†Œ ํ–ˆ์„ ๋•Œ ๊ด€๋ฆฌ์ž๊ฐ€ ์ทจ์†Œํ™•์ธ ํ•œ ๊ฒฝ์šฐ ํ–‰์‚ฌ ์‹ ์ฒญ ์ƒํƒœ ๋ณ€๊ฒฝ ๋ฉ”์ผ

 

๊ทธ๋Ÿฐ๋ฐ ์ด ๊ฐ๊ฐ์˜ ์ผ€์ด์Šค๋ฅผ ๊ฐ๊ฐ์˜ ์ด๋ฒคํŠธ๋กœ ๋งŒ๋“ ๋‹ค๋ฉด?

 

๊ทธ๋Ÿฌ๋ฉด ๋ฉ”์ผ ๋ฐœ์†ก์„ ๋‹ด๋‹นํ•˜๋Š” MailService์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ฐ ์ด๋ฒคํŠธ ๋งˆ๋‹ค ๋ฉ”์ผ ๋ฐœ์†ก ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

์ง€๊ธˆ ๋‹น์žฅ ํ•„์š”ํ•œ ๋ฉ”์ผ ๊ธฐ๋Šฅ์€ ํšŒ์›๊ฐ€์ž…๊ณผ ํ–‰์‚ฌ ์‹ ์ฒญ ์ƒํƒœ ๋ณ€๊ฒฝ, ๋‘ ๊ฐ€์ง€ ๊ฒฝ์šฐ์ด์ง€๋งŒ ํ™•์žฅ์„ฑ์— ์žˆ์–ด์„œ๋Š” ์ข‹์ง€ ๋ชปํ•˜๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.

// ํ™•์žฅ์„ฑ์— ์žˆ์–ด ์ข‹์ง€ ๋ชปํ•œ ์ด์œ 
@Service
@RequiredArgsConstructor
public class MailService {

    private final JavaMailSender emailSender;
    private final SpringTemplateEngine templateEngine;
    
    @Async
    @Transactional(propagation = REQUIRES_NEW)
    @TransactionalEventListener
    public void sendEmail(MailEvent1 mailEvent1) {
    // MailEvent1์— ๋Œ€ํ•œ ๋ฉ”์ผ ์ „์†ก
    }
    
    @Async
    @Transactional(propagation = REQUIRES_NEW)
    @TransactionalEventListener
    public void sendEmail(MailEvent2 mailEvent1) {
    // MailEvent2์— ๋Œ€ํ•œ ๋ฉ”์ผ ์ „์†ก
    }
    ...
    // ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ํ•˜๋Š” sendEmail() ๋ฉ”์„œ๋“œ ๊ฐœ์ˆ˜ == ๋ฐœํ–‰ํ•˜๋Š” ์ด๋ฒคํŠธ ์ข…๋ฅ˜ ๊ฐœ์ˆ˜

 

๋”ฐ๋ผ์„œ ๋ฉ”์ผ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์ด๋ฒคํŠธ๋Š” ๋™์ผํ•œ ์ด๋ฒคํŠธ MailEvent ๊ฐ์ฒด๋กœ ๋ฐœํ–‰๋˜์–ด์•ผ ํ–ˆ๋‹ค.

 

๊ทธ๋Ÿฐ๋ฐ, ๋ฌธ์ œ์ ์ด ๋ฐœ์ƒํ–ˆ๋‹ค.

1. ํšŒ์›๊ฐ€์ž…์ด ์™„๋ฃŒ ๋˜์—ˆ์„ ๊ฒฝ์šฐ ์›ฐ์ปด ๋ฉ”์ผ

2. ์‚ฌ์šฉ์ž๊ฐ€ ํ–‰์‚ฌ ์‹ ์ฒญ์„ ํ•˜๊ณ  ๊ด€๋ฆฌ์ž๊ฐ€ ์Šน์ธ or ํ–‰์‚ฌ ์ทจ์†Œ ํ–ˆ์„ ๋•Œ ๊ด€๋ฆฌ์ž๊ฐ€ ์ทจ์†Œํ™•์ธ ํ•œ ๊ฒฝ์šฐ ํ–‰์‚ฌ ์‹ ์ฒญ ์ƒํƒœ ๋ณ€๊ฒฝ ๋ฉ”์ผ

 

๊ฐ ๋‘ ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ํ•„์š”ํ•œ ์ •๋ณด๊ฐ€ ๋‹ฌ๋ž๋‹ค.

ํšŒ์›๊ฐ€์ž… ํ•  ๊ฒฝ์šฐ, ์ด๋ฉ”์ผ๋งŒ ์•Œ๋ฉด ๋œ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๊ทธ๋ƒฅ ์›ฐ์ปด ์ด๋ฉ”์ผ ๋ณด๋‚ด๋ฉด ๋˜๋‹ˆ๊นŒ!

 

ํ•˜์ง€๋งŒ 2๋ฒˆ ํ–‰์‚ฌ ์ƒํƒœ ๋ณ€๊ฒฝ ๊ณ ์ง€ํ•ด์ฃผ๋Š” ์ด๋ฉ”์ผ์€?

์‚ฌ์šฉ์ž์˜ ์ด๋ฉ”์ผ๋„ ํ•„์š”ํ•  ๋ฟ ์•„๋‹ˆ๋ผ, ํ–‰์‚ฌ๋ฅผ ์ฃผ์ตœํ•˜๋Š” ํด๋Ÿฝ์ด๋ฆ„, ํ–‰์‚ฌ์ด๋ฆ„, ๊ทธ๋ฆฌ๊ณ  ํ–‰์‚ฌ ์ƒํƒœ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ณ€๊ฒฝํ–ˆ๋Š”์ง€๊ฐ€ ์ถ”๊ฐ€์ ์œผ๋กœ ํ•„์š”ํ–ˆ๋‹ค.

 

์ด ๊ฒƒ์„ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ• ๊นŒ ์ƒ๊ฐํ•˜๋‹ค.. ๋„์ž…ํ•œ ๊ฒƒ์ด ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ํŒจํ„ด์ด๋‹ค.

 

๋ฉ”์ผ์„ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด์„œ Spring Mail์„ ์‚ฌ์šฉํ• ๋•Œ ๋ฉ”์ผ์„ ์ „์†กํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ MimeMessage๋ฅผ ์ƒ์„ฑํ•ด์„œ JavaMailSender์—์ธ ์•„๋ž˜ emailSender ๊ฐ์ฒด์— send()ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ๋„˜๊ฒจ์คฌ์–ด์•ผ ํ–ˆ๋‹ค.

์ด MailInfo์•ˆ์—๋Š” ์ด๋ฉ”์ผ ์ฃผ์†Œ์˜ ๋ฐฐ์—ด๊ณผ ์ด๋ฉ”์ผ์˜ ์ œ๋ชฉ๊ณผ ํ…œํ”Œ๋ฆฟ ์ด๋ฆ„์ด ์กด์žฌํ•ด์•ผ ํ–ˆ๋‹ค.

 

๋ฉ”์ผ์„ ๋ณด๋‚ผ ๋•Œ๋Š” ํ•ญ์ƒ ๋™์ผํ•œ MailInfo๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌํ•ด์•ผ ํ–ˆ๋‹ค.

๋ถ€๊ฐ€์ ์œผ๋กœ ์–ด๋Š html ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ• ์ง€, ์ œ๋ชฉ์€ ์–ด๋–ป๊ฒŒ ๋ณด๋‚ผ์ง€๋Š” ๊ฐ ์ด๋ฒคํŠธ๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ์„ค์ •ํ•ด์ค˜์•ผ ํ–ˆ๋‹ค.

๋”ฐ๋ผ์„œ MailInfo๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถ”์ƒ ํด๋ž˜์Šค๋กœ ๋งŒ๋“ค์—ˆ๋‹ค.

public abstract class MailInfo {

    private final String[] addresses;

    public MailInfo(String[] addresses) {
        this.addresses = addresses;
    }

    public String[] email() {
        return this.addresses;
    }

    public abstract String title();

    public abstract String templateName();

    @Override
    public String toString() {
        return new StringJoiner(", ", MailInfo.class.getSimpleName() + "[", "]")
                .add("addresses=" + Arrays.toString(addresses))
                .toString();
    }

}

์ผ๋‹จ TemplateName์ด๋ผ๋Š” enum์„ ๋งŒ๋“ค์–ด WELCOME, EVENT_STATUS_CHANGED ๊ฐ™์ด ํ…œํ”Œ๋ฆฟ ์ด๋ฆ„๊ณผ ํ…œํ”Œ๋ฆฟ ์ œ๋ชฉ, DB์˜ html ํ…œํ”Œ๋ฆฟ id ๋ฅผ ๋ชจ์•„ ๊ด€๋ฆฌํ•จ์œผ๋กœ์„œ ์‘์ง‘๋„๋ฅผ ๋†’ํ˜”๋‹ค.

public enum TemplateName {

    WELCOME("[Space Club] ์ŠคํŽ˜์ด์Šค ํด๋Ÿฝ์— ๊ฐ€์ž…ํ•ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.", "welcome", 1L),
    EVENT_STATUS_CHANGED("[Space Club] ์‹ ์ฒญํ•œ ํ–‰์‚ฌ ์ƒํƒœ ๋ณ€๊ฒฝ ์•Œ๋ฆผ", "event-status-change", 2L),
    ;

    private final String title;

    private final String templateName;

    private final Long templateId;
    ...

 

๊ทธ๋ฆฌ๊ณ  ๋‹คํ˜•์„ฑ์„ ์ด์šฉํ•ด ๊ฐ ๋ณด๋‚ด๋Š” ๋ฉ”์ผ ์ข…๋ฅ˜์— ๋”ฐ๋ผ์„œ ๊ตฌ์ฒดํ™”๋ฅผ ์‹œ์ผœ์ฃผ์—ˆ๋‹ค.

 

๋จผ์ € ํšŒ์›๊ฐ€์ž… ์‹œ ์›ฐ์ปด ๋ฉ”์ผ์„ ๋ณด๋‚ด๋Š” ๋ฉ”์ผ ์ •๋ณด์ด๋‹ค

public class WelcomeMailInfo extends MailInfo {

    public WelcomeMailInfo(String[] addresses) {
        super(addresses);
    }
    
    @Override
    public String title() {
        return WELCOME.getTitle();
    }

    @Override
    public String templateName() {
        return WELCOME.getTemplateName();
    }
...

 

๊ทธ๋ฆฌ๊ณ  ํ–‰์‚ฌ ์ƒํƒœ ๋ณ€๊ฒฝ์„ ๊ณ ์ง€ํ•˜๋Š” ๋ฉ”์ผ ์ •๋ณด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

public class EventStatusChangeMailInfo extends MailInfo {

    private final String clubName;
    private final String eventName;
    private final String eventStatus;
    
    public EventStatusChangeMailInfo(String[] addresses, String clubName, String eventName, String eventStatus) {
        super(addresses);
        this.clubName = clubName;
        this.eventName = eventName;
        this.eventStatus = eventStatus;
    }
    
    @Override
    public String title() {
        return EVENT_STATUS_CHANGED.getTitle();
    }

    @Override
    public String templateName() {
        return EVENT_STATUS_CHANGED.getTemplateName();
    }
...

 

์ด๋ ‡๊ฒŒ ๋งŒ๋“  ๊ฐ๊ฐ์˜ MailInfo์— ๋Œ€ํ•œ ๊ตฌํ˜„์ฒด๋Š” MailEvent์—์„œ ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด์„œ ๊ฐ ๋ฉ”์ผ์„ ๋ณด๋‚ด๋Š” ์ƒํ™ฉ์— ๋งž๊ฒŒ ์ด๋ฒคํŠธ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฉ”์„œ๋“œ ์˜ค๋ฒ„๋กœ๋”ฉ(overloading)๋ฅผ ํ†ตํ•ด ์ƒ์„ฑํ•˜๋„๋ก ํ•ด์ฃผ์—ˆ๋‹ค.

public record MailEvent(MailInfo mailInfo) {

    public static MailEvent createMailEvent(String email) {
        WelcomeMailInfo welcomeMailInfo = WelcomeMailInfo.from(email);
        return new MailEvent(welcomeMailInfo);
    }

    public static MailEvent createMailEvent(String email, String clubName, String eventName, String eventStatus) {
        EventStatusChangeMailInfo mailInfo = EventStatusChangeMailInfo.from(email, clubName, eventName, eventStatus);
        return new MailEvent(mailInfo);
    }
}

 

๋‹ค์‹œ ์œ„ ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ํŒจํ„ด์„ ๊ฐ„๋‹จํžˆ ์„ค๋ช…ํ• ๋•Œ ๊ตฌ์กฐ๋กœ ๋Œ์•„๊ฐ€๋ณธ๋‹ค๋ฉด,

MailInfo๊ฐ€ Product๊ฐ€ ๋˜๊ณ , EventStatusChangeMailInfo,WelcomeMailInfo๊ฐ€ concrete product๊ฐ€ ๋œ๋‹ค.

MailEvent๊ฐ€ Creator๋กœ์„œ  ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ concrete product๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก ํ•œ๋‹ค.

 

๋‹ค๋งŒ ์ „ํ†ต์ ์ธ ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ํŒจํ„ด์—์„œ๋Š” Creator๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค๋กœ์„œ Concrete Creator๋ฅผ ํ†ตํ•ด Concrete Product๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก ํ•˜์ง€๋งŒ, ๋‚˜๋Š” Concrete Creator ๋Œ€์‹  Creator์—์„œ ๋ฐ”๋กœ ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด Product๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๊ทธ๊ฒƒ์„ Creator๋กœ ํ•œ๋ฒˆ ๊ฐ์‹ธ๋Š” ๊ณผ์ •์„ ๊ฑฐ์น˜๋Š” ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค.

 

OCP์™€ DIP์›์น™์„ ์ง€ํ‚ค๊ณ  ์žˆ๊ณ , ์ถ”๊ฐ€์ ์œผ๋กœ ๋ฉ”์ผ์„ ๋ณด๋‚ด๋Š” ์ƒํ™ฉ์ด ์ƒ๊ธฐ๋”๋ผ๋„ ์‘์ง‘๋„๊ฐ€ ๋†’๊ธฐ ๋•Œ๋ฌธ์— ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์šฉ์ดํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

์ƒˆ๋กœ์šด ๋ฉ”์ผ์„ ๋ณด๋‚ด๋Š” ์ƒํ™ฉ์ด ์ƒ๊ธฐ๋ฉด MailEvent์—์„œ ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€์ ์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ , MailInfo๋ฅผ overrideํ•ด์„œ ์ƒˆ๋กœ์šด Concrete Product๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๊ณ  MailService์˜ sendMail()์€ ์žฌ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ๋ฅผ ๋‹ค์‹œ ๋ณด๋‹ˆ ์ข€ ๋” ๋ฆฌํŒฉํ† ๋ง ํ•  ์ƒํ™ฉ๋“ค์ด ๋ณด์ด๊ธดํ•œ๋‹ค ๐Ÿ˜…

 

โญ๏ธ ๊ด€๋ จ PR: https://github.com/Space-Club/Backend/pull/300

 

feat: ์ŠคํŽ˜์ด์Šค ํด๋Ÿฝ ๋ฉ”์ผ ์‹œ์Šคํ…œ ๊ตฌํ˜„ by juno-junho · Pull Request #300 · Space-Club/Backend

๐Ÿ–ฅ๏ธ ์ž‘์—… ๋‚ด์šฉ resource ์•„๋ž˜์˜ htmlํŒŒ์ผ๋กœ ๊ด€๋ฆฌ ํ•˜๋˜ ๋ฐฉ์‹์—์„œ DB์— html ํŒŒ์ผ ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝ factory method pattern์„ ํ†ตํ•ด MailService ๋ฆฌํŽ™ํ† ๋ง ๊ด€๋ฆฌ์ž๊ฐ€ ์ด๋ฒคํŠธ ์ƒํƒœ ๋ณ€๊ฒฝ ์‹œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฉ”์ผ

github.com

 

 

๐Ÿค”  ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ ํŒจํ„ด(Template Method Pattern)๊ณผ ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ํŒจํ„ด(Factory Method Pattern)์˜ ์ฐจ์ด

ํ…œํ”Œ๋ฆฟ  ๋ฉ”์„œ๋“œ ํŒจํ„ด์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ์„ค๋ช…

ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ ํŒจํ„ด์ด๋ž€ ๋ถ€๋ชจ ํด๋ž˜์Šค์—์„œ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ณจ๊ฒฉ์„ ์ •์˜ํ•˜๊ณ , ํ•ด๋‹น ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ์ž์‹ ํด๋ž˜์Šค๋“ค์ด ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ํŠน์ • ๋‹จ๊ณ„๋ฅผ ์žฌ์ •์˜(override)ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ํŒจํ„ด์ด๋‹ค.

์ถœ์ฒ˜: https://refactoring.guru/ko/design-patterns/template-method

์ƒ์œ„ ์ถ”์ƒ ํด๋ž˜์Šค์˜ ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ์—์„œ ํ•˜์œ„ ํด๋ž˜์Šค๊ฐ€ ์žฌ์ •์˜(override)ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํŒจํ„ด์„ ๋งํ•œ๋‹ค.

→ ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ํŒจํ„ด์€ ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ ํŒจํ„ด์˜ ํŠน์ˆ˜ํ™”๋ผ๊ณ  ์ƒ๊ฐ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋Œ€๊ทœ๋ชจ ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ์˜ ํ•œ ๋‹จ๊ณ„์˜ ์—ญํ• ์„ ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

โœจ  2๏ธโƒฃ ์ „๋žต ํŒจํ„ด(Strategy Pattern) ์ ์šฉ ์‚ฌ๋ก€

๐Ÿ“ข  ์ „๋žต ํŒจํ„ด์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ์„ค๋ช…

์ „๋žต ํŒจํ„ด์€ ๋””์ž์ธ ํŒจํ„ด์˜ ๊ฝƒ์ด๋ผ๊ณ  ๋ถˆ๋ฆด๋งŒํผ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด์ด๋ผ๊ณ  ํ•œ๋‹ค.

์ „๋žต ํŒจํ„ด์€ ๋Ÿฐํƒ€์ž„์— ์ „๋žต์„ ์„ ํƒํ•ด ๊ฐ์ฒด ๋™์ž‘์„ ๋™์ ์œผ๋กœ ๋ฐ”๋€Œ๋„๋ก ํ•  ์ˆ˜ ์žˆ๋Š” ๋””์ž์ธ ํŒจํ„ด์ด๋‹ค.

์ถœ์ฒ˜: https://refactoring.guru/ko/design-patterns/strategy

์œ„์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ, ์ „๋žต์€ interface๋กœ ์ •์˜๋˜์–ด ์žˆ๊ณ , ๊ตฌ์ฒด์ ์ธ ConcreteStrategy๋“ค์€ ๊ฐ๊ฐ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  Context๋Š” ํŠน์ • ๋™์ž‘์„ ์‹คํ–‰ํ•  ๋•Œ ๋งˆ๋‹ค ํ•ด๋‹น ๋™์ž‘๊ณผ ์—ฐ๊ฒฐ๋œ ConcreteStrategy์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

Client๋Š” ConcreteStrategy ํŠน์ • ์ „๋žต ๊ฐ์ฒด๋ฅผ ์ปจํ…์ŠคํŠธ์— ์ „๋‹ฌ ํ•จ์œผ๋กœ์„œ ์ „๋žต์„ ๋“ฑ๋กํ•˜๊ฑฐ๋‚˜ ๋ณ€๊ฒฝํ•ด ์ „๋žต ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‹คํ–‰ํ•œ ๊ฒฐ๊ณผ๋ฅผ ์–ป๋Š”๋‹ค.

 

๐Ÿ“ ์–ธ์ œ ์ „๋žต ํŒจํ„ด์„ ์‚ฌ์šฉํ–ˆ๋Š”๊ฐ€?

์ด๋•Œ๊นŒ์ง€ ์ฝ”๋”ฉ์„ ํ•˜๋ฉด์„œ ์•Œ๊ฒŒ ๋ชจ๋ฅด๊ฒŒ ๋งŽ์ด ์ „๋žต ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด ์™”์„ ๊ฑฐ๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

ํ•˜์ง€๋งŒ ๊ธฐ์–ต์— ๋‚จ๋Š” ์ „๋žต ํŒจํ„ด ์‚ฌ์šฉ ์˜ˆ์‹œ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ž‘๋…„ NEXTSTEP ๊ต์œก๊ธฐ๊ด€์—์„œ ์ง„ํ–‰ํ•˜๋Š” TDD, ํด๋ฆฐ ์ฝ”๋“œ with Java 16๊ธฐ ๊ณผ์ •์„ ์ง„ํ–‰ํ•  ๋•Œ์˜€๋‹ค.

 

๊ทธ๋•Œ ๋ ˆ์ด์‹ฑ ๊ฒŒ์ž„ ๋ฏธ์…˜์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ ์ „๋žต ํŒจํ„ด์— ๋Œ€ํ•ด์„œ ์ฒ˜์Œ ์ ‘ํ•˜๊ณ  ํ•™์Šตํ•ด ๋ฏธ์…˜์— ์ ์šฉํ–ˆ๋Š”๋ฐ, ๊ตฌํ˜„ํ•ด์•ผํ•  ์ƒํ™ฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•˜๋‹ค.

1. ์ž๋™์ฐจ ์ด๋ฆ„ ๋ฆฌ์ŠคํŠธ์™€ ์‹œ๋„ ํšŸ์ˆ˜๋ฅผ ์ž…๋ ฅ ๋ฐ›๋Š”๋‹ค. 

2. ์‹œ๋„ ํšŸ์ˆ˜๋งŒํผ ๊ณ„์‚ฐ์„ ํ•˜๋Š”๋ฐ, 0๋ถ€ํ„ฐ 9๊นŒ์ง€์˜ ๋‚œ์ˆ˜๋ฅผ ์ƒ์„ฑํ•ด ํ•ด๋‹น ๊ฐ’์ด 4์ด์ƒ์ด๋ฉด ํ•œ์นธ ์ „์ง„, 4๋ฏธ๋งŒ์ด๋ฉด ์ „์ง„ํ•˜์ง€ ๋ชปํ•œ๋‹ค.

3. ํ•ด๋‹น ํšŸ์ˆ˜๋งŒํผ ๊ฒŒ์ž„์„ ์ง„ํ–‰ ํ›„, ๊ฐ€์žฅ ๋งŽ์ด ์ „์ง„ ํ•œ ์ž๋™์ฐจ ์ด๋ฆ„ (์—ฌ๋Ÿฌ๋Œ€์ผ ๊ฒฝ์šฐ ์ž๋™์ฐจ ๋ฆฌ์ŠคํŠธ)์˜ ์ด๋ฆ„์„ ์ถœ๋ ฅํ•˜๋Š” ๊ฒƒ์ด์˜€๋‹ค.

 

์ด ๋•Œ ์—ฌ๋Ÿฌ Car๋ผ๋Š” ๋„๋ฉ”์ธ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๊ณ , Car ํด๋ž˜์Šค ์•ˆ์—์„œ ์ „์ง„ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์—†๋Š”์ง€๋ฅผ ํŒ๋‹จํ•ด ์ž๋™์ฐจ ์œ„์น˜ ๊ฐ’์„ ์ฆ๊ฐ€ ์‹œ์ผœ์•ผ ํ–ˆ๋‹ค.

public class Car {
    private final Position position;
    private final CarName carName;

    public static final int MOVABLE_LOWER_BOUND = 4;
    
    public static boolean isMovable() {
        Random random = new Random();
        int value = random.nextInt(10);
        return value >= MOVABLE_LOWER_BOUND;
    }
    ...

 

๋”ฐ๋ผ์„œ isMovable() ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์—์„œ ๋žœ๋ค ๊ฐ’์„ ์ƒ์„ฑํ•ด 4๋ณด๋‹ค ํฐ์ง€ ์•ˆํฐ์ง€ ํ™•์ธ์„ ํ•ด์คฌ๋Š”๋ฐ, ์ด๋ ‡๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ๋ฌธ์ œ์ ์œผ๋กœ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ํž˜๋“ค์–ด ์ง„๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๋‹ค.

ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ํž˜๋“  ๊ฐ’(๋‚œ์ˆ˜)์„ ๋‚ด๋ถ€์—์„œ ์ฒ˜๋ฆฌํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ์™ธ๋ถ€์—์„œ ์ฃผ์ž… ํ•ด ์ค˜์•ผ ํ–ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ๊ฐ์ฒด์ง€ํ–ฅ์ ์œผ๋กœ ์ƒ๊ฐํ•ด ๋ดค์„๋•Œ, ํ•ด๋‹น ๋‚œ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ธฐ๋Šฅ์€ ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ์ด๋ฏ€๋กœ, ์ •์ฑ…์ด ๋ณ€๊ฒฝ๋  ์ˆ˜ ๋„ ์žˆ์—ˆ๋‹ค.

 

๋”ฐ๋ผ์„œ ๋‚˜๋Š” ์ „๋žต ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด ์™ธ๋ถ€์—์„œ ๋žœ๋ค ๊ฐ’์„ ์ฃผ์ž…ํ•ด ์คŒ(DI)์œผ๋กœ์„œ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ์‰ฌ์šด ์ฝ”๋“œ๋กœ Car ๋„๋ฉ”์ธ ๊ฐ์ฒด๋ฅผ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

@FunctionalInterface
public interface NumberGenerator {
    int generate();
}
public class RandomNumberGenerator implements NumberGenerator{

    private static final Random random = new Random();
    public static final int RANDOM_BOUND_NUMBER = 10;

    @Override
    public int generate() {
        return random.nextInt(RANDOM_BOUND_NUMBER);
    }
}
public class Car {
    private final Position position;
    private final CarName carName;

    public static final int MOVABLE_LOWER_BOUND = 4;
    
    public static boolean isMovable(NumberGenerator numberGenerator) {
        int value = numberGenerator.generate();
        return value >= MOVABLE_LOWER_BOUND;
    }
    ...

 

๊ทธ๋ฆฌ๊ณ  ๊ฒŒ์ž„์„ ์ง„ํ–‰ํ•˜๋Š” ์™ธ๋ถ€ RacingCarGame์ด๋ผ๋Š” ๊ฐ์ฒด ์•ˆ์—์„œ Car๋ฅผ ์ „์ง„์‹œ์ผœ ์ฃผ์—ˆ๋‹ค.

public class RacingCarGame {

   private final NumberGenerator numberGenerator;
   ...
   
   private void execute(Car car) {
        if (Car.isMovable(numberGenerator)) {
            car.move();
        }
        OutputView.printCarNameAndStatus(car);
    }
    ...

๊ทธ๋ฆฌ๊ณ  NumberGenerator์— ๋Œ€ํ•œ ๊ตฌํ˜„์ฒด์ธ RandomNumberGenerator๋Š” ์™ธ๋ถ€ Application์—์„œ ์ฃผ์ž…ํ•ด ์ฃผ์—ˆ๋‹ค.

public class Application {
    public static void main(String[] args) {
        RacingCarGame racingCarGame = new RacingCarGame(new RandomNumberGenerator());
        racingCarGame.run();
    }
}

 

๋‹ค์‹œ ์ „๋žต ํŒจํ„ด์˜ ๊ตฌ์กฐ๋กœ ๋Œ์•„๊ฐ€ ๋‚ด ๊ตฌ์กฐ๋ฅผ ์„ค๋ช…ํ•˜์ž๋ฉด,

NumberGenerator๋Š” Strategy ์ธํ„ฐํŽ˜์ด์Šค, RandomNumberGenerator๋Š” ConcreteStrategy๊ฐ€ ๋œ๋‹ค.

๊ตฌํ˜„์ฒด๋ฅผ ์ฃผ์ž…ํ•ด์ฃผ๋Š” Client๋Š” Application์ด, Context๋Š” RacingCarGame์ด ๋œ๋‹ค.

 

์ด๋ ‡๊ฒŒ ์ „๋žต ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด strategy๊ฐ€ ํ™•์žฅ๊ฐ€๋Šฅํ•œ OCP ์›์น™๋ฅผ ์ง€ํ‚ค๋ฉฐ, DIP ์›์น™์„ ํ†ตํ•ด ๊ตฌ์ฒด์ ์ธ ๊ฒƒ์ด ์•„๋‹Œ ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กดํ•ด ๋™์ ์œผ๋กœ strategy๊ฐ€ ์ฃผ์ž…๋  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ํž˜๋“  strategy๋ฅผ ์™ธ๋ถ€์—์„œ ์ฃผ์ž…ํ•ด ์ฃผ์–ด ๊ด€๊ณ„๋ฅผ ๋Š์–ด๋ƒ„์œผ๋กœ์„œ ๋„๋ฉ”์ธ ๊ฐ์ฒด์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ๋‹ค.

 

โญ๏ธ ๊ด€๋ จ ๋ ˆํฌ์ง€ํ† ๋ฆฌ ๋งํฌ :

https://github.com/juno-junho/java-racingcar/blob/step5/src/main/java/study/racingcar/strategy/RandomNumberGenerator.java

 

๐Ÿค” ์ „๋žต ํŒจํ„ด(Strategy Pattern)๊ณผ ํ…œํ”Œ๋ฆฟ ์ฝœ๋ฐฑ ํŒจํ„ด(Template Callback Pattern) ์ฐจ์ด

ํ…œํ”Œ๋ฆฟ ์ฝœ๋ฐฑ ํŒจํ„ด์€ ์ „๋žต ํŒจํ„ด์˜ ๋ณ€ํ˜•๋œ ํ˜•ํƒœ์ด๋‹ค.

์ „๋žต ํŒจํ„ด๊ณผ ๋ชจ๋“ ๊ฒƒ์ด ๋™์ผํ•˜๋‹ค. ๋‹ค๋งŒ, ์ „๋žต์„ ์ต๋ช… ๋‚ด๋ถ€ ํด๋ž˜์Šค๋กœ ์ •์˜ํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์ด ํŠน์ง•์ด๋‹ค

 

์œ„ ์ „๋žต ํŒจํ„ด์—์„œ Client ์—ญํ• ์„ ํ•˜๋Š” Application์ด ์ „๋žต ๊ฐ์ฒด์ธ RandomNumberGenerator๋ฅผ Context์ธ RacingCarGame ๊ฐ์ฒด์— ์ „๋‹ฌ ํ–ˆ๋‹ค.

 

ํ…œํ”Œ๋ฆฟ ์ฝœ๋ฐฑ ํŒจํ„ด์—์„œ๋Š” Context๊ฐ€ Template์ด ๋˜๊ณ , Strategy๋ฅผ RandomNumberGenerator ์ฒ˜๋Ÿผ ๋งŒ๋“ค์–ด ๋‘๋Š” ๊ฒƒ์ด ์•„๋‹Œ ์ต๋ช… ํด๋ž˜์Šค๋‚˜ ๋žŒ๋‹ค๋กœ ์ •์˜ํ•ด client์—์„œ ํ˜ธ์ถœ ์‹œ ๋ฐ”๋กœ ์ •์˜ํ•ด ์ฃผ๋Š” ํŒจํ„ด์„ ๋งํ•œ๋‹ค.

 

์ฆ‰, ์œ„ ๋ ˆ์ด์‹ฑ ๊ฒŒ์ž„์˜ ์ฝ”๋“œ๋ฅผ ํ…œํ”Œ๋ฆฟ ์ฝœ๋ฐฑ ํŒจํ„ด์œผ๋กœ ๋ณ€๊ฒฝํ•ด ๋ณด์ž๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

public class Application {
    public static void main(String[] args) {
    // ์ต๋ช… ํด๋ž˜์Šค ์‚ฌ์šฉ
        RacingCarGame racingCarGame = new RacingCarGame(
                new NumberGeneratePolicy() {
                    @Override
                    public int generate() {
                        return new Random.nextInt(10);
                    }
                });
        racingCarGame.run();
    }
}

public class Application {
    public static void main(String[] args) {
    // ๋žŒ๋‹ค์‹ ์‚ฌ์šฉ
        RacingCarGame racingCarGame = new RacingCarGame(() -> new Random().nextInt(10));
        racingCarGame.run();
    }
}

 

๋‹จ์œ„ ํ…Œ์ŠคํŠธ์‹œ NumberGenerator ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ…Œ์ŠคํŠธ ๋”๋ธ” dummy ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ–ˆ๋Š”๋ฐ,

์ด๋•Œ๋„ ์‚ฌ์šฉํ–ˆ๋˜ ํŒจํ„ด์ด ์ง€๊ธˆ ๋ณด๋‹ˆ ํ…œํ”Œ๋ฆฟ ์ฝœ๋ฐฑ ํŒจํ„ด์˜€๋‹ค.

@Nested
    @DisplayName("Movable ๋ฉ”์„œ๋“œ ๊ฒ€์ฆ")
    class MoveableValidationTest {
        public static final int lowerBoundNum = Car.MOVABLE_LOWER_BOUND;

        @DisplayName("์ฐจ๊ฐ€ ์›€์ง์ผ ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค")
        @ParameterizedTest(name = "{0}์„ ๋„ฃ์—ˆ์„๋•Œ false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค")
        @ValueSource(ints = {lowerBoundNum - 1, lowerBoundNum - 2, lowerBoundNum - 3, lowerBoundNum - 4})
        void when_NumberUnderLowerBound_Expects_False(int num) {
            // given, when
            boolean actualResult = Car.isMovable(() -> num);

            // then
            assertThat(actualResult).isFalse();
        }

        @DisplayName("Moveable ํ•  ๊ฒฝ์šฐ true๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.")
        @ParameterizedTest(name = "{0}์„ ๋„ฃ์—ˆ์„๋•Œ true๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค")
        @ValueSource(ints = {lowerBoundNum, lowerBoundNum + 1, lowerBoundNum + 2, lowerBoundNum + 3})
        void when_NumberIsEqualToOrOverLowerBound_Expects_True(int num) {
            // given, when
            boolean actualResult = Car.isMovable(() -> num);

            // then
            assertThat(actualResult).isTrue();
        }
    }

โญ๏ธ ๊ด€๋ จ ๋ ˆํฌ์ง€ํ† ๋ฆฌ ๋งํฌ :

https://github.com/juno-junho/java-racingcar/blob/step5/src/test/java/study/racingcar/domain/CarTest.java

 

๐Ÿ”š ๋๋งบ์Œ..

๋””์ž์ธ ํŒจํ„ด์— ๋Œ€ํ•ด์„œ ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค ๋ฐ๋ธŒ์ฝ”์Šค ๊ณผ์ •๋™์•ˆ ํ•™์Šตํ•˜๊ณ , ์ ์šฉ๋„ ํ•ด๋ณด๊ธฐ๋„ ํ•ด์„œ ๋ฟŒ๋“ฏํ–ˆ๋‹ค.

์‹ค๋ฌด์— ๊ฐ€์„œ๋Š” ๋””์ž์ธ ํŒจํ„ด์„ ์ ์žฌ์ ์†Œ์— ์ž์œ ์ž์žฌ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ณ  ์‹ถ๋‹ค.

 

๋ฉ˜ํ† ๋‹˜๋„ ๋ชจ๋“  ๋””์ž์ธ ํŒจํ„ด์„ ๋‹ฌ๋‹ฌ ์™ธ์šฐ๊ธฐ ๋ณด๋‹ค๋Š” ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ํŠน์ • ํŒจํ„ด๋“ค์ด ์žˆ๋Š”๋ฐ, ์ฝ”๋“œ๋ฅผ ๋ณด๊ณ  ์•„ ์ด๊ฒŒ ๊ทธ ํŒจํ„ด์œผ๋กœ ๋˜์–ด์žˆ๊ตฌ๋‚˜!๋ฅผ ์•Œ๋ฉด ๋œ๋‹ค๊ณ ๋Š” ํ•˜์…จ๋‹ค.

 

์ถ”๊ฐ€์ ์œผ๋กœ ๊พธ์ค€ํžˆ ํ•™์Šต๊ณผ ์ ์šฉ์ด ํ•„์š”ํ•ด ๋ณด์ธ๋‹ค.

 

Reference

https://refactoring.guru/ko/design-patterns/what-is-pattern

 

๋””์ž์ธ ํŒจํ„ด์ด๋ž€?

๋””์ž์ธ ํŒจํ„ด์ด๋ž€? ๋””์ž์ธ ํŒจํ„ด์€ ์†Œํ”„ํŠธ์›จ์–ด ๋””์ž์ธ ๊ณผ์ •์—์„œ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋“ค์— ๋Œ€ํ•œ ์ „ํ˜•์ ์ธ ํ•ด๊ฒฐ์ฑ…์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ฝ”๋“œ์—์„œ ๋ฐ˜๋ณต๋˜๋Š” ๋””์ž์ธ ๋ฌธ์ œ๋“ค์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋งž์ถคํ™”ํ•  ์ˆ˜ ์žˆ๋Š”

refactoring.guru

https://product.kyobobook.co.kr/detail/S000001628116

 

์Šคํ”„๋ง ์ž…๋ฌธ์„ ์œ„ํ•œ ์ž๋ฐ” ๊ฐ์ฒด ์ง€ํ–ฅ์˜ ์›๋ฆฌ์™€ ์ดํ•ด | ๊น€์ข…๋ฏผ - ๊ต๋ณด๋ฌธ๊ณ 

์Šคํ”„๋ง ์ž…๋ฌธ์„ ์œ„ํ•œ ์ž๋ฐ” ๊ฐ์ฒด ์ง€ํ–ฅ์˜ ์›๋ฆฌ์™€ ์ดํ•ด | ์ด ์ฑ…์€ ์ž๋ฐ”์—์„œ ์Šคํ”„๋ง์œผ๋กœ ๋‚˜์•„๊ฐ€๊ธฐ ์œ„ํ•œ ์—ฐ๊ฒฐ ๊ณ ๋ฆฌ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

product.kyobobook.co.kr

https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9CFactory-Method-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90

 

๐Ÿ’  ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ(Factory Method) ํŒจํ„ด - ์™„๋ฒฝ ๋งˆ์Šคํ„ฐํ•˜๊ธฐ

Factory Method Pattern ํŒฉํ† ๋ฆฌ ๋ฉ”์†Œ๋“œ ํŒจํ„ด์€ ๊ฐ์ฒด ์ƒ์„ฑ์„ ๊ณต์žฅ(Factory) ํด๋ž˜์Šค๋กœ ์บก์Šํ™” ์ฒ˜๋ฆฌํ•˜์—ฌ ๋Œ€์‹  ์ƒ์„ฑํ•˜๊ฒŒ ํ•˜๋Š” ์ƒ์„ฑ ๋””์ž์ธ ํŒจํ„ด์ด๋‹ค. ์ฆ‰, ํด๋ผ์ด์–ธํŠธ์—์„œ ์ง์ ‘ new ์—ฐ์‚ฐ์ž๋ฅผ ํ†ตํ•ด ์ œํ’ˆ ๊ฐ์ฒด๋ฅผ

inpa.tistory.com

https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%A0%84%EB%9E%B5Strategy-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90

 

๐Ÿ’  ์ „๋žต(Strategy) ํŒจํ„ด - ์™„๋ฒฝ ๋งˆ์Šคํ„ฐํ•˜๊ธฐ

Strategy Pattern ์ „๋žต ํŒจํ„ด์€ ์‹คํ–‰(๋Ÿฐํƒ€์ž„) ์ค‘์— ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ „๋žต์„ ์„ ํƒํ•˜์—ฌ ๊ฐ์ฒด ๋™์ž‘์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ”๋€Œ๋„๋ก ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ํ–‰์œ„ ๋””์ž์ธ ํŒจํ„ด ์ด๋‹ค. ์—ฌ๊ธฐ์„œ '์ „๋žต'์ด๋ž€ ์ผ์ข…์˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ๋  ์ˆ˜

inpa.tistory.com

 
 
 
 

https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-Template-Callback-%EB%B3%80%ED%98%95-%ED%8C%A8%ED%84%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0

 

๐Ÿ’  Template Callback ๋””์ž์ธ ํŒจํ„ด

Template Callback Pattern ํƒฌํ”Œ๋ฆฟ ์ฝœ๋ฐฑ ํŒจํ„ด(Template Callback Pattern)์€ ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ DI(Dependency injection) ์˜์กด์„ฑ ์ฃผ์ž…์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํŠน๋ณ„ํ•œ ์ „๋žต ํŒจํ„ด์ด๋‹ค. ์Šคํ”„๋ง์˜ JdbcTemplate, RestTemplate, Transaction

inpa.tistory.com

 

 
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday