1. 模板方法模式
1.1 概述
定义:定义一个操作中的算法骨架,而将算法中的一些步骤延迟到子类中,使子类可以在不改变该算法的情况下重新定义该算法某些的特定步骤。
1.2 结构
模板方法(Template Method)模式包含以下主要角色:
- 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法组成。
- 模板方法:定义算法骨架,按某种顺序调用其包含的基本方法。
- 基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。它分为三种:
- 抽象方法(Abstract Method):一个抽象方法由抽象类声明,由其具体子类实现。
- 具体方法(Concrete Method):一个抽象方法由一个抽象类或具体类声明并实现,其子类可以覆盖或直接继承。
- 钩子方法(Hook Method):在抽象类中已实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。一般钩子方法用于逻辑判断,方法名一般为isXXX,返回值类型一般为boolean类型。
具体子类(Concrete Class):实现抽象类中定义的抽象方法和钩子方法,他们是一个顶级逻辑的组成步骤。
1.3 案例
【例】炒菜
炒菜的步骤基本固定,分为倒油、热油、倒蔬菜、倒调料、翻炒等步骤。通过模板方法用代码模式实现。 ```java /**- @Description: 烹饪
@Author: zhang lei */ public abstract class Cooking {
public final void cook() {
this.pourOil();
this.heatOil();
this.pourVegetable();
this.pourSauce();
this.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(“翻炒…”); } }
/**
- @Description: 炒包菜
@Author: zhang lei */ public class FryBaoCai extends Cooking{ @Override public void pourVegetable() {
System.out.println("放入包菜...");
}
@Override public void pourSauce() {
System.out.println("加调料:盐、辣椒、耗油...");
} }
/**
- @Description: 炒土豆丝
@Author: zhang lei */ public class FryTuDouSi extends Cooking{ @Override public void pourVegetable() {
System.out.println("放入土豆丝...");
}
@Override public void pourSauce() {
System.out.println("加调料:盐、辣椒、陈醋...");
} }
- @Description: 模板方法模式测试
- @Author: zhang lei
*/
public class TemplateTest {
public static void main(String[] args) {
} } ```FryBaoCai fryBaoCai = new FryBaoCai();
fryBaoCai.cook();
System.out.println("----------------");
FryTuDouSi fryTuDouSi = new FryTuDouSi();
fryTuDouSi.cook();
1.4 优缺点
优点:
- 提高代码复用性,将相同部分的代码放在抽象父类中,而将不同的代码放在不同的子类中;
- 实现了反向控制,通过一个父类调用其子类操作,通过子类的具体实现扩展不同的行为,实现了反向控制,并符合“开闭原则”;
缺点:
- 对每个不同的实现都要定义一个子类,这会导致类的数量增加,系统更加庞大,设计也更加抽象;
父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向控制的结构,它提高了代码的阅读难度。
1.5 应用场景
算法的整体步骤很固定,但其中个别部分易变时,这时可以使用模板方法模式,将易变部分抽象出来,让子类去实现。
- 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。
2. 策略模式
2.1 概述
定义:值对象有某种行为,但在不同的场景中,有不同的算法实现。该模式定义一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法实现分开,并委派给不同的对象将这些算法实现。
2.2 结构
策略模式的主要角色如下:
- 抽象策略(Abstract Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现,此角色给出所有的具体策略类所需的接口
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
- 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
2.3 案例
【例】促销活动
百货公司指定年度促销活动。针对不同的节日(春节、中秋节、圣诞节)推出不同的促销活动。由促销员将促销活动展示给客户。 ```java /**- @Description: 促销活动接口
- @Author: zhang lei */ public interface Promotions { void show(); }
/**
- @Description: 活动A
- @Author: zhang lei
*/
public class StrategyA implements Promotions{
@Override
public void show() {
} }System.out.println("买一送一");
/**
- @Description: 活动B
- @Author: zhang lei
*/
public class StrategyB implements Promotions{
@Override
public void show() {
} }System.out.println("满200减30...");
/**
- @Description: 活动C
- @Author: zhang lei
*/
public class StrategyC implements Promotions{
@Override
public void show() {
} }System.out.println("满500元,加1元换购金镶玉吊坠一枚...");
/**
- @Description: 销售员
@Author: zhang lei */ public class Seller { private Promotions promotions;
public Seller(Promotions promotions) {
this.promotions = promotions;
}
public void sellerShowToC(){
promotions.show();
} }
- @Description: 策略模式测试
@Author: zhang lei */ public class StrategyTest { public static void main(String[] args) {
Seller seller = new Seller(new StrategyA());
seller.sellerShowToC();
Seller seller2 = new Seller(new StrategyB());
seller2.sellerShowToC();
Seller seller3 = new Seller(new StrategyC());
seller3.sellerShowToC();
2.4 优缺点
优点:
- 策略类之间可以随意切换
- 易于扩展
- 避免使用多重条件选择语句(if else),充分体现面向对象的设计思想
缺点:
- 客户端必须知道所有的策略类
策略模式将产生很多策略类,可以通过享元模式在一定程度上减少对象的数量
2.5 应用场景
一个系统需要动态地在几种算法算法中选择一种时,可将每个算法封装到策略类中。
- 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支植入他们各自的策略类中以代替这些条件语句。
- 系统中各算法完全独立,且要去对客户隐藏具体算法实现细节时。
- 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
- 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体执行的行为。
3. 命令模式
3.1 概述
定义:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分隔开。这样两者之间通过命令对象进行沟通,这样方便对存储对象进行存储、传递、调用、增加和管理。
3.2 结构
命令模式(Command)包含以下结构:
抽象命令(Abstract Command)角色:定义命令的接口,声明执行的方法。
具体命令(Concrete Command)角色:实现命令接口,定义具体的命令;通过会持有接受者,并调用接收者的功能来完成命令要执行的操作。
实现者/接收者(Receiver)角色:真正执行命令的对象,任何类都可能成为一个接收者,只要它能够实现命令要求的功能。
调用者/请求者(Invoker)角色:要求命令对象执行操作,通常会持有命令对象,可以持有多个命令对象。
3.3 案例
【例】餐厅点餐
服务员:调用者角色,发起命令
厨师:接收者角色,执行命令
订单:命令角色
/**
* @Description: 命令接口
* @Author: zhang lei
*/
public interface Command {
void execute();
}
/**
* @Description: 订单命令
* @Author: zhang lei
*/
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.getDiningTable() + "号桌订单:");
Set<String> keys = order.getFoodDic().keySet();
for (String key : keys) {
receiver.makeFood(order.getFoodDic().get(key), key);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(order.getDiningTable() + "号桌取餐...");
}
}
/**
* @Description: 订单
* @Author: zhang lei
*/
public class Order {
/**
* 餐桌号
*/
private int diningTable;
private Map<String, Integer> foodDic = new HashMap<String, Integer>();
public int getDiningTable() {
return diningTable;
}
public void setDiningTable(int diningTable) {
this.diningTable = diningTable;
}
public Map<String, Integer> getFoodDic() {
return foodDic;
}
public void setFoodDic(String name, int num) {
foodDic.put(name, num);
}
}
/**
* @Description: 厨师
* @Author: zhang lei
*/
public class SeniorChef {
public void makeFood(int num, String foodName){
System.out.println(num + " 份 " + foodName);
}
}
/**
* @Description: 服务员
* @Author: zhang lei
*/
public class Waitor {
private ArrayList<Command> commands;
public Waitor(){
commands = new ArrayList<>();
}
public void setCommands(Command cmd){
commands.add(cmd);
}
/**
* 下订单
*/
public void orderUp(){
System.out.println("服务员:您有新的外卖订单...");
for (Command cmd : commands){
if (cmd != null){
cmd.execute();
}
}
}
}
/**
* @Description: 命令模式测试
* @Author: zhang lei
*/
public class CommandTest {
public static void main(String[] args) {
// 创建订单
Order order1 = new Order();
order1.setDiningTable(1);
order1.getFoodDic().put("虾仁水饺",3);
order1.getFoodDic().put("松鼠桂鱼",1);
Order order2 = new Order();
order2.setDiningTable(2);
order2.getFoodDic().put("青椒牛肉面",1);
order2.getFoodDic().put("兰州拉面",1);
// 创建接收者
SeniorChef receiver = new SeniorChef();
OrderCommand cmd1 = new OrderCommand(receiver, order1);
OrderCommand cmd2 = new OrderCommand(receiver, order2);
// 调用者
Waitor waitor = new Waitor();
waitor.setCommands(cmd1);
waitor.setCommands(cmd2);
waitor.orderUp();
}
}
3.4 优缺点
优点:
- 降低系统耦合度。命令模式可以将调用操作的对象和实现操作的对象解耦
- 增加和删除命令非常方便。采用命令模式增加和删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活
- 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组命令,即宏命令。
- 方便实现Undo和Redo操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
缺点:
- 使用命令模式可能导致某些系统有过多的具体类。
-
3.5 使用场景
系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
- 系统需要在不同的时间指定请求、将请求排队和执行请求。
- 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
4. 责任链模式
4.1 概述
定义:又称职责链模式,为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一个链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
4.2 结构
责任链模式包含以下角色:
- 抽象处理者(Abstract Handler)角色:定义一个处理请求接口,包含抽象处理方法和一个后继连接。
- 具体处理者(Concrete Handler)角色:实现抽象处理者处理方法,判断能否处理本次请求,如何能处理请求则处理,否则将该请求转给它的后继者。
客户类(Client)角色:创建处理链,并向连头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
4.3 案例
【例】请假流程控制 ```java /**
- @Description: 请假条
@Author: zhang lei / 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 String getName() {
return name;
}
public int getNum() {
return num;
}
public String getContent() {
return content;
} }
/**
- @Description: 处理者抽象类
@Author: zhang lei */ public abstract class Handler { protected final static int NUM_ONE = 1; protected final static int NUM_THREE = 3; protected final static int NUM_SEVEN = 7;
private int numStart; private int numEnd;
// 上一级领导 private Handler nextHandler;
/**
设置请假天数范围 上不封顶 */ public Handler(int numStart) { this.numStart = numStart; }
/**
- 设置请假天数范围 *
- @param numStart
@param numEnd */ public Handler(int numStart, int numEnd) { this.numStart = numStart; this.numEnd = numEnd; }
/**
- 设置上一级领导 *
@param nextHandler */ public void setNextHandler(Handler nextHandler) { this.nextHandler = nextHandler; }
/**
- 提交请假条 *
@param leave */ public final void submit(LeaveRequest leave) { if (this.numStart == 0) {
return;
} //如果请假天数达到该领导的处理要求 if (leave.getNum() >= this.numStart) {
this.handlerLeave(leave);
//如果还有上级,并且请假天数超过了当前领导的处理范围
if (this.nextHandler != null && leave.getNum() > numEnd) {
//提交下一级处理
this.nextHandler.submit(leave);
} else {
System.out.println("流程结束...");
}
} }
/**
- 各级领导处理请假条方法 *
- @param leave */ protected abstract void handlerLeave(LeaveRequest leave); }
/**
- @Description: 小组长
@Author: zhang lei */ public class GroupLeader extends Handler {
public GroupLeader() {
//小组长有权批准1~3天请假
super(Handler.NUM_ONE, Handler.NUM_THREE);
}
@Override protected void handlerLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天。" + leave.getContent() + "。");
System.out.println("小组长审批:同意");
} }
/**
- @Description: 部门经理
@Author: zhang lei */ public class Manager extends Handler{ public Manager(){
//部门经理有权批准3~7天请假
super(Handler.NUM_THREE, Handler.NUM_SEVEN);
}
@Override protected void handlerLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
System.out.println("部门经理审批:同意");
} }
/**
- @Description: 总经理
@Author: zhang lei */ public class GeneralManager extends Handler{ public GeneralManager() {
super(Handler.NUM_SEVEN);
}
@Override protected void handlerLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
System.out.println("总经理审批:同意");
} }
- @Description: 责任链模式测试
@Author: zhang lei */ public class ChainTest { public static void main(String[] args) {
LeaveRequest leave = new LeaveRequest("李白", 10, "喝醉了...");
GroupLeader groupLeader = new GroupLeader();
Manager manager = new Manager();
GeneralManager generalManager = new GeneralManager();
groupLeader.setNextHandler(manager);
manager.setNextHandler(generalManager);
//提交请假申请
groupLeader.submit(leave);
4.4 优缺点
优点:
- 降低对象之间的耦合度
- 增强了系统的可扩展性
- 增强了给对象指派责任的灵活性
- 责任链简化了对象之间的连接
- 责任分担
缺点:
- 不能保证每个请求一定被处理
- 对比较长的责任链,请求的处理可能涉及多个处理对象,系统性能将收到一个影响。
- 责任链建立的合理性要靠客户端保证,增加了客户端的复杂性,可能会由于责任链的错误设置而导致系统出错,如可能会造成循环调用。
5. 状态模式
5.1 概述
定义:当一个对象内在状态改变时允许其改变行为,这个对象像改变了其类。
状态模式的核心是封装,状态改变引起行为的改变
5.2 结构
状态模式中三种角色:
抽象状态(State)角色:接口或者抽象类,负载对象状态定义,并且封装环境角色以实现状态切换。
具体状态(Concrete State)角色:每一个状态必须完成两个职责:本状态的行为管理以及趋向状态处理。简而言之,就是本状态要处理的事情,以及如何过渡到其他状态。
环境(Context)角色:定义客户端需要的接口,并负责环境切换。
5.3 案例
【例】电梯案例
电梯有四种动作,开门、关门、运行、停止,电梯的4中动作执行都有前置条件,特定条件下才能执行特定动作。图标表示如下:
开门(open) | 关门(close) | 运行(run) | 停止(stop) | |
---|---|---|---|---|
敞门状态 | ✖ | ✔ | ✖ | ✖ |
闭门状态 | ✔ | ✖ | ✔ | ✔ |
运行状态 | ✖ | ✖ | ✖ | ✔ |
停止状态 | ✔ | ✖ | ✔ | ✖ |
/**
* @Description: 环境角色
* @Author: zhang lei
*/
public class Context {
//定义出所有的电梯状态
public final static OpenningState openningState = new OpenningState();
public final static ClosingState closeingState = new ClosingState();
public final static RunningState runningState = new RunningState();
public final static StoppingState stoppingState = new StoppingState();
//定义一个当前电梯状态
private LiftState liftState;
public LiftState getLiftState() {
return liftState;
}
/**
* 设置电梯初始状态
* @param liftState
*/
public void setLiftState(LiftState liftState) {
this.liftState = liftState;
//把当前环境通知到所有实现类中
this.liftState.setContext(this);
}
public void open(){
this.liftState.open();
}
public void close(){
this.liftState.close();
}
public void run(){
this.liftState.run();
}
public void stop(){
this.liftState.stop();
}
}
/**
* @Description: 抽象状态类
* @Author: zhang lei
*/
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 run();
public abstract void stop();
}
/**
* @Description: 敞门状态
* @Author: zhang lei
*/
public class OpenningState extends LiftState{
@Override
public void open() {
System.out.println("电梯门打开...");
}
@Override
public void close() {
//状态修改
super.context.setLiftState(Context.closeingState);
//委托ClosingState来执行
super.context.getLiftState().close();
}
@Override
public void run() {
// do nothing
}
@Override
public void stop() {
// do nothing
}
}
/**
* @Description: 闭门状态
* @Author: zhang lei
*/
public class ClosingState extends LiftState {
@Override
public void close() {
System.out.println("电梯门关闭...");
}
@Override
public void open() {
super.context.setLiftState(Context.openningState);
super.context.getLiftState().open();
}
@Override
public void run() {
super.context.setLiftState(Context.runningState);
super.context.getLiftState().run();
}
@Override
public void stop() {
super.context.setLiftState(Context.stoppingState);
super.context.getLiftState().stop();
}
}
/**
* @Description: 运行状态
* @Author: zhang lei
*/
public class RunningState extends LiftState{
@Override
public void open() {
// do nothing
}
@Override
public void close() {
// do nothing
}
@Override
public void run() {
System.out.println("电梯正在运行...");
}
@Override
public void stop() {
super.context.setLiftState(Context.stoppingState);
super.context.stop();
}
}
/**
* @Description: 停止状态
* @Author: zhang lei
*/
public class StoppingState extends LiftState{
@Override
public void open() {
super.context.setLiftState(Context.openningState);
super.context.open();
}
@Override
public void close() {
// do nothing
}
@Override
public void run() {
super.context.setLiftState(Context.runningState);
super.context.run();
}
@Override
public void stop() {
System.out.println("电梯停止了...");
}
}
/**
* @Description: 状态模式测试
* @Author: zhang lei
*/
public class StateTest {
public static void main(String[] args) {
Context context = new Context();
context.setLiftState(new ClosingState());
context.open();
context.close();
context.run();
context.stop();
}
}
5.4 优缺点
优点:
- 将所有与某个状态有关的行为放在一个类中,方便进行管理。
- 允许状态逻辑和状态对象合为一体,而不是某个巨大的条件语句块。
缺点:
- 对象模式会增加系统类的数量。
- 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码混乱。
-
5.5 使用场景
当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
- 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
6. 观察者模式 💥
6.1 概述
定义:又称发布-订阅(Publish/Subsctribe)模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当这个主题对象变化时,会通知所有的观察者对象,使他们能够自动的更新自己。
6.2 结构
观察者模式有以下角色:
- 抽象主题(Subject)角色:抽象主题角色把所有对象保存在一个集合中,每个主题对象都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
- 具体主题(ConcreteSubject)角色:该角色将有关状态存入具体观察者对象,在具体主题内部状态发生改变时,给所有注册过的观察者发送通知。
- 抽象观察者(ObServer)角色:是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改的通知时更新自己。
- 具体观察者(ConcreteObserver)角色:实现抽象观察者定义的接口,以便在得到主题更改的通知时更新自己。
6.3 案例
【例】微信公众号
当你关注的微信公众号更新时,它就会推送给关注该公众号的微信客户端。 ```java /**- @Description: 抽象观察者
- @Author: zhang lei */ public interface Observer { void update (String msg); }
/**
- @Description: 具体观察者
@Author: zhang lei */ public class WeChatUser implements Observer{ // 微信用户名 private String name;
public WeChatUser(String name) {
this.name = name;
}
@Override public void update(String msg) {
System.out.println(name + " 收到订阅消息:" + msg);
} }
/**
- @Description: 抽象主题
@Author: zhang lei / public interface Subject { /*
- 增加主题
@param observer */ void attch (Observer observer);
/**
- 删除主题
@param observer */ void detach(Observer observer);
/**
- 通知消息
- @param message */ void notify(String message); }
/**
- @Description: 具体主题
@Author: zhang lei */ public class ConcreteSubject implements Subject{ private List
WeChatUsers = new ArrayList<>(); @Override public void attch(Observer observer) {
WeChatUsers.add(observer);
}
@Override public void detach(Observer observer) {
WeChatUsers.remove(observer);
}
@Override public void notify(String message) {
for (Observer weChatUser : WeChatUsers) {
weChatUser.update(message);
}
} }
- @Description: 观察者模式测试
@Author: zhang lei */ public class ObserverTest { public static void main(String[] args) {
ConcreteSubject concreteSubject = new ConcreteSubject();
//创建微信用户
WeChatUser user1 = new WeChatUser("李白");
WeChatUser user2 = new WeChatUser("裴擒虎");
WeChatUser user3 = new WeChatUser("狄仁杰");
//订阅公众号
concreteSubject.attch(user1);
concreteSubject.attch(user2);
concreteSubject.attch(user3);
//公众号更新内容发送通知
concreteSubject.notify("上元节当日,公孙离将在长安坊表演惊鸿舞...");
6.4 优缺点
优点:
- 降低了目标与观察者之间的耦合度,两者之间值抽象耦合关系;
- 被观察者发送通知,所有注册的观察者都会收到消息(可以实现广播机制)
缺点:
7. 中介模式
7.1 概述
定义:又叫调停模式,定义一个中介模式角色来封装一些列对象之间的交互,使原有对象之间耦合松散,并且可以独立的改变他们之间的交互。
7.2 结构
中介模式包含以下主要结构:
- 抽象中介(Mediator)角色:它是抽象对象接口,提供了同事对象注册与转发同事对象信息的抽象方法。
- 具体中介(Concrete Mediator)角色:实现中介者接口,定义一个List来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
- 抽象同事类(Colleague)角色:定义同事类的接口,保存中介对象,提供同事对象交互的抽象方法,实现所有相互影响同事类的公共功能。
- 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介对象负责后续的交互
7.3 案例
【例】租房
租客通过房屋中介租房,房主将房屋托管给房屋中介,租房者从房屋中介那获取房屋信息。房屋中介充当租房者和房主之间的中介。 ```java /**- @Description: 抽象中介者
- @Author: zhang lei */ public abstract class Mediator { public abstract void constact(String msg, Person person); }
/**
- @Description: 抽象同事类
@Author: zhang lei */ public abstract class Person { protected String name; protected Mediator mediator;
public Person(String name, Mediator mediator) {
this.name = name;
this.mediator = mediator;
} }
/**
- @Description: 具体房主类-房屋拥有者
@Author: zhang lei */ public class HomeOwner extends Person { public HomeOwner(String name, Mediator mediator) {
super(name, mediator);
}
/**
- 与中介联系 *
@param msg */ public void constact(String msg) { mediator.constact(msg, this); }
/**
- 获取消息 *
- @param message */ public void getMessage(String message) { System.out.println(“房主” + name + “收到消息:” + message); } }
/**
- @Description: 具体同事类-租客
@Author: zhang lei */ public class Tenant extends Person{ public Tenant(String name, Mediator mediator) {
super(name, mediator);
}
/**
- 与中介联系 *
@param msg */ public void constact(String msg) { mediator.constact(msg, this); }
/**
- 获取消息 *
- @param message */ public void getMessage(String message) { System.out.println(“租客” + name + “收到消息:” + message); } }
/**
- @Description: 中介机构
@Author: zhang lei */ public class MediatorStructure extends Mediator { //声明房主和租客的信息 private HomeOwner homeOwner; private Tenant tenant;
public HomeOwner getHomeOwner() {
return homeOwner;
}
public void setHomeOwner(HomeOwner homeOwner) {
this.homeOwner = homeOwner;
}
public Tenant getTenant() {
return tenant;
}
public void setTenant(Tenant tenant) {
this.tenant = tenant;
}
@Override public void constact(String msg, Person person) {
if (homeOwner == person) {
//如果是房主,获取租客信息
tenant.getMessage(msg);
} else {
//反之,获取房主信息
homeOwner.getMessage(msg);
}
} }
- @Description: 中介模式测试
@Author: zhang lei */ public class MediatorTest { public static void main(String[] args) {
//中介结构
MediatorStructure mediatorStructure = new MediatorStructure();
HomeOwner libai = new HomeOwner("李白", mediatorStructure);
Tenant sulie = new Tenant("苏烈", mediatorStructure);
mediatorStructure.setHomeOwner(libai);
mediatorStructure.setTenant(sulie);
sulie.constact("我要租长安公馆");
libai.constact("租金每月1W,押三付一,一年起租");
7.4 优缺点
优点:
- 松散耦合
- 集中控制交互
- 一对多关联转为一对一关联
缺点:
当同事类过多时,中介者的职责将很大,他会变得复杂而庞大,以致于系统难以维护。
7.5 应用场景
系统中对象之间存在复杂的引用关系,系统结构混乱且难以理解。
- 当想创建一个运行于多个类之间的对象,又不想生成新的子类时。
8. 迭代器模式
8.1 概述
定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不是暴露聚合对象的内部表示。
8.2 结构
迭代器模式包含以下角色:
- 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口;
- 具体聚合(Concrete Aggergate)角色:实现抽象聚合类,返回一个具体迭代器的实例;
- 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含hasNext()、next()等方法;
- 具体迭代器(Concrete Interator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
8.3 案例
【例】定义一个可以存储学生对象的容器对象,将遍历该容器的功能交给迭代器来实现。 ```java /**- @Description: 迭代器接口
- @Author: zhang lei */ public interface StudentIterator { boolean hasNext(); Student next(); }
/**
- @Description: 学生类
@Author: zhang lei */ public class Student { private String name; private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} }
/**
- @Description: 具体迭代器
@Author: zhang lei */ public class StudentIteratorImpl implements StudentIterator { private List
list; private int position = 0; public StudentIteratorImpl(List
list) { this.list = list;
}
@Override public boolean hasNext() {
return position < list.size();
}
@Override public Student next() {
Student currentStudent = list.get(position);
position++;
return currentStudent;
} }
/**
- @Description: 抽象容器
@Author: zhang lei */ public interface StudentAggregate { void addStudent(Student student);
void removeStudent(Student student);
StudentIterator getStudentIterator(); }
/**
- @Description: 具体容器
@Author: zhang lei */ public class StudentAggregateImpl implements StudentAggregate { private List
list = new ArrayList<>(); @Override public void addStudent(Student student) {
this.list.add(student);
}
@Override public void removeStudent(Student student) {
this.list.remove(student);
}
@Override public StudentIterator getStudentIterator() {
return new StudentIteratorImpl(list);
} }
/**
- @Description: 迭代器模式测试
@Author: zhang lei */ public class InteratorTest { public static void main(String[] args) {
Student zhaoyun = new Student("赵云", 20);
Student zhangfei = new Student("张飞", 22);
Student huangzhong = new Student("黄忠", 24);
StudentAggregateImpl studentAggregate = new StudentAggregateImpl();
studentAggregate.addStudent(zhaoyun);
studentAggregate.addStudent(zhangfei);
studentAggregate.addStudent(huangzhong);
StudentIterator studentIterator = studentAggregate.getStudentIterator();
while (studentIterator.hasNext()){
System.out.println(studentIterator.next().getName());
}
8.4 优缺点
优点:
- 它支持以不同方式遍历一个聚合对象,在同一聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用更换一个不同的迭代器即可改变遍历算法,我们也可以自定义迭代器的子类来扩展遍历方式;
- 迭代器简化了聚合类。
- 在迭代器模式中,由于引入了抽象层,增加了新的聚合类和迭代器都很方便,无须修改源代码,满足“开闭原则”的要求。
缺点:
9. 访问者模式
9.1 概述
定义:封装一些作用于某种数据结构中的各元素的操作,他可以在不改变这个数据结构的前提下定义作用于这些元素的新操作。
9.2 结构
访问者模式包含以下角色:
- 抽象访问者(Visitor)角色:定义了对每一元素
Element
访问行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素类的个数是一样的,从这点可以看出,访问者模式要求元素类的个数不能改变; - 具体访问者(Concrete Visitor)角色:给出对每一元素类访问时所产生的具体行为;
- 抽象元素(Element)角色:定义一个接受访问者的方法
accept
,其意义在于指明每个元素都可以被访问; - 具体元素(Concrete Element)角色:提供接受访问方法的具体实现,而这个具体实现,通常情况下使用访问者提供的访问该类的方法。
对象结构(Object Structure)角色:定义上述所提到的对象结构,对象结构是抽象的表述,具体点可以理解为一个具有容器性质或者符合对象特性的类,他会含有一组元素
Element
,并且可以迭代这些元素,供访问者访问。9.3 案例
【例】给宠物喂食
宠物有猫狗、狗、乌龟等,宠物喂食的话,主人可以喂,其他人也可以喂。 ``` /**- @Description: 抽象访问者
@Author: zhang lei */ public interface Person { void feed(Cat cat);
void feed(Dog dog); }
/**
- @Description: 具体访问者-宠物主人
@Author: zhang lei */ public class Owner implements Person{ @Override public void feed(Cat cat) {
System.out.println("主人给宠物猫喂食...");
}
@Override public void feed(Dog dog) {
System.out.println("主人给宠物狗喂食...");
} }
/**
- @Description: 具体访问者-陌生人
@Author: zhang lei */ public class Someone implements Person{ @Override public void feed(Cat cat) {
System.out.println("陌生人给宠物猫喂食...");
}
@Override public void feed(Dog dog) {
System.out.println("陌生人给宠物狗喂食...");
} }
/**
- @Description: 抽象元素-宠物
- @Author: zhang lei */ public interface Animal { void accept(Person person); }
/**
- @Description: 具体元素-猫
- @Author: zhang lei
*/
public class Cat implements Animal{
@Override
public void accept(Person person) {
} }person.feed(this);
System.out.println("喵喵喵...");
/**
- @Description: 具体元素-狗
- @Author: zhang lei
*/
public class Dog implements Animal{
@Override
public void accept(Person person) {
} }person.feed(this);
System.out.println("汪汪汪...");
/**
- @Description: 结构对象-宠物主人家
@Author: zhang lei */ public class Home { ArrayList
animals = new ArrayList<>(); public void action(Person person) {
for (Animal animal : animals) {
animal.accept(person);
}
}
public void add(Animal animal) {
animals.add(animal);
} }
- @Description: 访问者模式测试
@Author: zhang lei */ public class VisitorTest { public static void main(String[] args) {
Home home = new Home();
home.add(new Dog());
home.add(new Cat());
Owner owner = new Owner();
home.action(owner);
Someone someone = new Someone();
home.action(someone);
9.4 优缺点
优点:
- 扩展性好,在不修改对象结构中元素的情况下,为对象结构中的元素添加新的功能;
- 复用性好,通过访问者来定义整个对象结构的通用功能,从而提高复用程度;
- 分离无关行为,通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一;
缺点:
- 对象结构变化很困难
在访问者模式中,没增加一个新的元素类,都要在每一个具体类中增加相应的具体操作,这样违背了“开闭原则”; 违反了依赖倒置原则
访问者模式依赖了具体类,而没有依赖抽象类。9.5 应用场景
对象结构相对稳定,但其操作算法经常变化的程序;
- 对象结构中的对象需要提供多种不同且不相关的操作,而且避免让这些操作的变化影响对象的结构。
10. 备忘录模式
10.1 概述
定义:又叫快照模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。
10.2 结构
备忘录模式包含以下主要角色:
- 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,他可以访问备忘录里的所有信息;
- 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些状态给发起人;
- 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
备忘录有两个等效接口:
- 窄接口:管理者对象看到的是备忘录的窄接口,这个接口只允许把备忘录对象传给其他对象;
- 宽接口:与管理者看到的窄接口相反,发起人对象可以看到宽接口,宽接口允许读取所有的数据,以便根据这些数据恢复发起人对象的内部状态。
10.3 案例
【例】游戏挑战BOSS
游戏中的某一个场景,游戏角色有生命力、攻击力、防御力等数据,在挑战BOSS前后状态肯定不一样,我们允许玩家如果感觉挑战BOSS的效果不太理想可以恢复到挑战之前的状态。
实现上述案例,有两种方式:
- “白箱”备忘录模式
- “黑箱”备忘录模式
10.3.1 “白箱”备忘录模式
备忘录角色对任何对象都提供一个接口,即宽接口,备忘录角色内部所存储的状态就对所有对象公开。10.3.2 “黑箱”备忘录模式
备忘录角色对发起人对象提供一个宽接口,而为其他对象提供一个窄接口。在Java语言中,实现双重接口的办法就是将备忘录类设计成发起人类的内部成员类。 ``` /**- @Description: 备忘录角色-备忘录接口
- @Author: zhang lei */ public interface Memento { }
/**
- @Description: 发起人类-游戏角色
@Author: zhang lei */ public class GameRole { 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; }
/**
- 保存角色状态 *
@return */ public Memento saveState() { return new RoleStateMemento(vit, atk, def); }
/**
- 恢复角色状态 *
@param memento */ public void recoverState(Memento memento) { RoleStateMemento roleStateMemento = (RoleStateMemento) memento; this.vit = roleStateMemento.getVit(); this.atk = roleStateMemento.getAtk(); this.def = roleStateMemento.getDef(); }
/**
状态展示 */ public void stateDisplay() { System.out.println(“角色生命值为:” + this.vit); System.out.println(“角色攻击力为:” + this.atk); System.out.println(“角色防御力为:” + this.def); }
private class RoleStateMemento implements Memento { private int vit; //生命值 private int atk; //攻击力 private int def; //防御力
public RoleStateMemento(int vit, int atk, int def) {
this.vit = vit;
this.atk = atk;
this.def = def;
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
} } }
/**
- @Description: 管理者角色-角色状态管理者
@Author: zhang lei */ public class RoleStateCaretaker { private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
} }
- @Description: 备忘录模式测试
@Author: zhang lei */ public class MementoTest { public static void main(String[] args) {
System.out.println("挑战BOSS前...");
GameRole gameRole = new GameRole();
gameRole.initState();
gameRole.stateDisplay();
//保存进度
RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
roleStateCaretaker.setMemento(gameRole.saveState());
System.out.println("挑战BOSS后...");
gameRole.fight();
gameRole.stateDisplay();
//恢复之前状态
System.out.println("恢复之前状态...");
gameRole.recoverState(roleStateCaretaker.getMemento());
gameRole.stateDisplay();
10.4 优缺点
优点:
- 提供了一种可恢复状态的机制。当用户需要时可以比较恢复到某个历史状态;
- 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能访问这些状态信息;
- 简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有的状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。
缺点:
资源消耗大。若果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内部资源。
10.5 使用场景
需要保存与恢复数据的场景,如单机游戏中的中间结果存档功能;
- 需要提供一个可回滚的操作场景,如Word、记事本等软件中的Ctrl+Z功能,还有数据库中事务。
11. 解释器模式
11.1 概述
定义:给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识解释语言中的句子。
在解释器模式中,我们需要将待解决的问题,提取出规则,抽象成为一种“语言”。比如加减法运算,规则为:由数值和+-符号组成的合法序列,类似“1+5-3”这种语言的句子。 解释器就是要解析出来语句的含义。
11.2 结构
解释器模式包含以下角色:
- 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解析操作,主要包含解释方法interpret();
- 终结符号表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应;
- 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每一条规则都对应一个非终结符号;
- 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
客户端(Client)角色:主要任务是将需要分析的句子或表达式转换成用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色简介访问解释器的解释方法。
11.3 案例
【例】设计实现加减法的软件 ``` /**
- @Description: 环境角色
@Author: zhang lei */ public class Context { private Map
map = new HashMap (); public void assign(Variable variable, Integer value){
map.put(variable,value);
}
public int getValue(Variable variable){
Integer value = map.get(variable);
return value;
} }
/**
- @Description: 抽象表达式
- @Author: zhang lei */ public abstract class AbstractExpression { public abstract int interpret(Context context); }
/**
- @Description: 终结符表达式角色
@Author: zhang lei */ public class Value extends AbstractExpression { private int value;
public Value(int value) {
this.value = value;
}
@Override public int interpret(Context context) {
return value;
}
@Override public String toString() {
return new Integer(value).toString();
} }
/**
- @Description: 非终结符表达式角色-加法
@Author: zhang lei */ public class Add extends AbstractExpression { private AbstractExpression left; private AbstractExpression right;
public Add(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override public int interpret(Context context) {
return left.interpret(context) + right.interpret(context);
}
@Override public String toString() {
return "(" + left.toString() + "+" + right.toString() + ")";
} }
/**
- @Description: 非终结符表达式角色-减法
@Author: zhang lei */ public class Subtraction extends AbstractExpression{ private AbstractExpression left; private AbstractExpression right;
public Subtraction(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override public int interpret(Context context) {
return left.interpret(context) - right.interpret(context);
}
@Override public String toString() {
return "(" + left.toString() + "-" + right.toString() +")";
} }
/**
- @Description: 终结符表达式角色-变量表达式
@Author: zhang lei */ 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;
} }
- @Description: 解释器模式测试
@Author: zhang lei */ public class InterpreterTest { public static void main(String[] args) {
Context context = new Context();
Variable a = new Variable("a");
Variable b = new Variable("b");
Variable c = new Variable("c");
Variable d = new Variable("d");
Variable e = new Variable("e");
context.assign(a, 1);
context.assign(b, 2);
context.assign(c, 3);
context.assign(d, 4);
context.assign(e, 5);
AbstractExpression expression = new Subtraction(new Add(a, e), new Subtraction(new Add(b, c), d));
System.out.println(expression + "=" + expression.interpret(context));
11.4 优缺点
优点:
- 易于改变和扩展文法。
由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。 - 实现文法较为容易
在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂。 - 增加新的解释表达式较为方便
如果用户需要增加新的解释表达式只需要对应新增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合“开闭原则”。
缺点: