前言
GOF-23模式分类,传统分为创建型,结构型,行为型。
- 创建型模式:用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF 中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。
- 结构型模式:用于描述如何将类或对象按某种布局组成更大的结构,GoF 中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。
- 行为型模式:用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。GoF 中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式。
李建忠老师给出了他的分类方式:
这样的分类方式,更加的细致。我首先要整理的就是组件协作中的Template Method。
Template Method
定义:定义一个操作中的算法骨架(稳定),而将一些步骤延迟(变化)到子类中。TemplateMethod使得子类可以不改变(复用)一个算法的结构也可重定义(override重写)该算法特定步骤。
该模式的主要优点如下。
- 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
- 它在父类中提取了公共的部分代码,便于代码复用。
- 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
该模式的主要缺点如下。
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
一些方法通用,却在每一个子类都重新写了这一方法。比如后面的试卷,每个学生都要用,不如进行进行抽离出来,形成一个模版。
附加:这里的延迟涉及一个在之前所说的重构关键技法中的晚绑定,就是一种“你别来调用我,我来调用你”。这个“早晚”,就是调用的代码出现早晚。两种选择,你写完整个流程,去调用别人已经写好的接口(这个代码在你之前就有了),就是早绑定。第二种,别人有整个流程,规定什么操作可以重构,你再去重构对应的操作,让他来调用你写好的代码,被调用的代码(你的代码)是在原代码之后才出现的就是一个晚绑定。就是被调用代码的出现时间。
模版结构
使用步骤
- 创建抽象类,列出算法骨架。以参考文献二中案例为例。
package com.lwx.template_method;
/**
* Created with IntelliJ IDEA.
* Description: 考题试卷
* User: lwx
* Date: 2019-03-11
* Time: 22:39
*/
public abstract class TestPaper {
private void testQuestion1(){
System.out.println("杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是:A.球磨铸铁 B.马口铁 C.高速合金钢 D。碳素纤维");
System.out.println("学生答案:" + answer1());
}
private void testQuestion2(){
System.out.println("杨过、程英、陆无双铲除了情花造成:A.使这种植物不再害人 B.使一种珍稀物种灭绝 C.破坏了那个生物圈的生态平衡 D.造成该地区沙漠化");
System.out.println("学生答案:" + answer2());
}
private void testQuestion3(){
System.out.println("蓝凤凰致使华山师徒、核谷六仙呕吐不止,如果你是大夫,会给他们开什么药:A.阿匹斯林 B.牛黄解毒片 " +
"C.氟哌酸 D.让他们喝大量的生牛奶 E.以上全不对");
System.out.println("学生答案:" + answer3());
}
public void answerQuestion() {
testQuestion1();
testQuestion2();
testQuestion3();
}
abstract String answer1();
abstract String answer2();
abstract String answer3();
}
- 实现骨架中的抽象方法。这里是学生A与学生B 对试卷进行作答
package com.lwx.template_method;
/**
* Created with IntelliJ IDEA.
* Description:
* User: lwx
* Date: 2019-03-11
* Time: 22:49
*/
public class StudentAPaper extends TestPaper {
@Override
String answer1() {
return "B";
}
@Override
String answer2() {
return "A";
}
@Override
String answer3() {
return "C";
}
}
package com.lwx.template_method;
/**
* Created with IntelliJ IDEA.
* Description:
* User: lwx
* Date: 2019-03-11
* Time: 22:49
*/
public class StudentBPaper extends TestPaper {
@Override
String answer1() {
return "C";
}
@Override
String answer2() {
return "B";
}
@Override
String answer3() {
return "A";
}
}
- 模版结构中的客户类,也就是主方法。
package com.lwx.template_method;
/**
* Created with IntelliJ IDEA.
* Description:
* User: lwx
* Date: 2019-03-11
* Time: 22:00
*/
public class Test {
public static void main(String[] args) {
TestPaper testPaper = null;
System.out.println("学生A答题");
testPaper = new StudentAPaper();
testPaper.answerQuestion();
System.out.println("----------------------------");
System.out.println("学生B答题");
testPaper = new StudentBPaper();
testPaper.answerQuestion();
}
}
- 运行效果
由于试卷是一样的,所以完全可以抽象成父类,然后在子类中去填写答案,这样就是通过把不变的行为搬移到超类,去除子类中的重复代码,这也是模板方法的特点。
用设计模式就是要看见代码中的变与不变,极大可能的实现复用。
模版方法:某个流程是固定的,只存在中间某步的具体操作的细微差别。比如说:去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。
参考文献
[1]http://c.biancheng.net/view/1376.html
[2]https://www.cnblogs.com/yijinqincai/p/10513843.html