常用设计模式有23中,分为:
创建型模式(主要用于创建对象)
1、单例模式 2、工厂方法模式 3、抽象工厂模式 4、建造者模式 5、原型模式行为型模式 (主要用于描述对象或类是怎样交互和怎样分配职责)
1、模板方法模式 2、中介者模式 3、命令模式 4、责任链模式 5、策略模式 6、迭代器模式
7、观察者模式 8、备忘录模式 9、访问者模式 10、状态模式 11、解释器模式结构型模式(主要用于处理类或对象的组合)
1、代理模式 2、装饰模式 3、适配器模式 4、组合模式 5、外观模式(门面模式) 6、享元模式 7、桥梁模式
模板方法模式
其定义如下:
一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
其中,AbstractClass叫做抽象模板,它的方法分为两类:
● 基本方法
基本方法也叫做基本操作,是由子类实现的方法,并且在模板方法被调用。
● 模板方法
可以有一个或几个,一般是一个具体方法,也就是一个框架,实现对基本方法的调度,完成固定的逻辑。
注意 为了防止恶意的操作,一般模板方法都加上final关键字,不允许被覆写。
通用代码:
AbstractClass如代码清单10-6所示。
<span style="font-size:18px;">代码清单10-6 抽象模板类
public abstract class AbstractClass {
//基本方法
protected abstract void doSomething();
//基本方法
protected abstract void doAnything();
//模板方法
public void templateMethod(){
/*
* 调用基本方法,完成相关的逻辑
*/
this.doAnything();
this.doSomething();
}
}</span>
具体模板如代码清单10-7所示。
<span style="font-size:18px;">代码清单10-7 具体模板类
public class ConcreteClass1 extends AbstractClass {
//实现基本方法
protected void doAnything() {
//业务逻辑处理
}
protected void doSomething() {
//业务逻辑处理
}
}</span>
<span style="font-size:18px;">public class ConcreteClass2 extends AbstractClass {
//实现基本方法
protected void doAnything() {
//业务逻辑处理
}
protected void doSomething() {
//业务逻辑处理
}
} </span>
场景类如代码清单10-8所示。
<span style="font-size:18px;">public class Client {
public static void main(String[] args) {
AbstractClass class1 = new ConcreteClass1();
AbstractClass class2 = new ConcreteClass2();
//调用模板方法
class1.templateMethod();
class2.templateMethod();
}</span>
注意 抽象模板中的基本方法尽量设计为protected类型,符合迪米特法则,不需要暴露的属性或方法尽量不要设置为protected类型。实现类若非必要,尽量不要扩大父类中的访问权限。
优点
● 封装不变部分,扩展可变部分
● 提取公共部分代码,便于维护
● 行为由父类控制,子类实现
缺点
- 子类对父类产生了影响
- 这在复杂的项目中,会带来代码阅读的难度,而且也会让新手产生不适感。
使用场景
● 多个子类有公有的方法,并且逻辑基本相同时。
● 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
● 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数(见“模板方法模式的扩展”)约束其行为。
实例 制造悍马
注意,抽象类HummerModel中的run()方法,由抽象方法变更为实现方法,其源代码如代码清单10-4所示。
<span style="font-size:18px;">代码清单10-4 抽象悍马模型
public abstract class HummerModel {
/*
* 首先,这个模型要能发动起来,别管是手摇发动,还是电力发动,反正
* 是要能够发动起来,那这个实现要在实现类里了
*/
public abstract void start();
//能发动,还要能停下来,那才是真本事
public abstract void stop();
//喇叭会出声音,是滴滴叫,还是哔哔叫
public abstract void alarm();
//引擎会轰隆隆地响,不响那是假的
public abstract void engineBoom();
//那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
public void run(){
//先发动汽车
this.start();
//引擎开始轰鸣
this.engineBoom();
//然后就开始跑了,跑的过程中遇到一条狗挡路,就按喇叭
this.alarm();
//到达目的地就停车
this.stop();
}
}</span>
在抽象的悍马模型上已经定义了run()方法的执行规则,先启动,然后引擎立刻轰鸣,中间还要按一下喇叭,制造点噪声(要不就不是名车了)。然后停车,它的两个具体实现类就不需要实现run()方法了,
场景类实现的任务就是把生产出的模型展现给客户,其源代码如代码清单10-5所示。
<span style="font-size:18px;">代码清单10-5 场景类
public class Client {
public static void main(String[] args) {
//XX公司要H1型号的悍马
HummerModel h1 = new HummerH1Model();
//H1模型演示
h1.run();
}
} </span>
扩展
出H1型号的悍马喇叭想让它响就响,H2型号的喇叭不要有声音,
类图改动似乎很小,在抽象类HummerModel中增加了一个实现方法isAlarm,确定各个型号的悍马是否需要声音,由各个实现类覆写该方法,同时其他的基本方法由于不需要对外提
供访问,
因此也设计为
protected类型
其源代码如 下:
<span style="font-size:18px;">代码清单10-9 扩展后的抽象模板类
public abstract class HummerModel {
/*
* 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正
* 是要能够发动起来,那这个实现要在实现类里了
*/
protected abstract void start();
//能发动,还要能停下来,那才是真本事
protected abstract void stop();
//喇叭会出声音,是滴滴叫,还是哔哔叫
protected abstract void alarm();
//引擎会轰隆隆的响,不响那是假的
protected abstract void engineBoom();
//那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
final public void run() {
//先发动汽车
this.start();
//引擎开始轰鸣
this.engineBoom();
//要让它叫的就是就叫,喇嘛不想让它响就不响
if(this.isAlarm()){
this.alarm();
}
//到达目的地就停车
this.stop();
}
//钩子方法,默认喇叭是会响的
protected boolean isAlarm(){
return true;
}
}</span>
在抽象类中,isAlarm是一个实现方法。其作用是模板方法根据其返回值决定是否要响 喇叭,子类可以覆写该返回值,由于 H1型号的喇叭是想让它响就响,不想让它响就不响,
由人控制,其源代码如代码清单10-10所示。
<span style="font-size:18px;">代码清单10-10 扩展后的H1悍马
public class HummerH1Model extends HummerModel {
private boolean alarmFlag = true; //要响喇叭
protected void alarm() {
System.out.println("悍马H1鸣笛...");
}
protected void engineBoom() {
System.out.println("悍马H1引擎声音是这样的...");
}
protected void start() {
System.out.println("悍马H1发动...");
}
protected void stop() {
System.out.println("悍马H1停车...");
}
protected boolean isAlarm() {
return this.alarmFlag;
}
//要不要响喇叭,是由客户来决定的
public void setAlarm(boolean isAlarm){
this.alarmFlag = isAlarm;
}
}</span>
只要调用H1型号的悍马,默认是有喇叭响的,当然你可以不让喇叭响,通过isAlarm(false)就可以实现。
H2型号的悍马是没有喇叭声响的,其源代码如代码清单10-11所
示。
<span style="font-size:18px;">代码清单10-11 扩展后的H2悍马
public class HummerH2Model extends HummerModel {
protected void alarm() {
System.out.println("悍马H2鸣笛...");
}
protected void engineBoom() {
System.out.println("悍马H2引擎声音是这样的...");
}
protected void start() {
System.out.println("悍马H2发动...");
}
protected void stop() {
System.out.println("悍马H2停车...");
}
//默认没有喇叭的
protected boolean isAlarm() {
return false;
}
}</span>
H2型号的悍马设置isAlarm()的返回值为false,也就是关闭了喇叭功能。
场景类代码如代码清单10-12所示。
<span style="font-size:18px;">代码清单10-12 扩展后的场景类
public class Client {
public static void main(String[] args) throws IOException {
System.out.println("-------H1型号悍马--------");
System.out.println("H1型号的悍马是否需要喇叭声响?0-不需要 1-需要");
String type=(new BufferedReader(new InputStreamReader(System.in))).readLine();
HummerH1Model h1 = new HummerH1Model();
if(type.equals("0")){
h1.setAlarm(false);
}
h1.run();
System.out.println("\n-------H2型号悍马--------");
HummerH2Model h2 = new HummerH2Model();
h2.run();
}
}</span>
H1型号的悍马 运行是需要交互的,首先,要求输入 是否有声音
在我们的抽象类中isAlarm的返回值就是影响了模板方法的执行结果,该方法就叫做钩子方法(Hook Method)。有了钩子方法模板方法模式才算完美,
模板方法模式就是在模板方法中按照一定的规则和顺序调用基本方法,具体到前面那个例子,就是run()方法按照规定的顺序(先调用start(),然后再调用engineBoom(),再调用
alarm(),最后调用stop())调用本类的其他方法,并且由isAlarm()方法的返回值确定run()中的
执行顺序变更。
在软件开发过程中,如果相同的一段代码复制过两次,就需要对设计产生怀疑,架构师要明确地说明为什么相同的逻辑要出现两次或更多次。