设计模式
- 中介者模式
-
基本介绍
- 中介者模式使用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
- 中介者模式属于行为模式,使代码易于维护。
- 比如mvc模式,controller是model和v的中介者,在前后端交互时起到中间人的作用。
-
代码
public abstract class Mediator { //将给中介者对象,加入到集合中 public abstract void Register(String colleagueName, Colleague colleague); //接收消息, 具体的同事对象发出 public abstract void GetMessage(int stateChange, String colleagueName); } //具体的中介者类 public class ConcreteMediator extends Mediator { //集合,放入所有的同事对象 private HashMap<String, Colleague> colleagueMap; public ConcreteMediator() { colleagueMap = new HashMap<String, Colleague>(); } //注册具体的同事类 @Override public void Register(String colleagueName, Colleague colleague) { colleagueMap.put(colleagueName, colleague); } //具体中介者的核心方法 //1. 根据得到消息,完成对应任务 //2. 中介者在这个方法,协调各个具体的同事对象,完成任务 @Override public void GetMessage(int stateChange, String colleagueName) { //处理闹钟发出的消息 if (colleagueMap.get(colleagueName) instanceof Alarm) { if (stateChange == 0) { ((CoffeeMachine) (colleagueMap.get( "CoffeeMachine"))).StartCoffee(); ((TV) (colleagueMap.get("TV"))).StartTv(); } else if (stateChange == 1) { ((TV) (colleagueMap.get("TV"))).StopTv(); } } else if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) { ((Curtains) (colleagueMap.get("Curtains"))) .UpCurtains(); } else if (colleagueMap.get(colleagueName) instanceof TV) {//如果TV发现消息 } else if (colleagueMap.get(colleagueName) instanceof Curtains) { //如果是以窗帘发出的消息,这里处理... } } } //同事抽象类 public abstract class Colleague { private Mediator mediator; public String name; public Colleague(Mediator mediator, String name) { this.mediator = mediator; this.name = name; } public Mediator GetMediator() { return this.mediator; } public abstract void SendMessage(int stateChange); } //具体的同事类 public class Alarm extends Colleague { //构造器 public Alarm(Mediator mediator, String name) { super(mediator, name); //在创建Alarm 同事对象时,将自己放入到ConcreteMediator 对象中[集合] mediator.Register(name, this); } public void SendAlarm(int stateChange) { SendMessage(stateChange); } @Override public void SendMessage(int stateChange) { //调用的中介者对象的getMessage this.GetMediator().GetMessage(stateChange, this.name); } } public class Curtains extends Colleague { public Curtains(Mediator mediator, String name) { super(mediator, name); mediator.Register(name, this); } @Override public void SendMessage(int stateChange) { this.GetMediator().GetMessage(stateChange, this.name); } public void UpCurtains() { System.out.println("I am holding Up Curtains!"); } } public class CoffeeMachine extends Colleague { public CoffeeMachine(Mediator mediator, String name) { super(mediator, name); mediator.Register(name, this); } @Override public void SendMessage(int stateChange) { this.GetMediator().GetMessage(stateChange, this.name); } public void StartCoffee() { System.out.println("It's time to startcoffee!"); } public void FinishCoffee() { System.out.println("After 5 minutes!"); System.out.println("Coffee is ok!"); SendMessage(0); } } public class TV extends Colleague { public TV(Mediator mediator, String name) { super(mediator, name); mediator.Register(name, this); } @Override public void SendMessage(int stateChange) { this.GetMediator().GetMessage(stateChange, this.name); } public void StartTv() { System.out.println("It's time to StartTv!"); } public void StopTv() { System.out.println("StopTv!"); } } public class Client{ public static void main(String[] args) { //创建一个中介者对象 Mediator mediator = new ConcreteMediator(); //创建Alarm 并且加入到 ConcreteMediator 对象的HashMap Alarm alarm = new Alarm(mediator, "Alarm"); //创建了CoffeeMachine 对象,并 且加入到 ConcreteMediator 对象的HashMap CoffeeMachine coffeeMachine = new CoffeeMachine(mediator, "CoffeeMachine"); new TV(mediator, "TV"); new Curtains(mediator, "Curtains"); //让闹钟发出消息 alarm.SendAlarm(0); coffeeMachine.FinishCoffee(); alarm.SendAlarm(1); } }
-
中介者模式注意事项和细节
- 多个类相互耦合,会形成网状结构,使用中介者模式将网状结构分离为星型结构,进行解耦。
- 减少类间依赖,降低耦合,符合迪米特法则
- 中介者承担了较多的责任,一旦中介者出现问题,整个系统就会受到影响。
- 如果设计不当,中介者对象本身变的过于复杂,这点在实际使用时,要特别注意。
-
- 备忘录模式
-
基本介绍:
- 备忘录模式在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
- 可以这样理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。而软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作。
- 备忘录模式属于行为型模式
-
代码
public class GameRole { //攻击力 private int vit; //防御力 private int def; //创建一个备忘录对象并将当前的属性赋值给他 public Memento createMemento() { return new Memento(vit, def); } public void recoverGameroleFromMenmento(Memento memento) { this.vit=memento.getVit(); this.def=memento.getDef(); } //显示当前的属性游戏角色的属性 public void display() { System.out.println("当前的角色的攻击力为:"+this.vit); System.out.println("当前的角色的防御力为:"+this.def); } public int getVit() { return vit; } public void setVit(int vit) { this.vit = vit; } public int getDef() { return def; } public void setDef(int def) { this.def = def; } } public class Memento { //攻击力 private int vit; //防御力 private int def; public Memento(int vit, int def) { this.vit = vit; this.def = def; } public int getVit() { return vit; } public int getDef() { return def; } public void setVit(int vit) { this.vit = vit; } public void setDef(int def) { this.def = def; } } public class Careator { private Memento memento; public Memento getMement() { return memento; } public void setMement(Memento memento) { this.memento = memento; } } public class Client { public static void main(String[] args) { Careator careator=new Careator(); GameRole gameRole=new GameRole(); gameRole.setVit(100); gameRole.setDef(100); System.out.println("战斗之前的状态"); gameRole.display(); //保存当前的状态 careator.setMement(gameRole.createMemento()); gameRole.setVit(0); gameRole.setDef(0); System.out.println("战斗之后的状态"); gameRole.display(); //恢复之前的状态 System.out.println("恢复之前的状态"); gameRole.recoverGameroleFromMenmento(careator.getMement()); gameRole.display(); } }
-
备忘录模式的注意事项和细节
- 给用户提供一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
- 实现了信息的封装,使得用户不需要关心状态的保存细节。
- 如果类的成员变量过多,势必会占用较大的资源,而且每一次保存都会消耗一定的内存,这个需要注意。
- 适用的场景:1、后悔药2.打游戏时的存档3.windows的ctrl+z4.IE中的后退5.数据库的事务管理
- 为了节约内存,备忘录模式可以和原型模式配合使用。
-
- 解释器模式
-
基本介绍
- 在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器。
- 解释器模式:是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释来解释语言中的句子(表达式)
- 应用场景:
- 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
- 一些重复出现的问题可以用一种简单的语言来表达
- 一个简单语法需要解释的场景
- 这样的例子还有,比如编译器、运算表达式、计算正则表达式、机器人等。
-
代码
/** * 抽象类表表达式,通过map的键值对可以获取到 * 变量的值 * @author Administrator * */ public abstract class Expression { //解释公式和数值,key就是公式value就是具体的值 public abstract int interpret(Map<String,Integer> var); } /** * 变量的解释器 * @author Administrator * */ public class VarExpression extends Expression { private String key; public VarExpression(String key) { this.key = key; } //根据变量的名称返回变量的值 @Override public int interpret(Map<String, Integer> var) { return var.get(key); } } /** * 抽象运算符号解析器,这里每个运算符号, * 都只和自己左右的两个数字有关系。 * 但左右两个数字有可能也是一个解析的结果, * 无论何种类型,都是expression的实现类 * @author Administrator * */ public class SymbolExpression extends Expression { protected Expression left; protected Expression right; public SymbolExpression(Expression left, Expression right) { this.left = left; this.right = right; } /** * 因为具体的实现是让子类来进行的所以此处为空实现 */ @Override public int interpret(Map<String, Integer> var) { return 0; } } public class SubExpression extends SymbolExpression { public SubExpression(Expression left, Expression right) { super(left, right); } @Override public int interpret(Map<String, Integer> var) { return super.left.interpret(var)-super.right.interpret(var); } } public class AddExpression extends SymbolExpression { public AddExpression(Expression left, Expression right) { super(left, right); } @Override public int interpret(Map<String, Integer> var) { return super.left.interpret(var)+super.right.interpret(var); } } public class Calculator { // 定义表达式 private Expression expression; // 构造函数传参,并解析 public Calculator(String expStr) { // expStr = a+b // 安排运算先后顺序 Stack<Expression> stack = new Stack<>(); // 表达式拆分成字符数组 char[] charArray = expStr.toCharArray();// [a, +, b] Expression left = null; Expression right = null; //遍历我们的字符数组, 即遍历 [a, +, b] //针对不同的情况,做处理 for (int i = 0; i < charArray.length; i++) { switch (charArray[i]) { case '+': // left = stack.pop();// 从stack取出left => "a" // 取出右表达式 "b" right = new VarExpression(String.valueOf(charArray[++i])); // 然后根据得到left 和 right 构建 AddExpresson加入stack stack.push(new AddExpression(left, right)); break; case '-': // left = stack.pop(); right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new SubExpression(left, right)); break; default: //如果是一个 Var 就创建要给 VarExpression 对象,并push到 stack stack.push(new VarExpression(String.valueOf(charArray[i]))); break; } } //当遍历完整个 charArray 数组后,stack 就得到最后Expression this.expression = stack.pop(); } public int run(Map<String, Integer> var) { //最后将表达式a+b和 var = {a=10,b=20} //然后传递给expression的interpreter进行解释执行 return this.expression.interpret(var); } } public class Client { public static void main(String[] args) { Calculator calculator=new Calculator("a+b-c"); Map<String,Integer> map = new HashMap<String,Integer>(); map.put("a", 10); map.put("b", 5); map.put("c", 9); System.out.println(calculator.run(map)); } }
-
解释器模式的注意事项和细节
- 当有一个语言需要解释执行时,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有更好的扩展性。
- 应用场景:编译器、运算表达式、正则表达式、机器人等。
- 使用解释器可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归用法,将会导致调式非常复杂、效率可能降低。
-
- 状态模式
-
基本介绍:
- 状态模式:它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换。
- 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变该类。
-
代码
/** * 状态接口 * @author Administrator * */ public interface State { //扣除积分 public void deduceMoney(); //是否抽中奖品 public boolean raffle(); //发放奖品 public void dispensePrize(); } /** * 不能抽奖的状态 * @author Administrator * */ public class NoRaffleState implements State { private Activity activity; public NoRaffleState(Activity activity) { this.activity = activity; } //当前状态可以扣积分然后改变状态为可以抽奖状态 @Override public void deduceMoney() { System.out.println("扣除50积分你可以抽奖了"); activity.setState(activity.getCanRaffleState()); } //挡墙状态不能抽奖 @Override public boolean raffle() { System.out.println("扣了积分才可以抽奖"); return false; } @Override public void dispensePrize() { System.out.println("当前状态不能发送奖品"); } } public class CanRaffleState implements State { private Activity activity; public CanRaffleState(Activity activity) { this.activity = activity; } @Override public void deduceMoney() { System.out.println("已经扣除积分"); } @Override public boolean raffle() { System.out.println("正在抽奖请稍等"); int num=new Random().nextInt(10); //1/10的找中奖机会 if(num==0) { System.out.println("恭喜中奖了"); activity.setState(activity.getDispenseState()); return true; } System.out.println("很遗憾没有中奖"); //状态改为不能抽奖 activity.setState(activity.getNoRaffleState()); return false; } @Override public void dispensePrize() { System.out.println("当前状态不能发放奖品"); } } public class DispenseState implements State { private Activity activity; public DispenseState(Activity activity) { this.activity = activity; } @Override public void deduceMoney() { System.out.println("不能扣除积分"); } @Override public boolean raffle() { System.out.println("不能抽奖"); return false; } @Override public void dispensePrize() { if(activity.getCount()>0) { System.out.println("正在领取奖品"); activity.setState(activity.getNoRaffleState()); }else { System.out.println("很遗憾奖品发送完了"); activity.setState(activity.getDispensOutState()); } } } /** * 抽奖活动结束 * @author Administrator * */ public class DispensOutState implements State { @Override public void deduceMoney() { System.out.println("奖品发送完了请下次再参加"); } @Override public boolean raffle() { System.out.println("奖品发送完了请下次再参加"); return false; } @Override public void dispensePrize() { System.out.println("奖品发送完了请下次再参加"); } } public class Activity { //表示活动当前的状态 private State state=null; //奖品的数量 private int count=0; private State noRaffleState=new NoRaffleState(this); private State canRaffleState=new CanRaffleState(this); private State dispenseState=new DispenseState(this); private State dispensOutState=new DispensOutState(); //构造器 //1. 初始化当前的状态为 noRafflleState(即不能抽奖的状态) //2. 初始化奖品的数量 public Activity( int count) { this.state = getNoRaffleState(); this.count = count; } public int getCount() { int curCount = count; count--; return curCount; } //扣除积分 public void debuctMoney() { if(count>0) { state.deduceMoney(); }else { setState(getDispensOutState());; } } public void raffle() { if(state.raffle()) { state.dispensePrize(); } } public State getState() { return state; } public void setState(State state) { this.state = state; } public State getNoRaffleState() { return noRaffleState; } public void setNoRaffleState(State noRaffleState) { this.noRaffleState = noRaffleState; } public State getCanRaffleState() { return canRaffleState; } public void setCanRaffleState(State canRaffleState) { this.canRaffleState = canRaffleState; } public State getDispenseState() { return dispenseState; } public void setDispenseState(State dispenseState) { this.dispenseState = dispenseState; } public State getDispensOutState() { return dispensOutState; } public void setDispensOutState(State dispensOutState) { this.dispensOutState = dispensOutState; } } public class Client { public static void main(String[] args) { Activity activity=new Activity(1); for (int i = 1; i < 11; i++) { System.out.println("第"+i+"次抽奖"); activity.debuctMoney(); activity.raffle(); } } }
-
状态模式的注意事项和细节
-
代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中。
-
方便维护。将容易产生问题的if-else语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多的if-else语句,而且容易出错。
-
符合“开闭原则”。容易增删状态。
-
会产生很多类。每个状态都要对应一类,当状态过多时会产生很多类,加大维护难度。
-
当一个时间或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同行为的时候,可以考虑状态模式。
-
-