March 10, 2022
어떤 작업을 처리하는 부분을 서브클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내역을 바꾸는 패턴이다.
The intent of the template method is to define the overall structure of the operation, while allowing subclasses to refine, or redefine certain steps. (템플릿 메서드 패턴의 목적은 작업의 전체 구조를 정의하는 동시에 하위 클래스가 특정 단계를 세분화 하거나 재정의 할 수 있도록 하는 것이다.)
먼저 Game를 구현하기 위한 메서드들을 abstract class 로 미리 만들어 놓는다.
abstract class Game {
protected int playersCount;
abstract void initializeGame();
abstract void makePlay(int player);
abstract boolean endOfGame();
abstract void printWinner();
}
이후 실행을 위한 template method를 정의한다.
abstract class Game {
protected int playersCount;
abstract void initializeGame();
abstract void makePlay(int player);
abstract boolean endOfGame();
abstract void printWinner();
final void playOneGame(int playersCount){
this.playersCount = playersCount;
initializeGame();
int j = 0;
while(!endOfGame()){
makePlay(j);
j = (j+1) % playersCount;
}
printWinner();
}
}
이후 위에서 선언한 게임을 위한 template를 extend한 각각의 게임을 만든다.
Monopoly 게임
public class Monopoly extends Game {
@Override
void initializeGame() {
// initialize money
}
@Override
void makePlay(int player) {
// Process one turn of player
}
@Override
boolean endOfGame() {
// return true if game is over according to Monopoly rules
return false;
}
@Override
void printWinner() {
// Display who wone
}
}
체스게임
public class Chess extends Game {
@Override
void initializeGame() {
// initialize money
}
@Override
void makePlay(int player) {
// Process one turn of player
}
@Override
boolean endOfGame() {
// return true if game is over according to Monopoly rules
return false;
}
@Override
void printWinner() {
// Display who wone
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Network network = null;
System.out.print("Input user name: ");
String userName = reader.readLine();
System.out.print("Input password: ");
String password = reader.readLine();
// Enter the message.
System.out.print("Input message : ");
String message = reader.readLine();
System.out.println("\nChoose social network for posting message.\n" +
"1 - Facebook\n" +
"2 - Twitter");
int choice = Integer.parseInt(reader.readLine());
// 선택되는 케이스에 따라 Facebook 또는 Twitter를 실행시켜주는 메서드이다.
if (choice == 1) {
network = new Facebook(userName, password);
} else if (choice == 2) {
network = new Twitter(userName, password);
}
network.post(message);
}
}
public abstract class Network {
String userName;
String password;
Network() {}
// post 메서드가 실질적으로 현재 실행되는 템플릿메서드 패턴에서 fixed logic 부분이다.
public boolean post(String message) {
// 로그인이 정상적으로 된다면 sendData 를 호출해준다.
if (logIn(this.userName, this.password)) {
// Send the post data.
boolean result = sendData(message.getBytes());
logOut();
return result;
}
return false;
}
// 이때 logIn과 sendData, logOut은 추상메서드로 나중에 구현클래스마다 기능은 다르다.
// 하지만 위에 post 메서드처럼 fixed logic의 부분으로 사용된다.
// 따라서 전체적인 코드의 흐름은 같다.
abstract boolean logIn(String userName, String password);
abstract boolean sendData(byte[] data);
abstract void logOut();
}
Facebook과 Twitter는 서로 다른 방법으로 각각 Network클래스의 abstract method를 구현하였지만, fixed로직에 의해 똑같은 flow로 실행된다.
public class Facebook extends Network {
public Facebook(String userName, String password) {
this.userName = userName;
this.password = password;
}
// 로그인 메서드지만 사실상 무엇인가를 확인하는 기능은 없다.
// 마치 로그인을 하는 과정처럼 그냥 console에서 보여주는 기능이다.
public boolean logIn(String userName, String password) {
System.out.println("\nChecking user's parameters");
System.out.println("Name: " + this.userName);
System.out.print("Password: ");
for (int i = 0; i < this.password.length(); i++) {
System.out.print("*");
}
// 약 5초동안 기다리도록 wait를 하는 기능을 한다.
simulateNetworkLatency();
System.out.println("\n\nLogIn success on Facebook");
return true;
}
// 마치 message를 보내는 것처럼 Console을 출력해주는 메서드이다.
public boolean sendData(byte[] data) {
boolean messagePosted = true;
if (messagePosted) {
System.out.println("Message: '" + new String(data) + "' was posted on Facebook");
return true;
} else {
return false;
}
}
public void logOut() {
System.out.println("User: '" + userName + "' was logged out from Facebook");
}
private void simulateNetworkLatency() {
try {
int i = 0;
System.out.println();
while (i < 10) {
System.out.print(".");
Thread.sleep(500);
i++;
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
public class Twitter extends Network {
public Twitter(String userName, String password) {
this.userName = userName;
this.password = password;
}
public boolean logIn(String userName, String password) {
System.out.println("\nChecking user's parameters");
System.out.println("Name: " + this.userName);
System.out.print("Password: ");
for (int i = 0; i < this.password.length(); i++) {
System.out.print("*");
}
simulateNetworkLatency();
System.out.println("\n\nLogIn success on Twitter");
return true;
}
public boolean sendData(byte[] data) {
boolean messagePosted = true;
if (messagePosted) {
System.out.println("Message: '" + new String(data) + "' was posted on Twitter");
return true;
} else {
return false;
}
}
public void logOut() {
System.out.println("User: '" + userName + "' was logged out from Twitter");
}
private void simulateNetworkLatency() {
try {
int i = 0;
System.out.println();
while (i < 10) {
System.out.print(".");
Thread.sleep(500);
i++;
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}