目录
4.行为型模式
1.模板方法模式
2.策略模式
3.命令模式
4.责任链模式
5.状态模式
6.观察者模式
7.中介者模式
8.迭代器模式
9.访问者模式
扩展
10.备忘录模式
11.解释器模式
5.设计模式结构图
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
行为型模式中除了模板方法模式和解释器模式以外都是对象行为模式
概述:定义一个算法结构,而将一些步骤延迟到子类实现。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。
结构:
优点:
缺点:
使用场景:1、算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。2、需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
案例:炒菜
炒菜的步骤是固定的T分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。现通过模板方法模式来用代码模拟。类图如下:
//抽象类:模板方法和基本方法
public abstract class AbstractClass {
//模板方法
public final 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 ConcreteClass_Caixin extends AbstractClass{
@Override
public void pourVegetable() {
System.out.println("下锅的蔬菜是菜心");
}
@Override
public void pourSauce() {
System.out.println("下锅的酱料是蒜蓉");
}
}
//炒包菜类
public class ConcreteClass_Baocai extends AbstractClass{
@Override
public void pourVegetable() {
System.out.println("下锅的蔬菜是包菜");
}
@Override
public void pourSauce() {
System.out.println("下锅的酱料是辣椒");
}
}
public class Client {
public static void main(String[] args) {
//炒包菜
ConcreteClass_Baocai baocai=new ConcreteClass_Baocai();
//调用父类的炒菜功能
baocai.cookProcess();
System.out.println("==========");
ConcreteClass_Caixin caixin = new ConcreteClass_Caixin();
caixin.cookProcess();
}
}
概述:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。
结构:
优点:
缺点:
使用场景:
主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
案例:促销活动
//抽象策略类
public interface Strategy {
void show();
}
//具体策略类,封装算法
public class StrategyA implements Strategy{
@Override
public void show() {
System.out.println("买一送一");
}
}
//具体策略类,封装算法
public class StrategyB implements Strategy{
@Override
public void show() {
System.out.println("满200减50");
}
}
//具体策略类,封装算法
public class StrategyC implements Strategy{
@Override
public void show() {
System.out.println("满1000加1元换购一件200元以下商品");
}
}
//销售员:环境类
public class SalesMan {
//聚合策略类对象
private Strategy strategy;
public SalesMan(Strategy strategy) {
this.strategy = strategy;
}
//由促销员展示促销活动给用户
public void salesManShow(){
strategy.show();
}
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
}
public class Client {
public static void main(String[] args) {
//春节来了。使用春节促销活动
SalesMan salesMan=new SalesMan(new StrategyA());
//展示促销活动
salesMan.salesManShow();
System.out.println("===========");
//中秋节来了。使用中秋节促销活动
salesMan.setStrategy(new StrategyB());
//展示促销活动
salesMan.salesManShow();
System.out.println("===========");
//圣诞节来了。使用圣诞节促销活动
salesMan.setStrategy(new StrategyC());
//展示促销活动
salesMan.salesManShow();
}
}
请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
概念:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
结构:
优点:
缺点:
使用场景:
主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
案例:点餐案例
//订单类
public class Order {
//餐桌号码
private int diningTable;
//下单的餐具及份数
private Map foodDir=new HashMap<>();
public int getDiningTable() {
return diningTable;
}
public void setDiningTable(int diningTable) {
this.diningTable = diningTable;
}
public Map getFoodDir() {
return foodDir;
}
public void setFood(String name,int num) {
foodDir.put(name,num);
}
}
//厨师类:接收者
public class SeniorChef {
public void makeFood(String name,int num){
System.out.println(num+"份"+name);
}
}
//抽象命令类
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.getDiningTable()+"桌的订单:");
Map foodDir = order.getFoodDir();
//遍历map集合
Set keys = foodDir.keySet();
for (String foodName : keys) {
receiver.makeFood(foodName,foodDir.get(foodName));
}
System.out.println(order.getDiningTable()+"的饭准备完毕");
}
}
//服务员类:请求者类
public class Waitor {
//持有多个命令对象
private List commands=new ArrayList<>();
public void setCommand(Command cmd) {
//将cmd对象存储到list集合中
commands.add(cmd);
}
//发起命令的功能:喊一句订单来了
public void orderUp(){
System.out.println("美女服务员:大厨,新订单来了");
for (Command command : commands) {
if (command!=null){
command.execute();
}
}
}
}
public class Client {
public static void main(String[] args) {
//创建一个订单对象
Order order = new Order();
order.setDiningTable(1);
order.setFood("西红柿鸡蛋面",1);
order.setFood("小杯可乐",2);
//创建一个订单对象
Order order2 = new Order();
order2.setDiningTable(2);
order2.setFood("尖椒肉丝面",1);
order2.setFood("小杯雪碧",1);
//创建厨师对象
SeniorChef receiver=new SeniorChef();
//创建命令对象
OrderCommand cmd1=new OrderCommand(receiver,order);
OrderCommand cmd2=new OrderCommand(receiver,order2);
//创建调用者:服务员对象
Waitor invoke=new Waitor();
invoke.setCommand(cmd1);
invoke.setCommand(cmd2);
//让我们服务元发起命令
invoke.orderUp();
}
}
在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据自己要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这增加了难度。这样的例子还有很多,如找领导出差报销、生活中的“击鼓传花"游戏
概念:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
结构:
优点:
缺点:
使用场景:
主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
案例:请假
现需要开发一个请假流程控制系统。 请假一天以下的假只需要小组长同意即可;请假1天到3天的假还需要部i门经理同意;请求3天到7天还需要总经理同意才行。
//请假条
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 {
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;
}
public Handler(int numStart, int numEnd) {
this.numStart = numStart;
this.numEnd = numEnd;
}
//设置上级领导对象
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
//各级领导处理请假条的方法
protected abstract void handleLeave(LeaveRequest leaveRequest);
//提交请假条
public final void submit(LeaveRequest leaveRequest) {
//该领导进行审批
this.handleLeave(leaveRequest);
if (this.nextHandler != null && leaveRequest.getNum() > this.numEnd) {
//提交给上级领导审批
this.nextHandler.submit(leaveRequest);
} else {
System.out.println("审批流程结束");
}
}
}
//小组长类:具体处理着
public class GroupLeader extends Handler{
public GroupLeader(){
super(0,Handler.NUM_ONE);
}
@Override
protected void handleLeave(LeaveRequest leaveRequest) {
System.out.println(leaveRequest.getName()+"请假"+leaveRequest.getNum()+"天"+leaveRequest.getContent());
System.out.println("小组长审批:同意");
}
}
//部门经理类:具体处理着
public class Manager extends Handler{
public Manager(){
super(Handler.NUM_ONE,Handler.NUM_THREE);
}
@Override
protected void handleLeave(LeaveRequest leaveRequest) {
System.out.println(leaveRequest.getName()+"请假"+leaveRequest.getNum()+"天"+leaveRequest.getContent());
System.out.println("部门经理审批:同意");
}
}
//总经理类:具体处理着
public class GeneralManager extends Handler{
public GeneralManager(){
super(Handler.NUM_THREE,Handler.NUM_SEVEN);
}
@Override
protected void handleLeave(LeaveRequest leaveRequest) {
System.out.println(leaveRequest.getName()+"请假"+leaveRequest.getNum()+"天"+leaveRequest.getContent());
System.out.println("总经理审批:同意");
}
}
public class Client {
public static void main(String[] args) {
//创建一个请假条对象
LeaveRequest leaveRequest = new LeaveRequest("小明", 7, "身体不适");
//创建各级领导对象
GroupLeader groupLeader = new GroupLeader();
Manager manager = new Manager();
GeneralManager generalManager = new GeneralManager();
//设置处理者链
groupLeader.setNextHandler(manager);
manager.setNextHandler(generalManager);
//小明提交请假申请
groupLeader.submit(leaveRequest);
}
}
概念:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
结构:
优点:
缺点:
使用场景:
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
案例:电梯
原案例:
[例]通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理.例如,如果电梯门]现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。
//电梯接口
public interface ILift {
//定义四个电梯的状态的常量
int OPENING_STATE=1;
int CLOSING_STATE=2;
int RUNNING_STATE=3;
int STOPPING_STATE=4;
//设置电梯状态的功能
void setState(int state);
//电梯操作的功能
void open();
void close();
void run();
void stop();
}
//电梯类
public class Lift implements ILift {
//声明一个记录当前电梯的状态
private int state;
@Override
public void setState(int state) {
this.state = state;
}
//执行开门动作
@Override
public void open() {
switch (state) {//表示当前电梯状态
case OPENING_STATE:
//什么是都不做
break;
case CLOSING_STATE:
System.out.println("电梯门打开了");
setState(OPENING_STATE);
break;
case RUNNING_STATE:
break;
case STOPPING_STATE:
System.out.println("电梯门打开了");
setState(OPENING_STATE);
break;
}
}
//执行关门动作
@Override
public void close() {
switch (state) {//表示当前电梯状态
case OPENING_STATE:
System.out.println("电梯门关闭");
setState(CLOSING_STATE);
break;
case CLOSING_STATE:
break;
case RUNNING_STATE:
//运行的时候是关闭的
break;
case STOPPING_STATE:
break;
}
}
//执行运行动作
@Override
public void run() {
switch (state) {//表示当前电梯状态
case OPENING_STATE:
//门开启的时候不能运行
break;
case CLOSING_STATE:
System.out.println("电梯运行");
setState(RUNNING_STATE);
break;
case RUNNING_STATE:
break;
case STOPPING_STATE://停止
System.out.println("电梯运行");
setState(RUNNING_STATE);
break;
}
}
//执行停止动作
@Override
public void stop() {
switch (state) {//表示当前电梯状态
case OPENING_STATE:
//门开启的时候是停止的
break;
case CLOSING_STATE:
break;
case RUNNING_STATE:
System.out.println("电梯停止");
setState(STOPPING_STATE);
break;
case STOPPING_STATE:
break;
}
}
}
public class Client {
public static void main(String[] args) {
//创建电梯对象
Lift lift = new Lift();
//设置当前电梯状态
lift.setState(ILift.CLOSING_STATE);
//打开
lift.open();
//关闭
lift.close();
//运行
lift.run();
//停止
lift.stop();
}
}
问题分析:
状态模式案例:
//抽象状态类
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();
}
//环境角色
public class Context {
//定义对应状态对象的常量
public final static OpeningState OPENING_STATE=new OpeningState();
public final static ClosingState CLOSING_STATE=new ClosingState();
public final static RuningState RUNING_STATE=new RuningState();
public final static StopingState STOPING_STATE=new StopingState();
//定义一个当前电梯状态变量
private LiftState liftState;
public LiftState getLiftState() {
return liftState;
}
//设置当前状态对象
public void setLiftState(LiftState liftState) {
this.liftState = liftState;
//设置当前状态中的Context
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();
}
}
//电梯开启状态
public class OpeningState extends LiftState {
//当前状态要执行的方法
@Override
public void open() {
System.out.println("电梯门开启");
}
@Override
public void close() {
//修改状态
super.context.setLiftState(Context.CLOSING_STATE);
//调用当前状态中的context照片呢个的close方法
super.context.close();
}
@Override
public void run() {
//电梯门开启不能run
}
@Override
public void stop() {
//开门的时候本身就是停止的
}
}
//电梯关闭状态
public class ClosingState extends LiftState{
@Override
public void open() {
super.context.setLiftState(Context.OPENING_STATE);
super.context.open();
}
@Override
public void close() {
System.out.println("电梯门关闭");
}
@Override
public void run() {
super.context.setLiftState(Context.RUNING_STATE);
super.context.run();
}
@Override
public void stop() {
super.context.setLiftState(Context.STOPING_STATE);
super.context.getLiftState().stop();
}
}
//电梯运行状态
public class RuningState extends LiftState{
@Override
public void open() {
//运行的时候不可以开电梯门
}
@Override
public void close() {
//运行的时候电梯门肯定已经关闭了
}
@Override
public void run() {
System.out.println("电梯运行");
}
@Override
public void stop() {
super.context.setLiftState(Context.STOPING_STATE);
super.context.stop();
}
}
//电梯停止状态
public class StopingState extends LiftState{
@Override
public void open() {
super.context.setLiftState(Context.OPENING_STATE);
super.context.getLiftState().open();
}
@Override
public void close() {
super.context.setLiftState(Context.CLOSING_STATE);
super.context.getLiftState().close();
}
@Override
public void run() {
super.context.setLiftState(Context.RUNING_STATE);
super.context.getLiftState().run();
}
@Override
public void stop() {
System.out.println("电梯停止");
}
}
public class Client {
public static void main(String[] args) {
//创建环境角色对象
Context context=new Context();
//设置当前电梯状态
context.setLiftState(new ClosingState());
context.open();
context.run();
context.close();
context.stop();
}
}
概念:又被称为发布-订阅(Publish/Subscribe) 模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。 这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
结构:
优点:
缺点:
使用场景:
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
案例:微信公众号
在使用微信公众号时,大家都会有这样的体验,当你关注的公众号中有新内容更新的话,它就会推送给关注公众号的微信用户端。我们使用观察者模式来模拟这样的场景,微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了程序猿这个公众号。
//抽象主题角色类
public interface Subject {
//添加订阅者:观察者对象
void attach(Observer observer);
//删除订阅者:
void detach(Observer observer);
//通知订阅者,更新消息
void notify(String message);
}
//抽象观察者类
public interface Observer {
void update(String message);
}
//具体主题角色类
public class SubscriptionSubject implements Subject {
//定义一个集合,用来存储多个观察者对象
private List weiXinUserList = new ArrayList<>();
@Override
public void attach(Observer observer) {
weiXinUserList.add(observer);
}
@Override
public void detach(Observer observer) {
weiXinUserList.remove(observer);
}
@Override
public void notify(String message) {
//遍历集合
for (Observer observer : weiXinUserList) {
//调用观察者对象中的update方法
observer.update(message);
}
}
}
//具体的观察者角色类
public class WeiXinUser implements Observer{
private String name;
public WeiXinUser(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name+"-"+message);
}
}
public class Client {
public static void main(String[] args) {
//创建公众号对象
SubscriptionSubject subscriptionSubject = new SubscriptionSubject();
//订阅公众号
subscriptionSubject.attach(new WeiXinUser("孙悟空"));
subscriptionSubject.attach(new WeiXinUser("猪悟能"));
subscriptionSubject.attach(new WeiXinUser("沙悟净"));
//公众号更新,发出消息给订阅者:观察者
subscriptionSubject.notify("雙馬程序猿的专栏更新了");
}
}
一般来说, 同事类之间的关系是比较复杂的,多个同事类之间互相关联时,他们之间的关系会呈现为复杂的网状结构,这是一种过度耦合的架构,即不利于类的复用,也不稳定。例如在下左图中,有六个同事类对象,假如对象1发生变化,那么将会有4个对象受到影响。如果对象2发生变化,那么将会有5个对象受到影响。也就是说,同事类之间直接关联的设计是不好的。
如果引入中介者模式,那么同事类之间的关系将变为星型结构,从下右图中可以看到,任何一个类的变动,只会影响的类本身,以及中介者,这样就减小了系统的耦合。一个好的设计,必定不会把所有的对象关系处理逻辑封装在本类中,而是使用一个专]的类来管理那些不属于自己的行为。
概念: 用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
结构:
优点:
缺点:
当同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护。
使用场景:
系统中对象之间存在复杂的引用关系,系统结构混乱且难以理解。
当想创建一个运行于多个类之间的对象,又不想生成新的子类时。
主要解决:对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。
案例:
现在租房基本都是通过房屋中介,房主将房屋托管给房屋中介,而租房者从房屋中介获取房屋信息。房屋中介充当租房者与房屋所有者之间的中介者。
//抽象中介者类
public abstract class Mediator {
public abstract void constact(String message,Person person);
}
//抽象同事类
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 constact(String message){
mediator.constact(message,this);
}
//获取信息的方法
public void getMessage(String message){
System.out.println("租房者"+name+"获取到的信息是:"+message);
}
}
//具体同事角色类
public class HouseOwner extends Person{
public HouseOwner(String name, Mediator mediator) {
super(name, mediator);
}
//和中介联系(沟通)
public void constact(String message){
mediator.constact(message,this);
}
//获取信息的方法
public void getMessage(String message){
System.out.println("房主"+name+"获取到的信息是:"+message);
}
}
//具体的中介者角色类
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;
}
@Override
public void constact(String message, Person person) {
if (person==houseOwner){
tenant.getMessage(message);
}else {
houseOwner.getMessage(message);
}
}
}
public class Client {
public static void main(String[] args) {
//创建中介者对象
MediatorStructure mediatorStructure=new MediatorStructure();
//创建租房者对象
Tenant tenant = new Tenant("李四", mediatorStructure);
//创建房主对象
HouseOwner houseOwner = new HouseOwner("张三", mediatorStructure);
//中介者要知道具体的房主和租房者
mediatorStructure.setTenant(tenant);
mediatorStructure.setHouseOwner(houseOwner);
tenant.constact("我要租三室的房子");
houseOwner.constact("我这里有三室的房子你要租吗?");
}
}
概念:供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
结构:
优点:
缺点:
使用场景:
主要解决:不同的方式来遍历整个整合对象。
案例:定义一个可以存储学生对象的容器对象,将遍历该容器的功能交由迭代器实现,涉及到的类如下:
public class Student {
private String name;
private String number;
//有参无参、getset、toSting
}
//抽象迭代器角色接口
public interface StudentIterator {
//判断是否还有元素
boolean hasNext();
//获取下一个元素
Student next();
}
//具体迭代器角色类
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=new ArrayList<>();
@Override
public void addStudent(Student student) {
list.add(student);
}
@Override
public void removeStudent(Student student) {
list.remove(student);
}
@Override
public StudentIterator getStudentIterator() {
return new StudentIteratorImpl(list);
}
}
public class Client {
public static void main(String[] args) {
//创建聚合对象
StudentAggregateImpl aggregate= new StudentAggregateImpl();
//添加元素
aggregate.addStudent(new Student("张三","003"));
aggregate.addStudent(new Student("李四","004"));
aggregate.addStudent(new Student("王五","005"));
aggregate.addStudent(new Student("赵六","006"));
//遍历聚合对象
//获取迭代器对象
StudentIterator iterator=aggregate.getStudentIterator();
//遍历
while (iterator.hasNext()){
//获取元素
Student student = iterator.next();
System.out.println(student.toString());
}
}
}
概念:主要将数据结构与数据操作分离,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。
结构:
优点:
缺点:
使用场景:
主要解决:稳定的数据结构和易变的操作耦合问题。
案例:给动物喂食
现在养宠物的人特别多,我们就以这个为例,当然宠物还分为狗,猫等,要给宠物喂食的话,主人可以喂,其他人也可以喂食。
//抽象访问者角色:给宠物喂食的人
public interface Person {
//喂食的对象
void feed(Cat cat);
void feed(Dog dog);
}
//抽象元素角色:动物抽象类
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 class Dog implements Animal {
@Override
public void accept(Person person) {
person.feed(this);//访问者给宠物狗喂食
System.out.println("狗狗吃食");
}
}
//具体访问者角色:主人
public class Owner implements Person{
@Override
public void feed(Cat cat) {
System.out.println("主人喂食猫");
}
@Override
public void feed(Dog dog) {
System.out.println("主人喂食狗");
}
}
//具体访问者角色:路人
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 nodeList=new ArrayList<>();
//添加元素功能
public void add(Animal animal){
nodeList.add(animal);
}
//
public void action(Person person){
//遍历集合获取每一个元素
for (Animal animal : nodeList) {
animal.accept(person);
}
}
}
public class Client {
public static void main(String[] args) {
//创建home对象
Home home = new Home();
//添加元素到home对象中
home.add(new Dog());
home.add(new Cat());
//创建主人对象
Owner owner=new Owner();
//宠物饿了,主人去喂食所有宠物
home.action(owner);
}
}
变量被声明时的类型叫做变量的静态类型,有些人又把静态类型叫做明显类型;而变量所引用的对象的真实类型又叫做变量的实际类型。比如Map map = new HashMap() ,map变量的静态类型是Map ,实际类型是HashMap 。根据对象的类型而对方法进行的选择,就是分派(Dispatch),分派(Dispatch)又分为两种,即静态分派和动态分派。
静态分派(Static Dispatch)发生在编译时期, 分派根据静态类型信息发生。静态分派对于我们来说并不陌生,方法重载就是静态分派。
动态分派(Dynamic Dispatch) 发生在运行时期, 动态分派动态地置换掉某个方法。Java通过方法的重写支持动态分派。
public class Animal {
public void execute(){
System.out.println("Animal");
}
}
public class Cat extends Animal{
@Override
public void execute() {
System.out.println("cat");
}
}
public class Dog extends Animal{
@Override
public void execute() {
System.out.println("dog");
}
}
public class Client {
public static void main(String[] args) {
Animal dog = new Dog();
Animal cat = new Cat();
dog.execute();
cat.execute();
}
}
上面代码的结果大家应该直接可以说出来,这不就是多态吗!运行执行的是子类中的方法。
Java编译器在编译时期并不总是知道哪些代码会被执行,因为编译器仅仅知道对象的静态类型,而不知道对象的真实类型;而方法的调用则是根据对象的真实类型,而不是静态类型。
通过方法重载支持静态分派。
public class Animal {
}
public class Cat extends Animal{
}
public class Dog extends Animal{
}
public class Execute {
public void execute(Animal a){
System.out.println("Animal");
}
public void execute(Dog d){
System.out.println("Dog");
}
public void execute(Cat c){
System.out.println("Cat");
}
}
public class Client {
public static void main(String[] args) {
Animal a= new Animal();
Animal d= new Dog();
Animal c= new Cat();
Execute execute = new Execute();
execute.execute(a);
execute.execute(d);
execute.execute(c);
}
}
重载方法的分派是根据静态类型进行的,这个分派过程在编译时期就完成了。
所谓双分派技术就是在选择一个方法的时候,不仅仅要根据消息接收者(receiver) 的运行时区别,还要根据参数的运行时区别。
public class Animal {
public void accept(Execute execute){
execute.execute(this);
}
}
public class Cat extends Animal{
public void accept(Execute execute){
execute.execute(this);
}
}
public class Dog extends Animal{
public void accept(Execute execute){
execute.execute(this);
}
}
public class Execute {
public void execute(Animal a){
System.out.println("Animal");
}
public void execute(Dog d){
System.out.println("Dog");
}
public void execute(Cat c){
System.out.println("Cat");
}
}
public class Client {
public static void main(String[] args) {
Animal a = new Animal();
Animal d = new Dog();
Animal c = new Cat();
Execute execute = new Execute();
a.accept(execute);
d.accept(execute);
c.accept(execute);
}
}
在上面代码中,客户端将Execute对象做为参数传递给Animal类型的变量调用的方法,这里完成第一次分派,这里是方法重写,所以是动态分派,也就是执行实际类型中的方法,同时也将自己this作为参数传递进去,这里就完成了第二次分派,这里的Execute类中有多个重载的方法,而传递进行的是this,就是具体的实际类型的对象。
说到这里,我们已经明白双分派是怎么回事了,但是它有什么效果呢?就是可以实现方法的动态绑定,我们可以对上面的程序进行修改。
双分派实现动态绑定的本质,就是在重载方法委派的前面加上了继承体系中覆盖的环节,由于覆盖是动态的,所以重载就是动态的了。
备忘录模式提供了-种状态恢复的实现机制,使得用户可以方便地回到一一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原,很多软件都提供了撤销(Undo) 操作,如Word、 记事本、Photoshop、 IDEA等软件在编辑时按Ctrl+Z组合键时能撤销当前操作,使文档恢复到之前的状态;还有在浏览器中的后退键、数据库事务管理中的回滚操作、玩游戏时的中间结果存档功能、数据库与操作系统的备份操作、棋类游戏中的悔棋功能等都属于这类。
概念:又叫快照模式,在不破坏封装性的前提下,捕获一个对象的内部状态, 并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。
结构:
窄接口:管理者(Caretaker)对象(和其他发起人对象之外的任何对象)看到的是备忘录的窄接口(narror Interface), 这个窄接口只允许他把备忘录对象传给其他的对象。
宽接口:与管理者看到的窄接口相反,发起人对象可以看到一个宽接口(wide Interface), 这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。
优点:
缺点:资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
使用场景:
主要解决:所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
案例:游戏挑战BOSS
游戏中的某个场景,一游戏角色有生命力、 攻击力、防御力等数据,在打Boss前和后一定会不一样的,我们允许玩家如果感觉与Boss决斗的效果不理想可以让游戏恢复到决斗之前的状态。
白箱备忘录模式
备忘录角色对任何对象都提供一个接口,即宽接口,备忘录角色的内部所存储的状态就对所有对象公开。类图如下:
//备忘录角色类
public class RoleStateMemento {
private int vit;//生命力
private int atk;//攻击力
private int def;//防御力
//getset有参无参
}
public class RoleStateCaretaker {
//声明RoleStateMemento类型的变量
private RoleStateMemento roleStateMemento;
//getset
}
//游戏角色类 :发起人角色
public class GameRole {
private int vit;//生命力
private int atk;//攻击力
private int def;//防御力
//初始化内部状态
public void initState(){
this.atk=100;
this.vit=100;
this.def=100;
}
//战斗
public void fight(){
this.atk=0;
this.vit=0;
this.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);
}
//getset
}
public class Client {
public static void main(String[] args) {
System.out.println("----------大战boos前----------");
//创建游戏角色对象
GameRole gameRole = new GameRole();
gameRole.initState();//初始状态操作
gameRole.stateDisplay();
//将该游戏角色内部状态进行备份
//创建管理者对象
RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
roleStateCaretaker.setRoleStateMemento(gameRole.saveState());
System.out.println("----------大战boos后----------");
//损耗严重
gameRole.fight();
gameRole.stateDisplay();
System.out.println("----------恢复之前状态----------");
gameRole.recoverState(roleStateCaretaker.getRoleStateMemento());
gameRole.stateDisplay();
}
}
白箱备忘录模式是破坏封装性的。但是通过程序员自律,同样可以在一定程度上实现模式的大部分用意。
黑箱备忘录
备忘录角色对发起人对象提供一个宽接口,而为其他对象提供一个窄接口。在Java语言中,实现双重接口的办法就是将备忘录类设计成发起人类的内部成员类。
将RolestateMemento 设为GameRole 的内部类, 从而将RolestateMemento 对象封装GameRole 里面;在外面提供一个标识接口Memento给RolestateCaretaker 及 其他对象使用。这样GameRole 类看到的是RolestateMemento 所有的接口,而Rolestatecaretaker及其他对象看到的仅仅是标识接口Memento 所暴露出来的接口,从而维护了封装型。类图如下:
//备忘录接口:对外提供窄接口
public interface Memento {
}
//游戏角色类 :发起人角色
public class GameRole {
private int vit;//生命力
private int atk;//攻击力
private int def;//防御力
//初始化内部状态
public void initState(){
this.atk=100;
this.vit=100;
this.def=100;
}
//战斗
public void fight(){
this.atk=0;
this.vit=0;
this.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);
}
//getset
private class RoleStateMemento implements Memento{
private int vit;//生命力
private int atk;//攻击力
private int def;//防御力
//有参无参、getset
}
}
public class RoleStateCaretaker {
//声明RoleStateMemento类型的变量
private Memento memento;
//getset
}
public class Client {
public static void main(String[] args) {
System.out.println("----------大战boos前----------");
//创建游戏角色对象
GameRole gameRole = new GameRole();
gameRole.initState();//初始状态操作
gameRole.stateDisplay();
//将该游戏角色内部状态进行备份
//创建管理者对象
RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
roleStateCaretaker.setMemento(gameRole.saveState());
System.out.println("----------大战boos后----------");
//损耗严重
gameRole.fight();
gameRole.stateDisplay();
System.out.println("----------恢复之前状态----------");
gameRole.recoverState(roleStateCaretaker.getMemento());
gameRole.stateDisplay();
}
}
概念:给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
文法(语法规则):
文法是用于描述语言的语法结构的形式规则。
expression ::= value| plus| minus
plus ::= expression + expression
minus ::= expression '-' expression
value ::= integer
注意:这里的符号::=表示“定义为”的意思,竖线 | 表示或,左右的其中一个,引号内为字符本身,引号外为语法
上面规则描述为:
表达式可以是一个值, 也可以是plus或者minus运算,而plus和minus又是由表达式结合运算符构成,值的类型为整型数。
抽象语法树:
在计算机科学中,抽象语法树(AbstractsyntaxTree, AST) , 或简称语法树(syntax tree) , 是源代码语法结构的一种抽象表示。
它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
用树形来表示符合文法规则的句子。
结构:
优点:
缺点:
使用场景:
主要解决:对于一些固定文法构建一个解释句子的解释器。
案例:设计加减法的功能
//抽象表达式类
public abstract class AbstractExpression {
public abstract int interpret(Context context);
}
//环境角色类
public class Context {
//定义一个map集合,用来存储变量及对应的值
private Map map=new HashMap<>();
//添加变量的功能
public void assign(Variable variable,Integer value){
map.put(variable,value);
}
//根据变量获取对应的值
public int getValue(Variable variable){
return map.get(variable);
}
}
//封装变量的类
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;
}
@Override
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;
}
@Override
public int interpret(Context context) {
//将左边表达式的结果和右边表达式的结果进行相减
return left.interpret(context) - right.interpret(context);
}
@Override
public String toString() {
return "(" + left.toString() + "-" + right.toString() + ")";
}
}
public class Client {
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);
//获取抽象语法树
AbstractExpression expression=new Minus(a,new Plus(new Minus(b,c),d));
//解释
int result = expression.interpret(context);
System.out.println(expression+"="+result);
}
}