ð í í늿 ë©ìë(Template Method) íšíŽ - ì벜 ë§ì€í°íêž°
Template Method Pattern
í í늿 ë©ìë(Template Method) íšíŽì ì¬ë¬ íŽëì€ìì ê³µíµìŒë¡ ì¬ì©íë ë©ìë륌 í í늿í íì¬ ìì íŽëì€ìì ì ìíê³ , íì íŽëì€ë§ë€ ìžë¶ ëì ì¬íì ë€ë¥Žê² 구ííë íšíŽìŽë€.
ìŠ, ë³íì§ ìë êž°ë¥(í í늿)ì ìì íŽëì€ì ë§ë€ìŽëê³ ì죌 ë³ê²œëë©° íì¥í êž°ë¥ì íì íŽëì€ìì ë§ë€ëë¡ íì¬, ììì ë©ìë ì€í ëì ììë ê³ ì íë©Žì ìžë¶ ì€í ëŽì©ì ë€ìí ë ì ìë 겜ì°ì ì¬ì©ëë€.
í í늿 ë©ìë íšíŽì ìììŽëŒë êž°ì ì ê·¹ëííì¬, ìê³ ëŠ¬ìŠì ëŒë륌 ë§ì¶ë ê²ì ìŽì ì ëë€. ìŽë¯ž ìë§ì íë ììí¬ìì ë§ì ë¶ë¶ì í í늿 ë©ìë íšíŽ ìœëê° ì°ëŠ¬ë ëªšë¥Žê² ì ì©ëìŽ ìë€.
ëììž íšíŽììì í í늿ì ë³íì§ ìë ê²ì ì믞íë€.
í í늿 ë©ìë íšíŽ 구조
- AbstractClass(ì¶ì íŽëì€) : í í늿 ë©ìë륌 구ííê³ , í í늿 ë©ìëìì ëìê°ë ì¶ì ë©ìë륌 ì ìžíë€. ìŽ ì¶ì ë©ìëë íì íŽëì€ìž ConcreteClass ìí ì ìíŽ êµ¬íëë€.
- ConcreteClass(구í íŽëì€) : AbstractClass륌 ììíê³ ì¶ì ë©ìë륌 구첎ì ìŒë¡ 구ííë€. ConcreteClassìì 구íí ë©ìëë AbstractClassì í í늿 ë©ìëìì ížì¶ëë€.
hook ë©ìë
í (hook) ë©ìëë ë¶ëªšì í í늿 ë©ìëì ìí¥ìŽë ìì륌 ì ìŽíê³ ì¶ìë ì¬ì©ëë ë©ìë íí륌 ë§íë€.
ìì 귞늌ìì 볎ë¯ìŽ í
í늿 ë©ìë ëŽì ì€íëë ëìì step2() ìŽëŒë ë©ìëì ì°ž, ê±°ì§ ì 묎ì ë°ëŒ ë€ì ì€í
ì ìŽë»ê² ìŽìŽëê°ì§ ì§ì íë€. ìŽë¥Œ íµíŽ ìì íŽëì€ìì ì¢ë ì ì°íê² í
í늿 ë©ìëì ìê³ ëŠ¬ìŠ ë¡ì§ì ë€ìí í ì ìë€ë í¹ì§ìŽ ìë€.
í ë©ìëë ì¶ì ë©ìëê° ìë ìŒë° ë©ìëë¡ êµ¬ííëë°, ì íì ìŒë¡ ì€ë²ëŒìŽë íì¬ ìì íŽëì€ìì ì ìŽíê±°ë ìëë©Ž ëëê±°ë íêž° ìíŽì ìŽë€.
í í늿 ë©ìë íšíŽ íëŠ
íŽëì€ êµ¬ì±
abstract class AbstractTemplate {
// í
í늿 ë©ìë : ë©ìë ìì final í€ìë륌 ë¶ìŽë©Ž ìì íŽëì€ìì ì€ë²ëŒìŽë©ìŽ ë¶ê°ë¥íš.
// ìì íŽëì€ìì ìì í
í늿ì ì€ë²ëŒìŽë©íŽì ìêž°ë§ìëë¡ ë°êŸžëë¡ íë íì륌 ìì² ëŽì
public final void templateMethod() {
// ììíì¬ êµ¬íëë©Ž ì€íë ë©ìëë€
step1();
step2();
if(hook()) { // ìì ë¡ì§ì ì€ííê±°ë ì€ííì§ ìì
// ...
}
step3();
}
boolean hook() {
return true;
}
// ììíì¬ ì¬ì©í ê²ìŽêž° ë묞ì protected ì ê·Œì ìŽì ì€ì
protected abstract void step1();
protected abstract void step2();
protected abstract void step3();
}
class ImplementationA extends AbstractTemplate {
@Override
protected void step1() {}
@Override
protected void step2() {}
@Override
protected void step3() {}
}
class ImplementationB extends AbstractTemplate {
@Override
protected void step1() {}
@Override
protected void step2() {}
@Override
protected void step3() {}
// hook ë©ìë륌 ì€ë²ëŒìŽë íŽì falseë¡ íì¬ í
í늿ìì ë§ì§ë§ ë¡ì§ìŽ ì€íëì§ ìëë¡ ì€ì
@Override
protected boolean hook() {
return false;
}
}
íŽëì€ íëŠ
class Client {
public static void main(String[] args) {
// 1. í
í늿 ë©ìëê° ì€íí 구ííí íì ìê³ ëŠ¬ìŠ íŽëì€ ìì±
AbstractTemplate templateA = new ImplementationA();
// 2. í
í늿 ì€í
templateA.templateMethod();
}
}
í í늿 ë©ìë íšíŽ í¹ì§
íšíŽ ì¬ì© ìêž°
- íŽëŒìŽìžížê° ìê³ ëŠ¬ìŠì í¹ì ëšê³ë§ íì¥íê³ , ì 첎 ìê³ ëŠ¬ìŠìŽë íŽë¹ 구조ë íì¥íì§ ìëë¡ í ë
- ëìŒí êž°ë¥ì ìì íŽëì€ìì ì ìíë©Žì íì¥, ë³íê° íìí ë¶ë¶ë§ íì íŽëì€ìì 구íí ë
íšíŽ ì¥ì
- íŽëŒìŽìžížê° ëê·ëªš ìê³ ëŠ¬ìŠì í¹ì ë¶ë¶ë§ ì¬ì ìíëë¡ íì¬, ìê³ ëŠ¬ìŠì ë€ë¥ž ë¶ë¶ì ë°ìíë ë³ê²œ ì¬íì ìí¥ì ë ë°ëë¡ íë€.
- ìì ì¶ìíŽëì€ë¡ ë¡ì§ì ê³µíµí íì¬ ìœëì ì€ë³µì ì€ìŒ ì ìë€.
- ìëž íŽëì€ì ìí ì ì€ìŽê³ , íµì¬ ë¡ì§ì ìì íŽëì€ìì êŽëŠ¬íë¯ë¡ì êŽëŠ¬ê° ì©ìŽíŽì§ë€
- í늬ì°ë ìì¹ (Hollywood Principle) : ê³ ìì€ êµ¬ì±ìììì ì ìì€ì ë€ë£šë ìì¹ (ì¶ìíì ìì¡Ž)
íšíŽ ëšì
- ìê³ ëŠ¬ìŠì ì ê³µë 곚격ì ìíŽ ì ì°ì±ìŽ ì íë ì ìë€.
- ìê³ ëŠ¬ìŠ êµ¬ì¡°ê° ë³µì¡í ìë¡ í í늿 ë¡ì§ íí륌 ì ì§íêž° ìŽë €ìì§ë€.
- ì¶ì ë©ìëê° ë§ìì§ë©Žì íŽëì€ì ìì±, êŽëŠ¬ê° ìŽë €ìì§ ì ìë€.
- ìì íŽëì€ìì ì ìžë ì¶ì ë©ìë륌 íì íŽëì€ìì 구íí ë, ê·ž ë©ìëê° ìŽë íìŽë°ìì ížì¶ëëì§ íŽëì€ ë¡ì§ì ìŽíŽíŽìŒ í íìê° ìë€.
- ë¡ì§ì ë³íê° ì겚 ìì íŽëì€ë¥Œ ìì í ë, 몚ë ìëž íŽëì€ì ìì ìŽ íì í ìë ìë€.
- íì íŽëì€ë¥Œ íµíŽ Ʞ볞 ëšê³ 구íì ìµì íì¬ ëŠ¬ì€ìœí ì¹í ë²ì¹ì ìë°í ì¬ì§ê° ìë€.
ìì 륌 íµíŽ ìì볎ë Template Method íšíŽ
ìí©ì ë°ëŒ ë€ìí ì°ì° ìê³ ëŠ¬ìŠ ì ì©
number.txt íìŒì ì íìë ì«ì ê°ë€ì ìœìŽ, ì«ìë€ì ì°ì°í 결곌륌 ìë €ì£Œë êž°ë¥ì 구ííŽë³Žì.
íŽëŠ°íì§ ìì 묞ì ì ìœë â
ìëì 구ì±ì 볎멎 FileProcesser íŽëì€ì process() ë©ìëìì ìê³ ëŠ¬ìŠì ìííë ê²ì 볌 ì ìë€.
Clientì ë©ìž ë©ìëìì íìŒê²œë¡ë¥Œ ì€ì íŽì£Œê³ process() 륌 ì€ííë©Ž 몚ë ì«ìë€ì ë§ì
í 결곌 15ê° ì¶ë ¥ëë€.
class FileProcessor {
private String path; // ìì±ìë¡ ë¶í° íìŒê²œë¡ë¥Œ ë°ìì ì ì¥
public FileProcessor(String path) {
this.path = path;
}
public int process() {
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
int result = 0;
String line = null;
// íìŒì ìë ê° ëŒìžì ìë ì«ìê°ë€ì 몚ë ë§ì
while ((line = reader.readLine()) != null) {
result += Integer.parseInt(line);
}
return result;
} catch (IOException e) {
throw new IllegalArgumentException(path + "ì íŽë¹íë íìŒìŽ ììµëë€.", e);
}
}
}
class Client {
public static void main(String[] args) {
// íìŒê²œë¡ ì€ì
FileProcessor fileProcessor = new FileProcessor("number.txt");
// ë§ì
í ê²°ê³Œê° ì»êž°
int result = fileProcessor.process();
System.out.println(result);
}
}
ê·žë°ë° ë§ìŒ 몚ë ì«ìë€ì ê³±ì íê±°ë ëëë ì¶ê°ì ìž ì°ì°íë êž°ë¥ìŽ íìíë€ê³ íë€.
ê·žëì MultiplyFileProcessor ëŒë ìë¡ìŽ íŽëì€ë¥Œ ì ìíê³ FileProcessor íŽëì€ìì êž°ë¥ì ê°ì žì ì°ì°íë ëŒìž ë¶ë¶ì ê³±ì ìŒë¡ ë°ê¿ 죌ìë€.
class MultiplyFileProcessor {
private String path;
public MultiplyFileProcessor(String path) {
this.path = path;
}
public int process() {
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
int result = 0;
String line = null;
// íìŒì ìë ê° ëŒìžì ìë ì«ìê°ë€ì 몚ë ê³±ì
while ((line = reader.readLine()) != null) {
result *= Integer.parseInt(line); // ìŽ ë¶ë¶ë§ ê³±ì
ìŒë¡ ë°ê¿ì€ë€.
}
return result;
} catch (IOException e) {
throw new IllegalArgumentException(path + "ì íŽë¹íë íìŒìŽ ììµëë€.", e);
}
}
}
íì§ë§ ì°ì° ë¶ë¶ë§ ë€ë¥Œë¿ìŽì§ íìŒì ìœìŽì ìœë ìê³ ëŠ¬ìŠ ë¶ë¶ì ëê°ë€. ìŠ, ìœëì ì€ë³µìŽ ë°ìí ê²ìŽë€.
ë³Žíµ ë©ìë ì€ë³µì íŽê²°íêž° ìíŽ ììì íê³ ë¶ëªš íŽëì€ìì ë©ìë륌 ì ìíë©Ž ìì íŽëì€ìì ê°ì žì ì¬ì©íë©Ž ëì§ë§, ìì ë©ìë ë¡ì§ì 볎멎 íëì€ë§ ìœëê° ë€ë¥ž ìí©ìŽë€.
ê·žë¬ë©Ž ê³µíµë ë¶ë¶ë§ìŽëŒë ë°ë¡ ë©ìëë¡ ë¹Œë©Ž ëëë°, ìœë ë¡ì§ì ì€íë € ë ë³µì¡íŽì§ê²ë§ ê°ë€.
ë°ëŒì ê³µíµë ë¶ë¶ì ë©ìëë¡ ë¹Œëê² ìë, ë°ëë¡ ë€ë¥ž ë¶ë¶ì ë©ìëë¡ ë¹Œë²ëŠ¬ë ë°ìì ì íì íë©Ž ëë€.
í í늿 ë©ìë íšíŽì ì ì©í ìœë âïž
뚌ì FileProcessor륌 ì¶ì íŽëì€ë¡ ë§ë€ê³ process() ë©ìë륌 í
í늿 ë©ìëë¡ ëª
ëª
íë€. ê·žëŠ¬ê³ í
í늿ìì ì€íëë caculate() ë©ìë륌 ë°ë¡ ì¶ì ë©ìëë¡ ë§ë€ìŽ ìì íŽëì€ìì 구íì íëë¡ ì ë íë€.
abstract class FileProcessor {
private String path; // ìì±ìë¡ ë¶í° íìŒê²œë¡ë¥Œ ë°ìì ì ì¥
public FileProcessor(String path) {
this.path = path;
}
// í
í늿 ë©ìë (ì€ë²ëŒìŽë© 못íê² final ì²ëŠ¬)
public final int process() {
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
int result = getResult();
String line = null;
while ((line = reader.readLine()) != null) {
result = caculate(result, Integer.parseInt(line));
}
return result;
} catch (IOException e) {
throw new IllegalArgumentException(path + "ì íŽë¹íë íìŒìŽ ììµëë€.", e);
}
}
protected abstract int caculate(int result, int number);
protected abstract int getResult();
}
ìŽì ê° êµ¬í íŽëì€ë€ìì ì¶ì íŽëì€ë¥Œ ììíê³ ì¶ì ë©ìë륌 구ííì¬ ìê³ ëŠ¬ìŠ ë¡ì§ì ì€ì íŽì£Œë©Ž, ì¶ì íŽëì€ìì 믞늬 ì ìë í í늿 ë©ìëì ë¡ì§ì ë°ëŒ ê°êž° ë€ë¥ž ìê³ ëŠ¬ìŠì ìííê² ëë€.
ì€ë³µëë ìœëë€ì ì¶ì íŽëì€ì 몚ìëê³ ë€ë¥ž ë¶ë¶ë§ ê° íŽëì€ë€ìŽ 구ííëë¡ íì¬ ê°ë ì±ì ëìŽê³ ì€ë³µì ì ê±°ìíš ê²ìŽë€.
// ëíêž° ì°ì° ìí
class PlusFileProcessor extends FileProcessor{
public PlusFileProcessor(String path) {
super(path);
}
@Override
protected int caculate(int result, int number) {
return result += number;
}
@Override
protected int getResult() {
return 0;
}
}
// ê³±ì
ì°ì° ìí
class MultiplyFileProcessor extends FileProcessor{
public MultiplyFileProcessor(String path) {
super(path);
}
@Override
protected int caculate(int result, int number) {
return result *= number;
}
@Override
protected int getResult() {
return 1; // ê³±ì
ì ìŽê¹ê°ìŽ 0ìŽë©Ž 결곌ë 멱ë±íëê¹
}
}
class Client {
public static void main(String[] args) {
PlusFileProcessor plusFileProcessor = new PlusFileProcessor("numbers.txt");
int result1 = plusFileProcessor.process();
System.out.println(result1); // 15
MultiplyFileProcessor multiplyFileProcessor = new MultiplyFileProcessor("numbers.txt");
int result2 = multiplyFileProcessor.process();
System.out.println(result2); // 120
}
}
í 늬ì°ë ìì¹ ì€ì
í늬ì°ë ìì¹(Hollywood Principle) ìŽë ê³ ìì€ ëªšë(ì¶ìíŽëì€, ìží°íìŽì€)ì ìì¡Žíê³ ê³ ìì€ ëªšëìì ì°ëœ(ë©ìë ì€í) íëŒë ìì¹ìŽë€.
ê°ì²Ž ëŒëŠ¬ ìŽìíê² ìŒí€ê³ ì€ìŒ, ìì¡Žì±ìŽ ë³µì¡íê² êŒ¬ì¬ìë ê²ì 'ìì¡Žì± ë¶íš(dependency rot)' ëŒê³ ë¶ë¥Žëë°, í늬ì°ë ìì¹ì íì©íë©Ž ìì¡Žì± ë¶íšë¥Œ ë°©ì§í ì ìê² ëë€.
ìë° íë¡ê·žëë°ìŒë¡ ê°ëší ë§íìë©Ž, ë€íì±ì ìŽì©íŽ ê³ ìì€ì ê°ì²Ž íì ììë§ ì ë§íë©Ž ë©ìë ì€íì íëŒë ë§ìŽë€.
ë°ë¡ ìì ìœëë¡ ë€ìŽê°ë³Žì
íŽëŠ°íì§ ìì 묞ì ì ìœë â
ë€ì LowerA ì LowerB íŽëì€ê° ìê³ ê°ê° ë©ìë륌 ê°ì§ê³ ìë€ê³ íì.
class LowerA {
void print(int num) {
System.out.println(num);
}
int calculate(int n1, int n2) {
return n1 + n2;
}
}
class LowerB {
void echo(int variable) {
System.out.println(variable);
}
int operation(int n1, int n2) {
return n1 * n2;
}
}
ë©ìë 구ì±ì ììží ìŽíŽë³Žë print() ì echo() ë©ìëë ë©ìë ëª
ë§ ë€ë¥Œ ë¿ìŽì§ ë©ìë ìê·žëì²ì ë©ìë ë¡ì§ìŽ ìì í ëìŒíë€. ë°ëŒì ìŽë ì°ëŠ¬ê° ë°°ì ë ëë¡ ìì(ê³ ìì€) íŽëì€ë¡ ë¬¶ìŽ ìœë ì€ë³µì ì€ìŒ ì ìë€.
class Higher {
void print(int num) {
System.out.println(num);
}
}
class LowerA extends Higher {
int calculate(int n1, int n2) {
return n1 + n2;
}
}
class LowerB extends Higher {
int operation(int n1, int n2) {
return n1 * n2;
}
}
íŽëì€ê° ìì êŽê³ë¥Œ íì±íë©Ž ê°ì²Ž ì§í¥ì êœìž ë€íì±ì ìŽì©í ì ìê² ëë€. íì§ë§ ìì§ë 묞ì ê° ìë€. ë©ìë ëŽë¶ ë¡ì§ìŽ ë¬ëŒ íµìŒìí€ì§ 못í calculate() ì operation() ë©ìë ìŽë€.
ë§ìŒ ìŽ ëì ì€íí íìê° ìë€ë©Ž ìŽì©ì ììŽ ë€ìŽ ìºì€í ì íŽìŒ ëë€.
class Client {
public static void main(String[] args) {
// ì
ìºì€í
(ê³ ìì€ ëªšë íì
ìŒë¡ ìžì€íŽì€í)
Higher obj = new LowerA();
// ìì íŽëì€ë¡ ë©ìëê° íµìŒëìŽ ìêž°ì ë©ìë ì€íì 묞ì ìì
obj.print(1000);
// íì§ë§ ë©ìë ëŽë¶ ë¡ì§ìŽ ë€ë¥ž ê²œì° ìì íŽëì€ë¡ ë¬¶ìŽ ì€ìê° ììŽ, íì íŽëì€ì ë°ë¡ ì ìíŽìŒ ëê³ ë€ìŽìºì€í
ì íµíŽ ì€ííŽìŒ íš
((LowerA) obj).calculate(10, 20);
// ë§ìŒ ë€ë¥ž ìì íŽëì€ë¡ êµì²Ží íìê° ìë€ë©Ž,
obj = new LowerB();
// ìì ë€ìŽ ìºì€í
ì íµíŽ ì€ííŽìŒ íë€.
((LowerB) obj).operation(100, 200);
}
}
í í늿 ë©ìë íšíŽì ì ì©í ìœë âïž
íì§ë§ ì°ëŠ¬ë ëììž íšíŽì ëíŽ ë°°ì ë€.
ëë¶ë¶ ì¬ëë€ìŽ í í늿 ë©ìë íšíŽì ë°°ìžë 'í í늿 ë©ìë' ì êµíëìŽ ìê°íëë°, í í늿 ë©ìëë ìê³ ëŠ¬ìŠ ëŒëìŒ ë¿ìŽê³ , í í늿 ë©ìë íšíŽì ì§ì íµì¬ì ì¶ì íŽëì€ë¥Œ íµí ìœë íµí© ë° ê³ ìì€ ìì¡Ž ì ë ìŽë€.
abstract class Higher {
void print(int num) {
System.out.println(num);
}
abstract int calculate(int n1, int n2);
}
class LowerA extends Higher {
@Override
int calculate(int n1, int n2) {
return n1 + n2;
}
}
class LowerB extends Higher {
@Override
int calculate(int n1, int n2) {
return n1 * n2;
}
}
class Client {
public static void main(String[] args) {
// ì
ìºì€í
(ê³ ìì€ ëªšë íì
ìŒë¡ ìžì€íŽì€í)
Higher obj = new LowerA();
obj.calculate(10, 20); // 30
obj = new LowerB();
obj.calculate(100, 200); // 20000
}
}
ì 늬íìë©Ž, ììì íµíŽ ì€ë³µëë ìœë륌 ìì íŽëì€ë¡ íµìŒììŒ ìœë ì€ë³µì ì ê±°íê³ , ë©ìë ìê·žëì²ê° ê°ì§ë§ ëŽë¶ ë¡ì§ìŽ ìì íŽëì€ë§ë€ ë€ë¥ž ë¶ë¶ì ì¶ì ë©ìë륌 íµíŽ ìì íŽëì€ìì ë€íì±ìŒë¡ ë©ìë륌 ì€íí ì ìëë¡, ê³ ìì€ì íì ìŒë¡ ì ì§íëŒë ê²ìŽë€.
hook ë©ìë ìì©íêž°
íŽëŠ°íì§ ìì 묞ì ì ìœë â
ë€ì곌 ê°ìŽ 컀íŒì í찚륌 ë§ëë Coffee ì Tea íŽëì€ê° ìë€. ë°°ìŽë°ì ê°ìŽ ìœëì ì€ë³µìŽ 볎ìŽë©° ìŽë¥Œ í í늿 ë©ìë íšíŽìŒë¡ 묶ìŽì£Œë €ê³ íë€.
class Coffee {
void prepareRecipe() {
boilWater(); // "묌 ëìŽêž°"
brewCoffeeGrinds(); // "íí°ë¥Œ íµíŽ 컀íŒë¥Œ ì°ë €ëŽë ì€"
pourInCup(); // "컵ì ë°ë¥Žë ì€"
addSugarAndMilk(); // "ì€í곌 ì°ì 륌 ì¶ê°íë ì€"
}
public void boilWater() {
System.out.println("묌 ëìŽêž°");
}
public void brewCoffeeGrinds() {
System.out.println("íí°ë¥Œ íµíŽ 컀íŒë¥Œ ì°ë €ëŽë ì€");
}
public void pourInCup() {
System.out.println("컵ì ë°ë¥Žë ì€");
}
public void addSugarAndMilk() {
System.out.println("ì€í곌 ì°ì 륌 ì¶ê°íë ì€");
}
}
class Tea {
void prepareRecipe() {
boilWater(); // "묌 ëìŽêž°"
steepTeaBag(); // "찚륌 ì°ëŠ¬ë ì€"
pourInCup(); // "컵ì ë°ë¥Žë ì€"
}
public void boilWater() {
System.out.println("묌 ëìŽêž°");
}
public void steepTeaBag() {
System.out.println("찚륌 ì°ëŠ¬ë ì€");
}
public void pourInCup() {
System.out.println("컵ì ë°ë¥Žë ì€");
}
}
ë íŽëì€ë¥Œ í í늿 ë©ìë íšíŽìŒë¡ í©ì¹êž° ì ì í í늿ì ë€ë¥ž ë¶ë¶ì ìŽíŽë³Žì.
ì°ì boilWater() ì pourInCup() ë©ìë ì¬ìŽì ìë ë¶ë¶ì, Coffeeì Tea íŽëì€ìì ê³µíµì ìŒë¡ ì€íëë ë¶ë¶ìŽë, ì¶ì ë©ìëë¡ ë°ë¡ 빌ì 구ííëë¡ íë©Ž ë ê² ê°ë€.
ê·žë ë€ë©Ž Coffee íŽëì€ìë§ ì€íëë addSugarAndMilk() ë©ìë ë¶ë¶ì ìŽë»ê² í ê¹? ë°ë¡ ìŽê³³ì hook ì²ëŠ¬íì¬ Coffee ìì íŽëì€ììë§ ì€íëëë¡ íë©Ž ëë€.
í í늿 ë©ìë íšíŽì ì ì©í ìœë âïž
abstract class CaffeineBeverage {
// í
í늿 ë©ìë
final void prepareRecipe() {
boilWater(); // "묌 ëìŽêž°"
brew();
pourInCup(); // "컵ì ë°ë¥Žë ì€"
if (customerWantsCondiments()) {
addCondiments();
}
}
abstract void brew();
abstract void addCondiments();
// hook ë©ìë
boolean customerWantsCondiments() {
return false;
}
public void boilWater() {
System.out.println("묌 ëìŽêž°");
}
public void pourInCup() {
System.out.println("컵ì ë°ë¥Žë ì€");
}
}
class Coffee extends CaffeineBeverage {
public void brew() {
System.out.println("íí°ë¥Œ íµíŽ 컀íŒë¥Œ ì°ë €ëŽë ì€");
}
public void addCondiments() {
System.out.println("ì€í곌 ì°ì 륌 ì¶ê°íë ì€");
}
boolean customerWantsCondiments() {
String answer = "";
System.out.print("컀íŒì ì°ì ì ì€íì ë£ìŽ ë늎ê¹ì? (y/n) : ");
try (BufferedReader in = new BufferedReader(new InputStreamReader(System.in))) {
answer = in.readLine();
} catch (IOException ioe) {
System.out.println("IO ì€ë¥");
}
if (answer.toLowerCase().startsWith("y")) {
return true;
} else {
return false;
}
}
}
class Tea extends CaffeineBeverage {
public void brew() {
System.out.println("찚륌 ì°ëŠ¬ë ì€");
}
public void addCondiments() {}
}
ìì hook ë©ìë ìí ìž customerWantsCondiments()ì 구íë¶ë¥Œ ììží 볎멎, ëšì true / false 늬íŽì ë ëì ê·ž ìŽì ì ì¬ì©ìë¡ë¶í° ì
ë ¥ê°ì ë°ë ë±ì ì¶ê°ì ìž ë¡ì§ì 구ì±í 걞 볌 ì ìë€.
ìŽë°ììŒë¡ hook ë©ìë륌 ì¢ ë ë€ìíê² ìì©í ì ìë€ë ì ì 볎ì¬ì£Œêž° ìíŽ ì륌 ë€ìë€.
class client {
public static void main(String[] args) {
CaffeineBeverage coffee = new Coffee();
System.out.println("ì»€íŒ ë§ëëì€...");
coffee.prepareRecipe();
System.out.println("\n--------------------------------\n");
CaffeineBeverage tea = new Tea();
System.out.println("íì°š ë§ëëì€...");
tea.prepareRecipe();
}
}
ì€ë¬Žìì ì°Ÿì볎ë Template Method íšíŽ
Java
java.io.InputStream,java.io.OutputStream,java.io.Reader,java.io.Writerì ìŒë° ë©ìë륌 íì íŽëì€ê° ì¬ì ìjava.util.AbstractList,java.util.AbstractSet,java.util.AbstractMapì ìŒë° ë©ìë륌 íì íŽëì€ê° ì¬ì ìjavax.servlet.http.HttpServletì 몚ëdoXXX()ë©ìëë Ʞ볞ì ìŒë¡ ìëµì HTTP 405 "Method Not Allowed" ëŠ¬íŽ ìœë륌 볎ëŽêž° ë묞ì ìŽë€ì ììíì¬ ì¬ì ì íì¬ ì¬ì©íë€.
HttpServlet
ì°ëŠ¬ê° HttpServlet íŽëì€ë¥Œ ìì íê³ doGet() 곌 doPost() ë©ìë륌 ì€ë²ëŒìŽë íë©Ž, ìì ìëžëŠ¿ ê°ì²Žìì í
í늿 ë¡ì§ì ìííë€ê° doGet, doPost 륌 ížì¶íŽìŒ í ë íì íŽëì€ë¥Œ ì°žì¡°íì¬ ë©ìë륌 ì€ííë€.
public class MyHello extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
AbstractMap
AbstractMap<K,V> íŽëì€ì ì ì ëìŽìë get() ë©ìë륌 ìŽë¥Œ ììíë HashMap, TreeMap ë± ìëžíŽëì€ìì ì€ë²ëŒìŽëíì¬ ìì ë§ì 구í ë°©ë²ìŒë¡ ë€ë¥ž ë°©ììŒë¡ ì¬ì ì íê³ ìë ê²ì 볌 ì ìë€. êŒ ì¶ì ë©ìë륌 ì¬ì ì íŽìŒëëê² ìëëŒ ìŒë° ë©ìëë í
í늿ì ê³ ì ëìŽ ì€íëë ê²ìŽëŒë©Ž ì€ë²ëŒìŽë©íì¬ ì¬ì ì íë©Ž 곧 ìê³ ëŠ¬ìŠ ë³íê° ëêž° ë묞ìŽë€.
AbstractMap<K,V> ì get() ë©ìë
public V get(Object key) {
Iterator<Entry<K,V>> i = entrySet().iterator();
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return e.getValue();
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return e.getValue();
}
}
return null;
}
HashMap<K,V> extends AbstractMap<K,V> ì get() ë©ìë
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
TreeMap<K,V> extends AbstractMap<K,V> ì get() ë©ìë
public V get(Object key) {
Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);
}
Spring Framework
Configuration
WebSecurityConfigureAdapter íŽëì€ë¥Œ ìì íê³ configure() ë©ìë륌 ì€ë²ëŒìŽëíë©Ž, ì€íë§ íë ììí¬ì Config ë¡ì§ì ê±°ëí ìê³ ëŠ¬ìŠ ì€ ìŒë¶ë¥Œ 구ííê² ëë ê²ìŽë€.
public class TemplateInSpring {
public static void main(String[] args) {
// TODO í
í늿-ìœë°± íšíŽ
// JdbcTemplate
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.execute("insert");
// RestTemplate
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.set("X-COM-PERSIST", "NO");
headers.set("X-COM-LOCATION", "USA");
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<String> responseEntity = restTemplate
.exchange("http://localhost:8080/users", HttpMethod.GET, entity, String.class);
}
@Configuration
class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll();
}
}
}
êŽë šë ëììž íšíŽ ì¢ ë¥
Strategy vs Temaplate Method
íšíŽ ì ì¬ì
ì ëµ íšíŽê³Œ í í늿 ë©ìë íšíŽì ìê³ ëŠ¬ìŠì ëì ë°ëŒ ì ì©íë€ë 컚ì ìŒë¡ìš, ëìŽ ê³µíµì ì ê°ì§ê³ ìë€.
ì ëµ ë° í í늿 ë©ìë íšíŽì ê°ë°©í íì ìì¹ì 충족íê³ ìœë륌 ë³ê²œíì§ ìê³ ìíížìšìŽ 몚ëì ìœê² íì¥í ì ìëë¡ íë ë° ì¬ì©í ì ìë€.
íšíŽ ì°šìŽì
ì ëµ íšíŽì í©ì±(composition)ì íµíŽ íŽê²°ì± ì ê°êµ¬íë©°, í í늿 ë©ìë íšíŽì ìì(inheritance)ì íµíŽ íŽê²°ì± ì ì ìíë€. ê·žëì ì ëµ íšíŽì íŽëŒìŽìžížì ê°ì²Ž ê°ì ê²°í©ìŽ ëìší ë°ë©Ž, í í늿 ë©ìë íšíŽììë ë 몚ëìŽ ë ë°ì íê² ê²°í©ëë€. (ê²°í©ëê° ëìŒë©Ž ìì¢ì)
ì ëµ íšíŽììë ëë¶ë¶ ìží°íìŽì€ë¥Œ ì¬ì©íì§ë§, í í늿 ë©ìë íšíŽìë ì£Œë¡ ì¶ì íŽëì€ë 구첎ì ìž íŽëì€ë¥Œ ì¬ì©íë€.
ì ëµ íšíŽììë ì 첎 ì ëµ ìê³ ëŠ¬ìŠì ë³ê²œí ì ìì§ë§, í í늿 ë©ìë íšíŽììë ìê³ ëŠ¬ìŠì ìŒë¶ë§ ë³ê²œëê³ ëëšžì§ë ë³ê²œëì§ ìì ìíë¡ ì ì§ëë€. (í í늿ì ì¢ ì)
ë°ëŒì ëšìŒ ììë§ìŽ ê°ë¥í ìë°ìì ìì ì íìŽ ìë í í늿 ë©ìë íšíŽë³Žë€ë, ë€ìíê² ë§ì ì ëµì implements í ì ìë ì ëµ íšíŽìŽ íì ìì ë§ìŽ ì¬ì©ëë ížìŽë€.
# ì°žê³ ìë£
ìœë©ìŒë¡ íìµíë GoFì ëììž íšíŽ - 백Ʞì
https://refactoring.guru/design-patterns/template-method
https://reactiveprogramming.io/blog/en/design-patterns/template-method
https://blogshine.tistory.com/13
https://yaboong.github.io/design-pattern/2018/09/27/template-method-pattern/