ํฐ์คํ ๋ฆฌ ๋ทฐ
๐จ ๋์์ธ ํจํด ์ ์ฉ๊ธฐ - ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด, ์ ๋ต ํจํด โจ (feat. ํ ํ๋ฆฟ ๋ฉ์๋, ํ ํ๋ฆฟ ์ฝ๋ฐฑ)
junojuno 2024. 2. 3. 18:16๐ช ๋ค์ด๊ฐ๊ธฐ์ ์์...
๋จผ์ , ์ด ํฌ์คํธ์์๋ ๋ด๊ฐ ์ผ๋ง๋ ๋์์ธ ํจํด์ ๋ํด์ ์ด์ฌํ ๊ณต๋ถํ๋์ง, ์ผ๋ง๋ ๋ง์ ๋์์ธ ํจํด์ ์๋์ง ๋ณด๋ค ๋์์ธ ํจํด์ผ๋ก ์ด๋ค ๋ฌธ์ ๋ฅผ ํด๊ฒฐ ํ๋์ง์ ํฌ์ปค์ค๋ฅผ ๋ง์ถฐ ๋ณด๊ณ ์ถ๋ค.
์๋ํ๋ฉด ๋์์ธ ํจํด์ ์ ์๊ณ ํ์ตํ๋ ๊ฒ๋ ์ค์ํ์ง๋ง, ๋์์ธ ํจํด์ผ๋ก ์ด๋ค ๋ฌธ์ ๋ฅผ ํด๊ฒฐ ํ๋์ง๊ฐ ๋ ์ค์ํ๋ค๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ์ด๋ค
๋์์ธ ํจํด ๊ฐ๊ฐ์ ๋ํ ์์ธํ ์ค๋ช ์ ๋ ธ์ ์ ์์ธํ ๊ธฐ๋ก์ ํด ๋์๋ค..! ๐
๋์์ธ ํจํด์ ๋ํด์ ํ์ตํ๊ณ ์ ๋ฆฌํ ๋ ธ์ ๋งํฌ
1. ๋์์ธ ํจํด ์ด๋
๋์์ธ ํจํด์ด๋ ์ํํธ์จ์ด ๋์์ธ ๊ณผ์ ์์ ์์ฃผ ๋ฐ์ํ๋ ๋ฌธ์ ๋ค์ ๋ํ ํด๊ฒฐ์ฑ ์์ ๋งํ๋ค.
SOLID ์์น์ ๋ฐ๋ผ ๊ฐ์ฒด์งํฅ์ ์ผ๋ก ํ๋ก๊ทธ๋๋ฐํ๋ ๋ฐ ์์ด์ ์ฌ๋ฌ ์ํฉ์ ๋ํ ๊ณตํต์ ๋ค์ ๋ชจ์ ๋์๊ฒ์ด ๋์์ธ ํจํด์ด๋ผ๊ณ ํ๋ค.
๋์์ธ ํจํด์ ์ฒ์์ ๋๋ ์ ํ์ ๋๋, ์ด๋ฐ ํจํด์ด ์ด์ ๋ถํฐ ์กด์ฌํ๊ตฌ๋?! ๋ผ๊ณ ์๊ฐํ๋ค.
ํ์ง๋ง ํ์ต์ ํ๊ณ ๋ณด๋, ๊ณ ๋ ๋ง์ ๊ฐ๋ฐ์ ๋ถ๋ค์ด ๋น์ฆ๋์ค ์๊ตฌ์ฌํญ์ ํ๋ก๊ทธ๋๋ฐ์ผ๋ก ์ฒ๋ฆฌํ๋ฉฐ ๋ง์ฃผ์น ๋ฌธ์ ๋ค์ ๋ํ ํด๊ฒฐ ์ฑ ์ค ๋ง์ ์ฌ๋๋ค์ด ์ธ์ ํ Best Practice๋ฅผ ์ ๋ฆฌํ ๊ฒ์ผ๋ก, 1994๋ GoF(Gang of Four)๋ผ๊ณ ๋ถ๋ฆฌ๋ Erich Gamma, Richard Helm, Ralph Johnson, John Vissides๊ฐ ์งํํ GoF ๋์์ธ ํจํด ์ฑ ์ ํตํด ์ ๋ฆฌ๋๊ณ ๋ฐ์ ๋์๋ค๊ณ ํ๋ค.
๋์์ธ ํจํด์ ๊ฐ์ฒด์งํฅ์ ํน์ฑ๊ณผ ๊ฐ์ฒด์งํฅ ์ค๊ณ ์์น(SOLID)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ตฌํ๋ ์ค๊ณ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด์ฃผ๋ ํจํด์ด๊ธฐ ๋๋ฌธ์ ํ์ต์ด ํ์ํ๋ค๋ ๊ฒ์ ๋๊ผ๋ค..!
2. ๋๋ ๋์์ธ ํจํด์ผ๋ก ์ด๋ค ๋ฌธ์ ๋ฅผ ํด๊ฒฐ ํ๋๊ฐ?
โจ 1๏ธโฃ ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด(Factory Method Pattern) ์ ์ฉ ์ฌ๋ก
๐ข ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด์ ๋ํ ๊ฐ๋จํ ์ค๋ช
ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด์ด๋ ๋ถ๋ชจ ํด๋์ค์์ ๊ฐ์ฒด๋ค์ ์์ฑํ ์ ์๋ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๊ณ , ์์ ํด๋์ค๋ค์ด ์์ฑ๋ ๊ฐ์ฒด๋ค์ ์ ํ์ ๋ณ๊ฒฝํ ์ ์๋๋ก ํ๋ ์์ฑํจํด์ ๋งํ๋ค.
์ ๊ตฌ์กฐ์์ ๋ณผ ์ ์๋ฏ, 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
๐ค ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด(Template Method Pattern)๊ณผ ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด(Factory Method Pattern)์ ์ฐจ์ด
ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด์ ๋ํ ๊ฐ๋จํ ์ค๋ช
ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด์ด๋ ๋ถ๋ชจ ํด๋์ค์์ ์๊ณ ๋ฆฌ์ฆ ๊ณจ๊ฒฉ์ ์ ์ํ๊ณ , ํด๋น ์๊ณ ๋ฆฌ์ฆ ๊ตฌ์กฐ๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ์์ ํด๋์ค๋ค์ด ์๊ณ ๋ฆฌ์ฆ์ ํน์ ๋จ๊ณ๋ฅผ ์ฌ์ ์(override)ํ ์ ์๋๋ก ํ๋ ํจํด์ด๋ค.
์์ ์ถ์ ํด๋์ค์ ํ ํ๋ฆฟ ๋ฉ์๋์์ ํ์ ํด๋์ค๊ฐ ์ฌ์ ์(override)ํ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ํจํด์ ๋งํ๋ค.
→ ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด์ ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด์ ํน์ํ๋ผ๊ณ ์๊ฐ ํ ์ ์๋ค. ๋๊ท๋ชจ ํ ํ๋ฆฟ ๋ฉ์๋์ ํ ๋จ๊ณ์ ์ญํ ์ ํฉํ ๋ฆฌ ๋ฉ์๋๊ฐ ํ ์ ์๋ค.
โจ 2๏ธโฃ ์ ๋ต ํจํด(Strategy Pattern) ์ ์ฉ ์ฌ๋ก
๐ข ์ ๋ต ํจํด์ ๋ํ ๊ฐ๋จํ ์ค๋ช
์ ๋ต ํจํด์ ๋์์ธ ํจํด์ ๊ฝ์ด๋ผ๊ณ ๋ถ๋ฆด๋งํผ ์์ฃผ ์ฌ์ฉํ๋ ํจํด์ด๋ผ๊ณ ํ๋ค.
์ ๋ต ํจํด์ ๋ฐํ์์ ์ ๋ต์ ์ ํํด ๊ฐ์ฒด ๋์์ ๋์ ์ผ๋ก ๋ฐ๋๋๋ก ํ ์ ์๋ ๋์์ธ ํจํด์ด๋ค.
์์์ ๋ณผ ์ ์๋ฏ, ์ ๋ต์ 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๋ฅผ ์ธ๋ถ์์ ์ฃผ์ ํด ์ฃผ์ด ๊ด๊ณ๋ฅผ ๋์ด๋์ผ๋ก์ ๋๋ฉ์ธ ๊ฐ์ฒด์ ๋ํ ํ ์คํธ๋ฅผ ์ฉ์ดํ๊ฒ ๋ง๋ค์ด ์ฃผ์๋ค.
โญ๏ธ ๊ด๋ จ ๋ ํฌ์งํ ๋ฆฌ ๋งํฌ :
๐ค ์ ๋ต ํจํด(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();
}
}
โญ๏ธ ๊ด๋ จ ๋ ํฌ์งํ ๋ฆฌ ๋งํฌ :
๐ ๋๋งบ์..
๋์์ธ ํจํด์ ๋ํด์ ํ๋ก๊ทธ๋๋จธ์ค ๋ฐ๋ธ์ฝ์ค ๊ณผ์ ๋์ ํ์ตํ๊ณ , ์ ์ฉ๋ ํด๋ณด๊ธฐ๋ ํด์ ๋ฟ๋ฏํ๋ค.
์ค๋ฌด์ ๊ฐ์๋ ๋์์ธ ํจํด์ ์ ์ฌ์ ์์ ์์ ์์ฌ๋ก ์ฌ์ฉํ ์ ์๋๋ก ํ๊ณ ์ถ๋ค.
๋ฉํ ๋๋ ๋ชจ๋ ๋์์ธ ํจํด์ ๋ฌ๋ฌ ์ธ์ฐ๊ธฐ ๋ณด๋ค๋ ๋ง์ด ์ฌ์ฉํ๋ ํน์ ํจํด๋ค์ด ์๋๋ฐ, ์ฝ๋๋ฅผ ๋ณด๊ณ ์ ์ด๊ฒ ๊ทธ ํจํด์ผ๋ก ๋์ด์๊ตฌ๋!๋ฅผ ์๋ฉด ๋๋ค๊ณ ๋ ํ์ จ๋ค.
์ถ๊ฐ์ ์ผ๋ก ๊พธ์คํ ํ์ต๊ณผ ์ ์ฉ์ด ํ์ํด ๋ณด์ธ๋ค.
Reference
https://refactoring.guru/ko/design-patterns/what-is-pattern
https://product.kyobobook.co.kr/detail/S000001628116
'Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๐จ๐ปโ๐ป ๊ธ์น์ด ํํฐ๋ง ๊ธฐ๋ฅ์ ๋ถ์ํ๊ณ ๋ฆฌํฉํ ๋ง ํด๋ณด์! (1) - Trie ์๋ฃ๊ตฌ์กฐ ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค ๋ถ์ (0) | 2024.05.15 |
---|---|
๐ [ํ๋ก๊ทธ๋๋จธ์ค ๋ฐฑ์๋ ๋ฐ๋ธ์ฝ์ค] Space Club ์ต์ข ํ๋ก์ ํธ ํ๊ณ (4) | 2024.05.09 |
์คํธ๋ญ๊ธ๋ฌ ํจํด (2) | 2023.05.31 |
Clean Code - ๋์์ ํด๋ฆฌ์คํฑ (์ผ๋ฐ) (0) | 2022.11.01 |
Clean Code ์ฑ ์ ์ฝ๊ณ (0) | 2022.11.01 |
- Total
- Today
- Yesterday