Template Mehtod
模板方法模式是一种行为设计模式, 它在超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。
这里是一些核心 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, 所有默认发送 HTTP 405 “方法不允许” 错误响应的
doXXX()
方法。 你可随时对其进行重写。
识别方法: 模版方法可以通过行为方法来识别, 该方法已有一个在基类中定义的 “默认” 行为。
模板方法模式结构
样例
在本例中, 模版方法模式定义了一个可与社交网络协作的算法。 与特定社交网络相匹配的子类将根据社交网络所提供的 API 来实现这些步骤。
基础社交网络类
package behavioral.templatemethod;
public abstract class Network {
String userName;
String password;
Network() {
}
/**
* 将信息发送给到网络
*
* @param message
* @return
*/
public boolean post(String message) {
if (logIn(this.userName, this.password)) {
boolean result = sendData(message.getBytes());
logOut();
return result;
}
return false;
}
public abstract boolean logIn(String userName, String password);
public abstract boolean sendData(byte[] data);
public abstract void logOut();
}
具体社交网络
package behavioral.templatemethod;
public class Facebook extends Network {
public Facebook(String userName, String password) {
this.userName = userName;
this.password = password;
}
@Override
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 Facebook");
return true;
}
@Override
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;
}
}
@Override
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();
}
}
}
另一个社交网络
package behavioral.templatemethod;
public class Twitter extends Network {
public Twitter(String userName, String password) {
this.userName = userName;
this.password = password;
}
@Override
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;
}
@Override
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;
}
}
@Override
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();
}
}
}
测试
package behavioral.templatemethod;
public class Twitter extends Network {
public Twitter(String userName, String password) {
this.userName = userName;
this.password = password;
}
@Override
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;
}
@Override
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;
}
}
@Override
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();
}
}
}
/**
* Input user name: hua
* Input password: qqq
* Input message: hello
*
* Choose social network for posting message.
* 1 - Facebook
* 2 - Twitter
* 1
*
* Checking user's parameters
* Name: hua
* Password: ***
* ..........
*
* LogIn success on Facebook
* Message: 'hello' was posted on Facebook
* User: 'hua' was logged out from Facebook
*/
适用场景
-
当你只希望客户端扩展某个特定算法步骤, 而不是整个算法或其结构时, 可使用模板方法模式。
模板方法将整个算法转换为一系列独立的步骤, 以便子类能对其进行扩展, 同时还可让超类中所定义的结构保持完整。
-
当多个类的算法除一些细微不同之外几乎完全一样时, 你可使用该模式。 但其后果就是,只要算法发生变化, 你就可能需要修改所有的类。
在将算法转换为模板方法时, 你可将相似的实现步骤提取到超类中以去除重复代码。 子类间各不同的代码可继续保留在子类中。
实现方式
- 分析目标算法, 确定能否将其分解为多个步骤。 从所有子类的角度出发, 考虑哪些步骤能够通用, 哪些步骤各不相同。
- 创建抽象基类并声明一个模板方法和代表算法步骤的一系列抽象方法。 在模板方法中根据算法结构依次调用相应步骤。 可用
final
最终修饰模板方法以防止子类对其进行重写。 - 虽然可将所有步骤全都设为抽象类型, 但默认实现可能会给部分步骤带来好处, 因为子类无需实现那些方法。
- 可考虑在算法的关键步骤之间添加钩子。
- 为每个算法变体新建一个具体子类, 它必须实现所有的抽象步骤, 也可以重写部分可选步骤。
模板方法优点
- 你可仅允许客户端重写一个大型算法中的特定部分, 使得算法其他部分修改对其所造成的影响减小。
- 你可将重复代码提取到一个超类中。
模板方法缺点
- 部分客户端可能会受到算法框架的限制。
- 通过子类抑制默认步骤实现可能会导致违反_里氏替换原则_。
- 模板方法中的步骤越多, 其维护工作就可能会越困难。