基本介绍
1)模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
2)简单说,模板方法模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤
3)这种类型的设计模式属于行为型模式。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。
例如:
1、创建一个抽象类,它的模板方法被设置为 final。
public abstract class Game { abstract void initialize(); abstract void startPlay(); abstract void endPlay(); //模板 public final void play(){ //初始化游戏 initialize(); //开始游戏 startPlay(); //结束游戏 endPlay(); } }
2、创建扩展了上述类的实体类。
public class Cricket extends Game { @Override void endPlay() { System.out.println("Cricket Game Finished!"); } @Override void initialize() { System.out.println("Cricket Game Initialized! Start playing."); } @Override void startPlay() { System.out.println("Cricket Game Started. Enjoy the game!"); } }
public class Football extends Game { @Override void endPlay() { System.out.println("Football Game Finished!"); } @Override void initialize() { System.out.println("Football Game Initialized! Start playing."); } @Override void startPlay() { System.out.println("Football Game Started. Enjoy the game!"); } }
3、使用者
public class TemplatePatternDemo { public static void main(String[] args) { Game game = new Cricket(); game.play(); game = new Football(); game.play(); } }
在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。
比如,我们希望制作纯豆浆,不添加任何的配料,请使用钩子方法对模板方法进行改造
//抽象类,表示豆浆 public abstract class SoyaMilk { //模板方法, make , 模板方法可以做成 final , 不让子类去覆盖. final void make() { select(); if(customerWantCondiments()) { addCondiments(); } soak(); beat(); } //选材料 void select() { System.out.println("第一步:选择好的新鲜黄豆 "); } //添加不同的配料, 抽象方法, 子类具体实现 abstract void addCondiments(); //浸泡 void soak() { System.out.println("第三步, 黄豆和配料开始浸泡, 需要 3 小时 "); } void beat() { System.out.println("第四步:黄豆和配料放到豆浆机去打碎 "); } //钩子方法,决定是否需要添加配料 boolean customerWantCondiments() { return true; } }
类图:
命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
使用场景:认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。
关键代码:定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口
1、创建command接口:
public interface ICommand { void execute(); }
2、具体的命令实现类
public class CommandA implements ICommand{ private Receiver receiver; public CommandA(Receiver receiver) { this.receiver = receiver; } @Override public void execute() { System.out.println("CommandA 执行 ....."); receiver.execute(); } } public class CommandB implements ICommand{ private Receiver receiver; public CommandB(Receiver receiver) { this.receiver = receiver; } @Override public void execute() { System.out.println("CommandB execute ..."); receiver.execute(); } }
3、Receiver:执行命令的对象,具体怎么执行命令
public class Receiver { public void execute(){ System.out.println("Receiver 执行命令"); } }
4、Invoker:创建命令入口,执行命令
public class Invoker { private ICommand CommandA; private ICommand CommandB; public Invoker(ICommand commandA, ICommand commandB) { CommandA = commandA; CommandB = commandB; } //执行命令A public void orderA(){ CommandA.execute(); } //执行命令B public void orderB(){ CommandB.execute(); } }
5、使用者
public static void main(String[] args) { Receiver receiver = new Receiver(); Invoker invoker = new Invoker(new CommandA(receiver), new CommandB(receiver)); invoker.orderA(); invoker.orderB(); }
代码分析
1)访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
2)主要将数据结构与数据操作分离,解决 数据结构和操作耦合性问题
3)访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口
4)访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决
1、Element 定义一个 accept 方法,接收一个访问者对象
public interface ComputerPart { void accept(ComputerPartVisitor computerPartVisitor); }
2、ConcreteElement 为具体元素,实现了 accept 方法
public class Computer implements ComputerPart{ ComputerPart[] parts; public Computer() { this.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); } } public class Keyboard implements ComputerPart{ @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } } public class Monitor implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } } public class Mouse implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } }
3、Visitor 是抽象访问者,为该对象结构中的 ConcreteElement 的每一个类声明一个 visit 操作
public interface ComputerPartVisitor { void visit(Computer computer); void visit(Mouse mouse); void visit(Keyboard keyboard); void visit(Monitor monitor); }
4、ConcreteVisitor :是一个具体的访问值 实现每个有 Visitor 声明的操作,是每个操作实现的部分.
public class ComputerPartDisplayVisitor implements ComputerPartVisitor{ @Override public void visit(Computer computer) { System.out.println("展示 computer"); } @Override public void visit(Mouse mouse) { System.out.println("展示鼠标"); } @Override public void visit(Keyboard keyboard) { System.out.println("展示键盘"); } @Override public void visit(Monitor monitor) { System.out.println("展示显示器"); } }
5、使用者
public static void main(String[] args) { ComputerPart computer = new Computer(); computer.accept(new ComputerPartDisplayVisitor()); }
这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
主要解决:不同的方式来遍历整个整合对象。
何时使用:遍历一个聚合对象。
如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。
关键代码:定义接口:hasNext, next。
应用实例:JAVA 中的 iterator。
使用场景: 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。
1、Iterator 接口
public interface Iterator { boolean hasNext(); Object next(); }
2、返回迭代器的 Container 接口
public interface Container { Iterator getIterator(); }
3、实现了 Container 接口的实体类将负责实现 Iterator 接口。
public class NameRepository implements Container{ public String[] names={"robert","john","julie","lora"}; @Override public Iterator getIterator() { return new NameIterator(); } private class NameIterator implements Iterator{ int index; @Override public boolean hasNext() { if (index<names.length){ return true; } return false; } @Override public Object next() { if (this.hasNext()){ return names[index++]; } return null; } } }
4、使用
public static void main(String[] args) { NameRepository nameRepository=new NameRepository(); Iterator iterator = nameRepository.getIterator(); while(iterator.hasNext()){ String name = (String)iterator.next(); System.out.println("Name : " + name); } }
应用场景:有些业务需要不断的被维护,修改。如果不断的去修改这个方法,可能会造成业务逻辑出错
破坏了单一职责原则,耦合了很多附属性的代码。
破坏了开闭原则(不修改代码的情况下进行扩展),比如:我创建订单的时候,还要发微信消息,肯定要修改代码来扩展功能,又或者我要修改发短信的逻辑的时候,又要动这个方法。
优化:把2,3,4拆出来,分别包到一个类里,然后类似的用一个for循环一个一个的去执行。就是观察者模式
例子:
public class OrderEvent extends ApplicationEvent { Long id; public OrderEvent(Object source,Long id) { super(source); this.id = id; } } // 发邮件的逻辑放到一个类里 @Component public class EmailListener implements ApplicationListener<OrderEvent> { @Override public void onApplicationEvent(OrderEvent orderEvent) { System.out.println("发邮件: " + orderEvent.id); } } // 发短信的逻辑放到一个类里 @Component public class SmsListener implements ApplicationListener<OrderEvent> { @Override public void onApplicationEvent(OrderEvent orderEvent) { System.out.println("发送短信的逻辑。" + orderEvent.id); } } @Service public class DeviceBusinessPurposeServiceImpl extends ServiceImpl<DeviceBusinessPurposeMapper, DeviceBusinessPurpose> implements DeviceService { @Autowired private ApplicationContext applicationContext; @Override public void createOrder() { // 创建订单 Long id = 1L; System.out.println("创建订单"); // 发布订单相关事件(Spring里有很多事件,指定要发布的事件是OrderEvent的事件) // 循环调用类里的业务逻辑 applicationContext.publishEvent(new OrderEvent(applicationContext, id)); } } ps:这里的Event和Listener ,直接用的Spring提供的。也可以自己写