1.模版方法
-
超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤
-
结构
-
抽象类:负责给出一个轮廓与骨架,由一个模版方法和若干个基本方法构成
- 模版方法:按某种顺序调用其包含的基本方法
- 基本方法:实现的每个步骤,基本方法又可以分为三种
- 抽象方法:一个抽象方法由抽象类声明,由具体子类实现
- 具体方法:一个具体方法由抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承
- 钩子方法:抽象类中可以实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean
-
具体子类:实现抽象类中所定义的抽象方法和钩子方法,它是一个顶级逻辑的组成步骤
-
-
实现
public abstract class AbstractClass { public void cookProcess(){ pourOil(); heatOil(); pourVegetable(); pourSauce(); fry(); } public void pourOil(){ System.out.println("倒油"); } public void heatOil(){ System.out.println("热油"); } public abstract void pourVegetable(); public abstract void pourSauce(); public void fry(){ System.out.println("一直翻炒"); } } public class ConCreateClassBaoCai extends AbstractClass{ @Override public void pourVegetable() { System.out.println("爆炒包菜"); } @Override public void pourSauce() { System.out.println("放入小辣椒"); } }
-
优缺点
- 优点
- 仅允许客户端重写一个大型算法中的特定部分, 使得算法其他部分修改对其所造成的影响减小
- 可将重复代码提取到一个超类中
- 缺点
- 部分客户端可能会受到算法框架的限制
- 通过子类抑制默认步骤实现可能会导致违反里氏替换原则
- 模板方法中的步骤越多, 其维护工作就可能会越困难
- 优点
2.策略模式
-
定义一系列算法,并将算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户,策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的虚i昂对这些算法进行管理
-
结构
-
抽象策略:由一个接口或抽象类实现,此角色给出所有的具体策略类所需的接口
-
具体策略:实现抽象策略定义的接口,提供具体的算法实现行为
-
环境类:持有一个策略类的引用,最终给客户端调用
-
-
实现
public interface Strategy { void show(); } public class StrategyA implements Strategy{ @Override public void show() { System.out.println("买一送一"); } } public class SalesMan { private Strategy strategy; public SalesMan(Strategy strategy) { this.strategy = strategy; } public void salesManShow(){ strategy.show(); } }
-
优缺点
- 优点
- 运行时切换对象内的算法
- 将算法的实现和使用算法的代码隔离开来
- 使用组合来代替继承
- 开闭原则, 你无需对上下文进行修改就能够引入新的策略
- 缺点
- 如果你的算法极少发生改变, 那么没有任何理由引入新的类和接口。 使用该模式只会让程序过于复杂
- 客户端必须知晓策略间的不同——它需要选择合适的策略
- 优点
3.命令模式
-
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、投递、调用、增加与管理
-
结构
-
抽象命令类:定义命令的接口,声明执行的方法
-
具体命令类:具体的命令,实现命令接口,通常会持有接受者,并调用接受者的功能来完成命令要执行的操作
-
实现者/接收者:接收者,真正执行命令的对象,任何类斗可能成为一个接收者,只要它能够实现命令要求实现的相对功能
-
调用者/请求者:要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象,这个是客户端真正触发命令并要求命令执行相对应的操作的地方,也就是说相当于使用命令对象的入口
-
-
实现
@Data public class Order { private int dingingTable; private Map<String,Integer> foodDic = new HashMap<>(); public void setFoodDic(String name ,int num) { foodDic.put(name,num); } } // 实现者/接收者 public class SeniorChef { public void makeFood(int num, String foodName) { System.out.println(num + "份," + foodName); } } // 抽象命令类 public interface Command { void execute(); } // 具体命令类 public class OrderCommand implements Command { private SeniorChef receiver; private Order order; public OrderCommand(SeniorChef receiver, Order order) { this.receiver = receiver; this.order = order; } @Override public void execute() { System.out.println(order.getDingingTable() + "桌的订单"); Map<String, Integer> foodDic = order.getFoodDic(); Set<String> keys = foodDic.keySet(); for (String key : keys) { receiver.makeFood(foodDic.get(key), key); } System.out.println(order.getDingingTable() + "桌的饭准备完毕"); } } // 调用者/请求者 public class Waiter { private List<Command> commands = new ArrayList<>(); public void setCommand(Command command) { commands.add(command); } public void orderUp(){ System.out.println("服务员,您有新的订单"); for (Command command : commands) { command.execute(); } } }
-
优缺点
- 优点
- 单一职责原则。 你可以解耦触发和执行操作的类
- 开闭原则。 你可以在不修改已有客户端代码的情况下在程序中创建新的命令
- 可以实现撤销和恢复功能
- 可以实现操作的延迟执行
- 可以将一组简单命令组合成一个复杂命令
- 缺点
- 代码可能会变得更加复杂, 因为你在发送者和接收者之间增加了一个全新的层次
- 优点
4.责任链模式
-
避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链,当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止
-
结构
-
抽象处理器:定义一个处理请求的接口,包含抽象处理方法和一个后继连接
-
具体处理器:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后续者
-
客户类:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程
-
-
实现
// 抽象处理器 public abstract class Handler { protected static final int NUM_ONE = 1; protected static final int NUM_THREE = 3; protected static final int NUM_SEVEN = 7; private int numStart ; private int numEnd; private Handler nextHandler; public Handler(int numStart, int numEnd) { this.numStart = numStart; this.numEnd = numEnd; } public void setNextHandler(Handler nextHandler) { this.nextHandler = nextHandler; } public abstract void submit(LeaveRequest leave); public void handleLeave(LeaveRequest leave){ this.submit(leave); if (this.nextHandler != null && leave.getNum() > this.numEnd){ this.nextHandler.handleLeave(leave); }else { System.out.println("异常"); } } } // 具体处理器 public class GroupLeader extends Handler{ public GroupLeader() { super(0,NUM_ONE); } @Override public void submit(LeaveRequest leave) { System.out.println(leave.getName() + "请假" + leave.getNum() + "天" + leave.getContent() + "。"); System.out.println("小组长审批通过"); } } @Data public class LeaveRequest { private String name; private int num; private String content; public LeaveRequest(String name, int num, String content) { this.name = name; this.num = num; this.content = content; } } // 客户类 public class TestChain { public static void main(String[] args) { LeaveRequest request = new LeaveRequest("张三",8,"身体不适"); GroupLeader groupLeader = new GroupLeader(); Manager manager = new Manager(); GeneralManager generalManager = new GeneralManager(); groupLeader.setNextHandler(manager); manager.setNextHandler(generalManager); groupLeader.handleLeave(request); } }
-
优缺点
- 优点
- 可以控制请求处理的顺序
- 单一职责原则。 你可对发起操作和执行操作的类进行解耦
- 开闭原则。 你可以在不更改现有代码的情况下在程序中新增处理者
- 缺点
- 部分请求可能未被处理
- 优点
5.状态模式
-
对有状态的对象,把复杂的判断逻辑提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为
-
结构
-
环境角色:定义客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理
-
抽象状态:定义一个接口,用以封装环境对象中的特定状态所对应的行为
-
具体状态:实现抽象状态所对应的行为
-
-
实现
// 抽象状态 public abstract class LiftState { protected Context context; public void setContext(Context context) { this.context = context; } public abstract void open(); public abstract void close(); public abstract void stop(); public abstract void run(); } // 环境角色 public class Context { public final static OpenNingState OPEN_NING_STATE = new OpenNingState(); public final static ClosingState CLOSING_STATE = new ClosingState(); public final static RunNingState RUN_NING_STATE = new RunNingState(); public final static StopPingState STOP_PING_STATE = new StopPingState(); private LiftState liftState; public void setLiftState(LiftState liftState) { this.liftState = liftState; liftState.setContext(this); } public void open() { liftState.open(); } public void close() { liftState.close(); } public void stop() { liftState.stop(); } public void run() { liftState.run(); } } // 具体状态 public class RunNingState extends LiftState{ @Override public void open() { } @Override public void close() { } @Override public void stop() { super.context.setLiftState(Context.STOP_PING_STATE); super.context.stop(); } @Override public void run() { System.out.println("电梯正在运行"); } }
-
优缺点
- 优点
- 单一职责原则, 将与特定状态相关的代码放在单独的类中
- 开闭原则,无需修改已有状态类和上下文就能引入新状态
- 通过消除臃肿的状态机条件语句简化上下文代码
- 缺点
- 如果状态机只有很少的几个状态, 或者很少发生改变, 那么应用该模式可能会显得小题大作
- 优点
6.观察者模式
-
定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态变化时,会通知所有观察者对象,使他们能够自动更新自已
-
结构
-
抽象主题:抽象主题角色能把所有观察者保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象
-
具体主题:该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知
-
抽象观察者:观察者的抽象类,定义了一个更新接口,使得在得到主题更改通知时更新自已
-
具体观察者:实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自已的状态
-
-
实现
// 抽象观察者 public interface Observer { void update(String msg); } // 具体主题 public class WxUserObserver implements Observer { private String name; public WxUserObserver(String name) { this.name = name; } @Override public void update(String msg) { System.out.println(name + "接收到消息:" + msg); } } // 抽象观察者 public interface Subject { void attach(Observer observer); void detach(Observer observer); void notifyMsg(String msg); } // 具体观察者 public class SubscriptionSubject implements Subject { private List<Observer> observerList = new ArrayList<>(); @Override public void attach(Observer observer) { observerList.add(observer); } @Override public void detach(Observer observer) { observerList.remove(observer); } @Override public void notifyMsg(String msg) { for (Observer observer : observerList) { observer.update(msg); } } }
-
优缺点
- 优点
- 可以在运行时建立对象之间的联系
- 开闭原则,你无需修改发布者代码就能引入新的订阅者类 (如果是发布者接口则可轻松引入发布者类)
- 缺点
- 订阅者的通知顺序是随机的
- 优点
7.中介者模式
-
定义中介角色来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互
-
结构
-
抽象中介者:中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法
-
具体中介者:实现中介者接口,定义list来管理同事对象,协调各个同事角色之间的交互关系,因为它依赖与同事角色
-
抽象同事类:定义同事类的接口,保存中介者对象,提供同事对象交换的抽象方法,实现所有互相影响的同事类的公共功能
-
具体同事类:抽象同事类的实现者,当需要与其它同事对象交互时,由中介者对象负责后续的交互
-
-
实现
// 抽象中介者 public abstract class Mediator { abstract void contact(String msg,Person person); } // 具体中介者 @Data public class MediatorStructure extends Mediator{ private HouseOwner houseOwner; private Tenant tenant; public void setMediatorStructure(HouseOwner houseOwner, Tenant tenant) { this.houseOwner = houseOwner; this.tenant = tenant; } @Override void contact(String msg, Person person) { if (person == houseOwner){ tenant.getMessage(msg); }else { houseOwner.getMessage(msg); } } } // 抽象同事 public abstract class Person { protected String name; protected Mediator mediator; public Person(String name, Mediator mediator) { this.name = name; this.mediator = mediator; } } // 具体同事类 public class Tenant extends Person { public Tenant(String name, Mediator mediator) { super(name, mediator); } public void contact(String msg) { mediator.contact(msg, this); } public void getMessage(String msg) { System.out.println("租房者:" + name + "获取到到信息是:" + msg); } }
-
优缺点
- 优点
- 可以减轻应用中多个组件间的耦合情况
- 开闭原则,你无需修改实际组件就能增加新的中介者
- 单一职责原则,以将多个组件间的交流抽取到同一位置, 使其更易于理解和维护。
- 缺点
- 一段时间后, 中介者可能会演化成为上帝对象
- 优点
8.迭代器模式
-
提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示
-
结构
-
抽象聚合:定义存储、添加、删除聚合元素以及创建迭代器对象的接口
-
具体聚合:实现抽象聚合类,返回一个具体迭代器的实例
-
抽象迭代器:定义访问和遍历聚合元素的接口,通常包含hasNext()、next()等方法
-
具体迭代器:实现抽象迭代器接口中定义的方法,完成对聚合对象的遍历,记录遍历的当前位置
-
-
实现
@Data @AllArgsConstructor @NoArgsConstructor public class Student { private String name ; private String number; } // 抽象聚合 public interface StudentAggregate { void addStudent(Student student); void removeStudent(Student student); StudentIterator getStudentIterator(); } // 具体聚合 public class StudentAggregateImpl implements StudentAggregate{ private List<Student> studentList = new ArrayList<>(); @Override public void addStudent(Student student) { studentList.add(student); } @Override public void removeStudent(Student student) { studentList.remove(student); } @Override public StudentIterator getStudentIterator() { return new StudentIteratorImpl(studentList); } } // 抽象迭代器 public interface StudentIterator { boolean hasNext(); Student next(); } // 具体迭代器 public class StudentIteratorImpl implements StudentIterator { private List<Student> students; private int position = 0; public StudentIteratorImpl(List<Student> students) { this.students = students; } @Override public boolean hasNext() { return position < students.size(); } @Override public Student next() { return students.get(position++); } }
-
优缺点
- 优点
- 单一职责原则,通过将体积庞大的遍历算法代码抽取为独立的类, 你可对客户端代码和集合进行整理
- 开闭原则, 你可实现新型的集合和迭代器并将其传递给现有代码, 无需修改现有代码
- 你可以并行遍历同一集合, 因为每个迭代器对象都包含其自身的遍历状态
- 缺点
- 程序只与简单的集合进行交互, 应用该模式可能会矫枉过正。
- 对于某些特殊集合, 使用迭代器可能比直接遍历的效率低。
- 优点
9.访问者模式
-
封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数就结构的前提下定义作用于这些元素的新的操作
-
结构
-
抽象访问者:定义了对每个元素访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素类个数是一样的,访问者模式要求元素类的个数不能改变
-
具体访问者:给出对每一个元素类访问时所产生的具体行为
-
抽象元素:定义了一个接受访问者的方法,其意义是指,每一个元素都要可以被访问者访问
-
具体元素:提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法
-
对象结构:定义当中所提到的对象结构,对象结构上一个抽象表述,具体点可以理解为一个具有容器性质或者复合对象特性的类,它含有一组元素,并且可以迭代这些元素,供访问者访问
-
-
实现
// 抽象元素 public interface Animal { void accept(Person person); } // 具体元素 public class Cat implements Animal{ @Override public void accept(Person person) { person.feed(this); System.out.println("喵喵喵~~~"); } } // 抽象访问者 public interface Person { void feed(Cat cat); void feed(Dog dog); } // 具体访问者 public class Someone implements Person { @Override public void feed(Cat cat) { System.out.println("其它人喂猫"); } @Override public void feed(Dog dog) { System.out.println("其它人喂狗"); } } // 结构对象 public class Home { private List<Animal> nodeList = new ArrayList<>(); public void action(Person person) { for (Animal animal : nodeList) { animal.accept(person); } } public void add(Animal animal) { nodeList.add(animal); } }
-
优缺点
- 优点
- 开闭原则。 你可以引入在不同类对象上执行的新行为, 且无需对这些类做出修改
- 单一职责原则。 可将同一行为的不同版本移到同一个类中
- 缺点
- 每次在元素层次结构中添加或移除一个类时, 你都要更新所有的访问者。
- 在访问者同某个元素进行交互时, 它们可能没有访问元素私有成员变量和方法的必要权限
- 优点
10.备忘录模式
-
又叫快照模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态
-
结构
- 发起人:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其它业务功能,它可以访问备忘录的所有信息
- 备忘录:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人,备忘录有两个等效的接口
- 窄接口:管理者对象(和其它发起人对象之外的任务对象)看到的是备忘录的窄接口,这个窄接口只允许他把备忘录对象传给其它的对象
- 宽接口:与管理者看到的窄接口相反,发起人对象可以看到一个宽接口,这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态
- 管理者:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能堆备忘录的内容进行访问与修改
-
白箱备忘录模式
@Data @NoArgsConstructor @AllArgsConstructor public class WhiteRoleStateMemento { private int vit; private int atk; private int def; } public class WhiteRoleStateCaretaker { private WhiteRoleStateMemento roleStateMemento; public WhiteRoleStateMemento getRoleStateMemento() { return roleStateMemento; } public void setRoleStateMemento(WhiteRoleStateMemento roleStateMemento) { this.roleStateMemento = roleStateMemento; } } public class WhiteGameRole { private int vit; private int atk; private int def; public void initState() { this.vit = 100; this.atk = 100; this.def = 100; } public void fight() { this.vit = 0; this.atk = 0; this.def = 0; } public WhiteRoleStateMemento saveState() { return new WhiteRoleStateMemento(vit, atk, def); } public void recoverState(WhiteRoleStateMemento roleStateMemento) { this.vit = roleStateMemento.getVit(); this.atk = roleStateMemento.getAtk(); this.def = roleStateMemento.getDef(); } public void stateDisplay() { System.out.println("角色生命力:" + vit); System.out.println("角色攻击力:" + atk); System.out.println("角色防御力:" + def); } }
-
黑箱备忘录模式
public interface BlackMemento { } public class BlackRoleStateCaretaker { private BlackMemento blackMemento; public BlackMemento getBlackMemento() { return blackMemento; } public void setBlackMemento(BlackMemento blackMemento) { this.blackMemento = blackMemento; } } public class BlackGameRole { private int vit; private int atk; private int def; public void initState() { this.vit = 100; this.atk = 100; this.def = 100; } public void fight() { this.vit = 0; this.atk = 0; this.def = 0; } public BlackMemento saveState() { return new RoleStateMemento(vit, atk, def); } public void recoverState(BlackMemento blackMemento) { RoleStateMemento roleStateMemento = (RoleStateMemento) blackMemento; this.vit = roleStateMemento.getVit(); this.atk = roleStateMemento.getAtk(); this.def = roleStateMemento.getDef(); } public void stateDisplay() { System.out.println("角色生命力:" + vit); System.out.println("角色攻击力:" + atk); System.out.println("角色防御力:" + def); } @AllArgsConstructor @NoArgsConstructor @Data private class RoleStateMemento implements BlackMemento { private int vit; private int atk; private int def; } }
11.解释器模式
-
给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子
-
结构
-
抽象表达式:定义解释器的接口,约定解释器的解释操作,主要包含解释方法
-
终结者表达式:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应
-
非终结符表达式:抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于非终结符表达式
-
环境角色:包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值
-
客户端:任务是将需要分析的句子或者表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方式,也可以通过环境角色间接访问解释器的解释方法
-
-
实现
public abstract class AbstractExpression { public abstract int interpret(Context context); } public class Context { private Map<Variable,Integer> map = new HashMap<>(); public void assign(Variable var ,Integer value){ map.put(var,value); } public int getValue(Variable var){ return map.get(var); } } public class Variable extends AbstractExpression { private String name; public Variable(String name) { this.name = name; } @Override public int interpret(Context context) { return context.getValue(this); } @Override public String toString() { return name; } } public class Plus extends AbstractExpression{ private AbstractExpression left; private AbstractExpression right; public Plus(AbstractExpression left, AbstractExpression right) { this.left = left; this.right = right; } public int interpret(Context context) { return left.interpret(context) + right.interpret(context); } @Override public String toString() { return "(" + left.toString() + "+" + right.toString() + ")"; } } public class Minus extends AbstractExpression{ private AbstractExpression left; private AbstractExpression right; public Minus(AbstractExpression left, AbstractExpression right) { this.left = left; this.right = right; } public int interpret(Context context) { return left.interpret(context) - right.interpret(context); } @Override public String toString() { return "(" + left.toString() + "-" + right.toString() + ")"; } }