代理模式

是使率非常高的模式: 为其它对象提供一种代理以控制这个对象的访问。该模式也称为委托模式,在使用的时候代理模式可以为我们提供非常好的访问控制。

如下代码

interface IGamePlayer {void login(String user, String password);void killBoss();void upgrade();
}class GamePalyer implements IGamePlayer {private String name = "";public GamePalyer(String name) {this.name = name;}@Overridepublic void login(String user, String password) {System.out.println("登录名为 " + user + "的用户" + this.name + "登录成功");}@Overridepublic void killBoss() {System.out.println(this.name + "在打怪");}@Overridepublic void upgrade() {System.out.println(this.name + "又升级了");}
}class GamePlayerProxy implements IGamePlayer {private IGamePlayer gamePlayer = null;public GamePlayerProxy(IGamePlayer gamePlayer) {this.gamePlayer = gamePlayer;}@Overridepublic void login(String user, String password) {this.gamePlayer.login(user,password);}@Overridepublic void killBoss() {this.gamePlayer.killBoss();}@Overridepublic void upgrade() {this.gamePlayer.killBoss();}
}
public class ProxyModel {public static void main(String[] args) {// 玩家直接打游戏GamePalyer palyer = new GamePalyer("张三");palyer.login("zhangsan","password");palyer.killBoss();palyer.upgrade();// 找个游戏代练帮 palyer 打升级GamePlayerProxy gamePlayerProxy = new GamePlayerProxy(palyer);gamePlayerProxy.login("zhangsan","password");gamePlayerProxy.killBoss();gamePlayerProxy.upgrade();}
}

代理模式也称为委托模式,在使用的时候代理模式可以为我们提供非常好的访问控制,上面例子代码就是使用 GamePlayerProxy 代理类去代理真正的玩游戏的人,调用的代理类的方法 login killBoss upgrade 等实际上都是真正的用户的方法。一个代理类可以代理多个被委托者或者代理者,一个代理类具体代理哪个真实主体角色,是由场景来决定的。

通常情况下一个接口只需要一个代理类就可以了。

代理模式的特点

  • 职责清晰

真实的角色就是实现实际的业务逻辑(实现接口中的方法),不用关系其它非本职责的事务。

  • 高扩展性

具体的主体角色是随时都会发生变化的,只要它实现了接口,不管如何变化,都需要复合接口中方法定义的输入输出逻辑,而代理类则不需要做任何修改可以直接使用。

普通代理

普通代理就是我们要知道代理的的存在,它的要求就是客户端只能访问代理角色,而不能访问方法的真实角色,这是比较简单的,如在上面的例子中main 函数是可以访问到被代理对象。

对上面的例子做一个扩展,使得main 方法只能访问代理对象,而访问不到被代理对象。

interface IGamePlayer {void login(String user, String password);void killBoss();void  upgrade();
}class GamePalyer implements IGamePlayer {private String name = "";// 对能创建的对象进行限制public GamePalyer(IGamePlayer _gamePlayer ,String name) throws Exception {if (_gamePlayer == null) {throw new Exception("不能创建角色");}else {this.name = name;}}@Overridepublic void login(String user, String password) {System.out.println("登录名为 " + user + "的用户" + this.name + "登录成功");}@Overridepublic void killBoss() {System.out.println(this.name + "在打怪");}@Overridepublic void upgrade() {System.out.println(this.name + "又升级了");}
}class GamePlayerProxy implements IGamePlayer {private IGamePlayer gamePlayer = null;// 通过构造函数要对谁进行代练public GamePlayerProxy(String gamePlayerName) {try {gamePlayer = new GamePalyer(this, gamePlayerName);} catch (Exception e) {e.printStackTrace();}}@Overridepublic void login(String user, String password) {this.gamePlayer.login(user,password);}@Overridepublic void killBoss() {this.gamePlayer.killBoss();}@Overridepublic void upgrade() {this.gamePlayer.killBoss();}
}
public class ProxyModel {public static void main(String[] args) {// 找个游戏代练帮 palyer 打升级,给被代理对象直接传递一个对象。GamePlayerProxy gamePlayerProxy = new GamePlayerProxy("张三");gamePlayerProxy.login("zhangsan","password");gamePlayerProxy.killBoss();gamePlayerProxy.upgrade();}
}

一个类可以实现多个接口,完成不同任务的整合,代理类不仅仅可以实现主体接口,也可以实现其它接口完成不了的任务,而且代理的目地是在目标对象的方法基础之上做增强,这种增强的本质通常就是对目标对象的方法进行拦截和过滤。如还可以给代理类实现其它的接口并实现,从而增强代理类

interface IProxy {void count();
}class GamePalyer implements IGamePlayer, IProxy{....}

动态代理

所谓动态代理就是在实现阶段不用关系代理谁,而是在运行阶段才指定代理哪一个对象,相对来说,自己写代理类的方式就是静态代理,而动态代理在横切面编程也就是AOP使用的毕竟流行。

class GamePlayIH implements InvocationHandler {// 被代理者Class cls = null;// 被代理的实例Object obj = null;public GamePlayIH(Object obj) {this.obj = obj;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return method.invoke(this.obj, args);}
}

完成对真实方法的调用,动态代理是根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称,我已经实现该接口下的所有方法了那么动态代理是如何知道被代理接口中的方法呢,默认情况下所有的方法的返回值都是空的,是的,代理已经实现它了,但是没有任何的逻辑含义,所有的方法都是由该Handler 来进行处理,即所有被代理的方法都是由 Handler 来进行处理 即所有被代理的方法都是由 InvocationHandler 接管实际的处理任务。

调用

GamePalyer palyer = new GamePalyer("张三");GamePlayIH gamePlayIH = new GamePlayIH(palyer);ClassLoader cl = palyer.getClass().getClassLoader();IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(cl, GamePalyer.class.getInterfaces(), gamePlayIH);
proxy.login("zhangsan","password");
proxy.killBoss();
proxy.upgrade();

这里我们既没有创建代理类,也没有实现 IGamePlayer 接口,这就是动态代理。

还可以使用动态代理做很多增强的事情

class GamePlayIH implements InvocationHandler {// 被代理者Class cls = null;// 被代理的实例Object obj = null;public GamePlayIH(Object obj) {this.obj = obj;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object invoke = method.invoke(this.obj, args);if (method.getName().equalsIgnoreCase("login")) {System.out.println("有人在用我的账号登录");}return invoke;}
}

使用动态代理写一个简单的AOP的功能

大概的设计如上图,动态代理实现代理的职责,业务逻辑Subject 实现相关的逻辑功能,两者直接没有必然的相互耦合的关系,通知Advice 从另一个切面切入,最终在高层模块Client 进行藕盒,完成逻辑的封装任务。

interface Subject {void doSomething(String str);
}class RealSubject implements Subject {@Overridepublic void doSomething(String str) {System.out.println("----" + str);}
}class MyInvocationHandler implements InvocationHandler {// 被代理的对象private Object target = null;// 通过构造函数传递一个对象public MyInvocationHandler(Object target) {this.target = target;}// 代理方法@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 执行代理方法return method.invoke(this.target, args);}
}class DynamicProxy<T> {public static <T> T newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) {// 寻找JoinPoint 连接点,AOP 框架使用元数据定义if (true) {// 执行一个前置通知(new BeforeAdvice()).exec();}// 执行目标,并返回结果return (T) Proxy.newProxyInstance(loader, interfaces, h);}
}class SubjectDynamicProxy extends DynamicProxy {public static <T> T newProxyInstance(Subject subject) {// 获得classLoaderClassLoader classLoader = subject.getClass().getClassLoader();// 获得接管数组Class<?>[] interfaces = subject.getClass().getInterfaces();//获得handlerMyInvocationHandler myInvocationHandler = new MyInvocationHandler(subject);return newProxyInstance(classLoader, interfaces, myInvocationHandler);}
}interface IAdvice {void exec();
}class BeforeAdvice implements IAdvice {@Overridepublic void exec() {System.out.println("我是前置通知,我被通知了");}
}
public class AOPExample {public static void main(String[] args) {Subject realSubject = new RealSubject();
//        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(realSubject);
//
//        Subject proxy = DynamicProxy.newProxyInstance(realSubject.getClass().getClassLoader(),
//                realSubject.getClass().getInterfaces(), myInvocationHandler);Subject proxy = SubjectDynamicProxy.newProxyInstance(realSubject);proxy.doSomething("finish");}
}

在这里实现的动态代理的首要条件是,代理类必须是一个接口,当然现在还有很多技术如CGBLIB 可以实现不需要接口也可以实现动态代理

状态机模式

下面以例子来一步一步的说明状态的设计模式

//电梯接口
interface ILift {void open();void close();void run();void top();
}class Lift implements ILift {@Overridepublic void open() {System.out.println("电梯门打开");}@Overridepublic void close() {System.out.println("电梯门关闭");}@Overridepublic void run() {System.out.println("电梯运行起来");}@Overridepublic void top() {System.out.println("电梯停了");}
}public class StateModel {public static void main(String[] args) {Lift lift = new Lift();lift.open();lift.close();}
}

上面的代码是一个人人都会写的电梯运行程序,但是这个程序也会有问题的:

  • 电梯门不是随时都能打开的

电梯门的打开是需要一定的前置条件的,不可能运行的时候突然打开,而且电梯的这个4个动作都是有前提条件的,也就是在特定的状态下才能做特定的事情。

  1. 敞门状态

按了电梯的上下按钮,电梯门开,只能在敞门状态才能做关门动作。

2. 闭门状态

电梯门关闭了这个状态下可能运行的动作是开门(下电梯),停止(忘记按路层了),运行

3.运行状态

电梯正在跑,在这个状态下电梯只能做的是停止

4. 停止状态

电梯停止不动,这个状态下电梯有继续运行和开门的操作

电梯的这4个状态,正确的应该是每一次动作的发生都应该对状态进行判断,判断是否可以执行动作,而在上面的实现中并没有做任何前置条件,所以这4个方法是不能为外部类调用的(外部类可能会调任何一个)

解决这个问题使用switch case 是一个办法

//电梯接口
interface ILift {// 在interface里面的变量都是 public static final 的int OPENNING_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();
}class Lift implements ILift {private int state;@Overridepublic void setState(int state) {this.state = state;}@Overridepublic void open() {switch (this.state) {// 以下的状态下开门的操作case OPENNING_STATE:break;case CLOSING_STATE:this.openWithoutLogic();this.setState(OPENNING_STATE);break;case RUNNING_STATE:break;case STOPPING_STATE:this.openWithoutLogic();this.setState(OPENNING_STATE);break;}}@Overridepublic void close() {switch (this.state) {// 以下的状态下关门的操作case OPENNING_STATE:this.closeWithoutLogic();this.setState(CLOSING_STATE);break;case CLOSING_STATE:break;case RUNNING_STATE:break;case STOPPING_STATE:break;}}@Overridepublic void run() {switch (this.state) {// 以下的状态下电梯运行的操作case OPENNING_STATE:break;case CLOSING_STATE:this.runWithoutLogic();this.setState(RUNNING_STATE);break;case RUNNING_STATE:break;case STOPPING_STATE:this.runWithoutLogic();this.setState(RUNNING_STATE);break;}}@Overridepublic void stop() {switch (this.state) {// 以下的状态下电梯停止的操作case OPENNING_STATE:break;case CLOSING_STATE:this.stopWithoutLogic();this.setState(STOPPING_STATE);break;case RUNNING_STATE:this.stopWithoutLogic();this.setState(STOPPING_STATE);break;case STOPPING_STATE:break;}}private void stopWithoutLogic() {System.out.println("电梯停止了");}private void runWithoutLogic() {System.out.println("电梯开始运行");}private void openWithoutLogic() {System.out.println("电梯门打开");}private void closeWithoutLogic() { System.out.println("电梯门关闭"); }
}public class StateModel {public static void main(String[] args) {Lift lift = new Lift();lift.setState(ILift.RUNNING_STATE);lift.stop();}
}

但是这里新的实现方式也是有问题的

  1. Lift 的实现类有点长,每个操作都使用switch case 去实现,如果操作更加的复杂,那么这个类会更大。
  2. 扩展性差劲,如果电梯还要在加状态,那么每一个操作都要去在switch case 中增加这个状态,并去处理。
  3. 还有一些非常规的操作,如检查修理的时候,电梯在停止状态下是可以打开的等。
class Context {public final static OpenningState openningState = new OpenningState();public final static ClosingState closingState = new ClosingState();public final static RunningState runningState = new RunningState();public final static StoppingState stoppingState = new StoppingState();public LiftState getLiftState() {return liftState;}private LiftState 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();}
}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();
}class OpenningState extends LiftState {@Overridepublic void open() {System.out.println("电梯门开启");}@Overridepublic void close() {//状态修改super.context.setLiftState(Context.closingState);//动作委托CloseState 来执行super.context.getLiftState().close();}@Overridepublic void run() {//do nothing}@Overridepublic void stop() {//do nothing}
}class ClosingState extends LiftState {@Overridepublic void open() {//状态修改super.context.setLiftState(Context.openningState);//动作委托CloseState 来执行super.context.getLiftState().open();}@Overridepublic void close() {System.out.println("电梯门关闭");}@Overridepublic void run() {//状态修改super.context.setLiftState(Context.runningState);//动作委托CloseState 来执行super.context.getLiftState().run();}@Overridepublic void stop() {//状态修改super.context.setLiftState(Context.stoppingState);//动作委托CloseState 来执行super.context.getLiftState().stop();}
}class RunningState extends LiftState {@Overridepublic void open() { }@Overridepublic void close() { }@Overridepublic void run() {System.out.println("电梯在运行...");}@Overridepublic void stop() {//状态修改super.context.setLiftState(Context.stoppingState);//动作委托CloseState 来执行super.context.getLiftState().stop();}
}class StoppingState extends LiftState {@Overridepublic void open() {//状态修改super.context.setLiftState(Context.openningState);//动作委托CloseState 来执行super.context.getLiftState().open();}@Overridepublic void close() { }@Overridepublic void run() {//状态修改super.context.setLiftState(Context.runningState);//动作委托CloseState 来执行super.context.getLiftState().run();}@Overridepublic void stop() {System.out.println("电梯停止了。。");}
}public class StateModel {public static void main(String[] args) {Context context = new Context();context.setLiftState(new ClosingState());context.open();context.close();context.run();context.stop();}
}

以上是新的设计,Context 是一个环境角色,它的作用是串联各个状态的过度,在LiftState 抽象类中我们定义并把这个环境角色聚合起来,并传递到子类,也就是4个具体的实现类中欧过自己根据环境来决定如何进行状态的过度。

通过实现这个代码,通过各个子类来实现,并且每个子类代码都比较短,也没有在使用switch case 语句。这也复合开闭原则,如果我们要增加状态,那么修改原来的类,但是这里的修改是增加,而不是在原来的基础上修改。最后现在各个状态是单独的类,只有与这个状态有关的因素修改了,这个类才修改,复合迪米特法则。

这就是状态模式

当一个对象内在状态改变的时候允许其改变行为,这个对象看起来像改变了其类。状态模式的核心就是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。

状态模式中的3个主要角色

  • State 抽象状态角色

接口或者抽象类,负责对对象状态的定义,并且封装环境角色以实现状态切换

  • ConcretState 具体状态角色

每一个具体状态必须完成两个职责,本状态的行为管理以及趋向状态处理,通俗低说,就是本状态下要做的事情,以及本状态如何过度到其它状态。

  • Context 环境角色

定义客户端需要的接口,并且负责具体状态的切换,

状态模式相对来说毕竟复杂,它提供了一种对物质运行的另一个观察视角,通过状态变化促使行为的变化,就类似水的状态一样,一碗水的初始状态是液态,通过加热变为气态,状态的改变同时也引起了体积的扩大,然后产生一种新的行为,鸣笛或者顶起壶盖。

class Context1 {public static final State STATE1 = new ConcretState();public static final State STATE2 = new ConcretState2();private State currentState;public void setCurrenttState(State state) {this.currentState = state;this.currentState.setContext(this);}// 行为委托public void handle1(){this.currentState.handle1();}public void handle2(){this.currentState.handle2();}}抽象环境角色
abstract class State {环境变量,提供子类访问protected Context1 context;设置环境角色public void setContext(Context1 context) {this.context = context;}行为1abstract void handle1();行为2abstract void handle2();
}class ConcretState extends State {@Overridevoid handle1() {// 本状态下必须处理的逻辑}@Overridevoid handle2() {//设置当前状态为 state2super.context.setCurrenttState(Context1.STATE2);// 过度到 state2 状态,由Context 实现super.context.handle2();}
}class ConcretState2 extends State {@Overridevoid handle1() {//设置当前状态为 state1super.context.setCurrenttState(Context1.STATE1);// 过度到 state2 状态,由Context 实现super.context.handle1();}@Overridevoid handle2() {// 本状态下必须处理的逻辑   }
}public class StateModel {public static void main(String[] args) {Context context = new Context();context.setLiftState(new ConcretState1());context.handle1();context.handle2();}
}

具体环境角色有两个职责,处理本状态必须完成的任务,决定是否可以过度到其它状态,在环境变量中有2个不成文的约束

  • 将状态变量对象申明为静态常量,有几个状态对象就几个状态变量
  • 环境角色具有状态抽象定义的多有行为,具体执行使用委托方式

而在使用的过程中,已经隐藏了状态的变化过程,它的切换引起了行为的变化,对外来说,只看到行为发生变化,而不用知道是状态变化引起的。

  • 状态模式有点:结构清晰,封装性非常好,遵循设计原则
  • 当然也会有缺点:子类太多,也就是会子类膨胀

使用场景

  • 行为随状态改变而改变的场景
  • 条件,分支判断语句的替代者

状态机设计模式_设计模式-代理/状态机模式相关推荐

  1. 设计模式学习笔记——代理(Proxy)模式

    设计模式学习笔记--代理(Proxy)模式 @(设计模式)[设计模式, 代理模式, proxy] 设计模式学习笔记代理Proxy模式 基本介绍 代理案例 类图 实现代码 Printable接口 Pri ...

  2. 策略设计模式_设计模式之策略者模式

    策略者模式简介 策略者模式定义一个算法接口,并由其实现类去实现,使得每一个算法都得到封装,并让他们可以相互替换.这是一种行为型模式.策略者模式降低了算法行为和环境角色的耦合度,使得算法可以独立发生变化 ...

  3. 工厂设计模式和策略设计模式_设计模式:策略

    工厂设计模式和策略设计模式 这次我想谈谈策略设计模式 . 通过这种方式,我开始撰写有关行为设计模式的文章. 这种模式表示对象之间的某些交互模式,以使代码更灵活且组织得更好.此方法的最本质点是对象之间的 ...

  4. c++ 设计模式_设计模式行为型:观察者模式(ObserverPattern)

    定义对象之间的一种一对多依赖关系,使得每一个对象发生状态的变化时,其相关依赖对象皆得到通知并被自动更新,又称为发布-订阅(Publish/Subscribe)模式.模型-视图(Model/View)模 ...

  5. java状态机设计模式_设计模式总结-State模式

    不同的状态,不同的行为;或者说,每个状态有着相应的行为. 二.State模式的适用场合: State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If elseif ...

  6. 模板设计模式_设计模式-模板方法模式

    一.模板方法模式的定义 在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板.它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行.这种类型的设计 ...

  7. 状态模式 设计模式_设计模式:状态

    状态模式 设计模式 本文将介绍状态设计模式 . 它是行为设计模式之一 . 您无需了解许多理论即可了解模式的主要概念. 该文章将分为几个部分,在其中我将提供有关需要应用该模式的情况,它所具有的利弊以及用 ...

  8. php设计模式原型模式,原型模式_设计模式_设计模式之原型模式 - Lane Blog

    10 8 Clicks: 6614 Date: 2014-04-21 21:48:35 Power By 李轩Lane 原型模式提取重复功能,避免了程序员喜欢复制粘贴的坏习惯.设计模式中的原型模式就是 ...

  9. 策略设计模式_设计模式之 策略模式

    设计模式的出现好像都是为了解决一个问题----表现和行为分离. 策略模式的定义 封装一系列的方法,在合适的条件下,调用相应的方法. 策略模式的代码表现 假设我们要计算某些指标 var cateInde ...

最新文章

  1. 最大子数组和Python解法
  2. Android通用流行框架大全
  3. jeecms附件标签用法
  4. linux mint 图标主题_如何在 Linux Mint 中更换主题
  5. obs捕获窗口没有窗口_学习工具 | 视频录制软件OBS
  6. Go语言基础(一)——HelloWorld
  7. 【深入理解Java虚拟机学习笔记】第三章 垃圾收集器与内存分配策略
  8. 密钥怎么存储在数据库中
  9. jsapi支付签名_小程序开发之微信支付
  10. Ubuntu 12.04下Proftpd FTP服务器配置
  11. Q8 凯立德 J07 升级方法
  12. 如何选择机器人的电机
  13. 图文详解VxLAN技术(二)
  14. ISP Pipeline
  15. pnpm : 无法加载文件 C:\Users\86183\AppData\Roaming\npm\pnpm.ps1,因为在此系统上禁止运行脚本。
  16. 机器人工程→合适的规划←
  17. 鸿蒙系统,鸿蒙app简易登录界面,界面开发教程
  18. MOSFET管驱动电路的设计
  19. apache服务器安装以及使用passenger插件部署rails应用,基于ubuntu 12.04 LTS
  20. AXD 在win7 vista下 启动失败

热门文章

  1. Django View和URL
  2. 【pyQT5】Python3+pyQT5开发环境安装与配置
  3. 学习python 正则表达式——与你同行!
  4. rocket-console控制台安装
  5. Android实例-手机安全卫士(三十六)-根据Service是否开启确定CheckBox选中状态
  6. 根据/proc/meminfo对空闲内存进行占用
  7. 取消Win7关机时的补丁更新
  8. 深入Managed DirectX9(二)
  9. go语言 c# 混合编程 pdf,C# 结合 Golang 开发
  10. beeline执行sql文件_MyBatis的SQL执行流程不清楚?看完这一篇就够了