2.10 模板模式
(1)概念
模板模式通常又叫模板方法模式(Template Method Pattern)是指定义一个算法的骨
架,并允许子类为一个或者多个步骤提供实现。模板方法使得子类可以在不改变算法结
构的情况下,重新定义算法的某些步骤,属于行为性设计模式。
(2)适用场景
1、一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
2、各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复。
3、通常,在微服务架构中一般会建立一个common的module,里面抽取公共的类,然后其他的module来是这个module的子module
(3)代码示例
整体的类图
我们通常上网课看视频,通常网课的步骤是发布预习资料–>制作课件 PPT–>在线直播–> 提 交 课 堂 笔 记 --> 提 交 源 码 --> 布 置 作 业 --> 检 查 作 业 。
首先建立一个抽象类NetworkCourse
- NetworkCourse 类
package com.alibaba.design.templatemethodpattern.course; /** * @author zhouyanxiang * @create 2020-07-2020/7/29-19:50 */ public abstract class NetworkCourse { public final void createCourse(){ //1、发布预习资料 this.postPreResource(); //2、制作PPT课件 this.createPPT(); //3、在线直播 this.liveVideo(); //4、提交课件、课堂笔记 this.postNote(); //5、提交源码 this.postSource(); //6、布置作业,有些课是没有作业,有些课是有作业的 //如果有作业的话,检查作业,如果没作业,完成了 if(needHomeWork()){ checkHomeWork(); } } abstract void checkHomeWork(); //钩子方法:实现流程的微调 protected boolean needHomeWork(){return false;} final void postSource(){ System.out.println("提交源代码"); } final void postNote(){ System.out.println("提交课件和笔记"); } final void liveVideo(){ System.out.println("直播授课"); } final void createPPT(){ System.out.println("创建备课PPT"); } final void postPreResource(){ System.out.println("分发预习资料"); } }
上述的钩子方法是设计可以进行流程的微调的,因为通常有些指标不是必须的,比如布置作业,有时候布置了有时候不用布置,因此可以这样来设计。
- JavaCourse类
package com.alibaba.design.templatemethodpattern.course; /** * @author zhouyanxiang * @create 2020-07-2020/7/29-19:51 */ public class JavaCourse extends NetworkCourse{ @Override void checkHomeWork() { System.out.println("检查java的作业"); } }
- PythonCourse类
package com.alibaba.design.templatemethodpattern.course; /** * @author zhouyanxiang * @create 2020-07-2020/7/29-19:52 */ public class PythonCourse extends NetworkCourse { private boolean needHomeworkFlag = false; public PythonCourse(boolean needHomeworkFlag){ this.needHomeworkFlag = needHomeworkFlag; } @Override void checkHomeWork() { System.out.println("检查Python的作业"); } @Override protected boolean needHomeWork() { return this.needHomeworkFlag; } }
- 客户端测试类
package com.alibaba.design.templatemethodpattern.test; import com.alibaba.design.templatemethodpattern.course.JavaCourse; import com.alibaba.design.templatemethodpattern.course.NetworkCourse; import com.alibaba.design.templatemethodpattern.course.PythonCourse; /** * @author zhouyanxiang * @create 2020-07-2020/7/29-19:53 */ public class NetworkCourseTest { public static void main(String[] args) { System.out.println("---Java架构师课程---"); NetworkCourse javaCourse = new JavaCourse(); javaCourse.createCourse(); System.out.println("---Python课程---"); NetworkCourse pythonCourse = new PythonCourse(true); pythonCourse.createCourse(); } }
(4)模板模式在源码中的体现
在AbstractList类中的get方法就是一个抽象的方法
在AbstractList类的继承类中的ArrayList中对get方法进行了重写
(5)模板模式的优缺点
- 优点:
1、利用模板方法将相同处理逻辑的代码放到抽象父类中,可以提高代码的复用性。
2、将不同的代码不同的子类中,通过对子类的扩展增加新的行为,提高代码的扩展性。
3、把不变的行为写在父类上,去除子类的重复代码,提供了一个很好的代码复用平台,
符合开闭原则。 - 缺点:
1、类数目的增加,每一个抽象类都需要一个子类来实现,这样导致类的个数增加。
2、类数量的增加,间接地增加了系统实现的复杂度。
3、继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍。
2.11 空对象模式 (不属于二十三中设计模式中的,这里是扩充一下)
(1)概念
在空对象模式(Null Object Pattern)中,一个空对象取代 NULL 对象实例的检查。Null 对象不是检查空值,而是反应一个不做任何动作的关系。这样的 Null 对象也可以在数据不可用的时候提供默认的行为。
在空对象模式中,我们创建一个指定各种要执行的操作的抽象类和扩展该类的实体类,还创建一个未对该类做任何实现的空对象类,该空对象类将无缝地使用在需要检查空值的地方。
(2)适用场景
满足下列条件时可以使用空对象模式
- 一个对象需要一个协作对象,但并无具体的协作对象
- 协作对象不需要做任何事情
- 需要大量对空值进行判断的时候;
(3)代码示例
我们将创建一个定义操作(在这里,是客户的名称)的 AbstractCustomer 抽象类,和扩展了 AbstractCustomer 类的实体类。工厂类 CustomerFactory 基于客户传递的名字来返回 RealCustomer 或 NullCustomer 对象。
NullPatternDemo,我们的演示类使用 CustomerFactory 来演示空对象模式的用法。
创建一个抽象类。
package com.alibaba.design.nullobjectpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/2-8:14 */ public abstract class AbstractCustomer { protected String name; public abstract boolean isNil(); public abstract String getName(); }
创建扩展了上述类的实体类。
package com.alibaba.design.nullobjectpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/2-8:16 */ public class NullCustomer extends AbstractCustomer { @Override public boolean isNil() { return true; } @Override public String getName() { return "Not Available in Customer Database"; } }
package com.alibaba.design.nullobjectpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/2-8:15 */ public class RealCustomer extends AbstractCustomer { public RealCustomer(String name) { this.name = name; } @Override public boolean isNil() { return false; } @Override public String getName() { return name; } }
创建 CustomerFactory 类。
package com.alibaba.design.nullobjectpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/2-8:17 */ public class CustomerFactory { public static final String[] names = {"Rob", "Joe", "Julie"}; public static AbstractCustomer getCustomer(String name){ for (int i = 0; i < names.length; i++) { if (names[i].equalsIgnoreCase(name)){ return new RealCustomer(name); } } return new NullCustomer(); } }
客户端测试类
package com.alibaba.design.nullobjectpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/2-8:17 */ public class NullPatternDemo { public static void main(String[] args) { AbstractCustomer customer1 = CustomerFactory.getCustomer("Rob"); AbstractCustomer customer2 = CustomerFactory.getCustomer("Bob"); AbstractCustomer customer3 = CustomerFactory.getCustomer("Julie"); AbstractCustomer customer4 = CustomerFactory.getCustomer("Laura"); System.out.println("Customers"); System.out.println(customer1.getName()); System.out.println(customer2.getName()); System.out.println(customer3.getName()); System.out.println(customer4.getName()); } }
输出结果:
(4)该模式在源码中的体现
commons-lang3 jar包里面的StringUtils
包含了isNotEmpty()就是对String类型的对象的空值判断,我们通常可以用他对字符串进行空值判断处理
(5)空对象模式的优缺点
- 优点
可以加强系统的稳固性,能有效防止空指针报错对整个系统的影响;
不依赖客户端便可以保证系统的稳定性; - 缺点
需要编写较多的代码来实现空值的判断,从某种方面来说不划算;
2.12 访问者模式
(1)概念
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
主要解决:稳定的数据结构和易变的操作耦合问题。
何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。
如何解决:在被访问的类里面加一个对外提供接待访问者的接口。
关键代码:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。
(2)适用场景
1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。
(3)代码示例
我们将创建一个定义接受操作的 ComputerPart 接口。Keyboard、Mouse、Monitor 和 Computer 是实现了 ComputerPart 接口的实体类。我们将定义另一个接口 ComputerPartVisitor,它定义了访问者类的操作。Computer 使用实体访问者来执行相应的动作。
VisitorPatternDemo,我们的演示类使用 Computer、ComputerPartVisitor 类来演示访问者模式的用法。
定义一个表示元素的接口。
- ComputerPart
package com.alibaba.design.visitorpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/2-8:54 */ public interface ComputerPart { public void accept(ComputerPartVisitor computerPartVisitor); }
定义一个表示访问者的接口。
- ComputerPartVisitor
package com.alibaba.design.visitorpattern; import com.alibaba.design.visitorpattern.Mouse; /** * @author zhouyanxiang * @create 2020-08-2020/8/2-8:54 */ public interface ComputerPartVisitor { public void visit(Computer computer); public void visit(Mouse mouse); public void visit(Keyboard keyboard); public void visit(Monitor monitor); }
创建扩展了ComputerPart 的实体类。
- Computer
package com.alibaba.design.visitorpattern; import com.alibaba.design.visitorpattern.Mouse; /** * @author zhouyanxiang * @create 2020-08-2020/8/2-8:56 */ public class Computer implements ComputerPart { ComputerPart[] parts; public Computer(){ parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()}; } @Override public void accept(ComputerPartVisitor computerPartVisitor) { for (int i = 0; i < parts.length; i++) { parts[i].accept(computerPartVisitor); } computerPartVisitor.visit(this); } }
- Keyboard
package com.alibaba.design.visitorpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/2-8:55 */ public class Keyboard implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } }
- Monitor
package com.alibaba.design.visitorpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/2-9:05 */ public class Monitor implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } }
- Mouse
package com.alibaba.design.visitorpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/2-9:05 */ public class Mouse implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } }
创建实现了上述类的实体访问者。
- ComputerPartDisplayVisitor
package com.alibaba.design.visitorpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/2-9:06 */ public class ComputerPartDisplayVisitor implements ComputerPartVisitor { @Override public void visit(Computer computer) { System.out.println("Displaying Computer."); } @Override public void visit(Mouse mouse) { System.out.println("Displaying Mouse."); } @Override public void visit(Keyboard keyboard) { System.out.println("Displaying Keyboard."); } @Override public void visit(Monitor monitor) { System.out.println("Displaying Monitor"); } }
客户端测试类
package com.alibaba.design.visitorpattern; /** * @author zhouyanxiang * @create 2020-08-2020/8/2-9:08 */ public class VisitorPatternDemo { public static void main(String[] args) { ComputerPart computer = new Computer(); computer.accept(new ComputerPartDisplayVisitor()); } }
输出结果:
(4)该模式在源码中的体现
在Spring中的应用:
它的具体实现都交给了valueResolver来实现
方法visitBeanDefinition实现了不同的visi方法t来对相同的数据进行不同的处理
(5)访问者模式的优缺点
优点:
1、符合单一职责原则。
2、优秀的扩展性。
3、灵活性。
缺点:
1、具体元素对访问者公布细节,违反了迪米特原则。
2、具体元素变更比较困难。
3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。