类型和定义
代理模式:为其他对象提供一种代理以控制对这个对象的访问 ,分为静态代理模式和动态代理模式(关于动态代理,推荐阅读 深入浅出模板模式和动态代理)。
装饰模式:动态地给一个对象添加一些额外的职责,就增加功能来说,它相比生成子类更加灵活。
都是GOF
结构类型设计模式
GOF
的23中模式中有一类模式叫做结构类型,其中结构类型的设计模式有适配器模式、桥接模式、组合模式、装饰模式、门面模式、享元模式和代理模式。
为什么叫结构类型设计模式呢?
因为它们都是通过组合类或者对象产生更大结构以适应更高层次的逻辑需求。
本文分享代理模式和装饰模式的对比。
装饰模式就是代理模式的一种特殊应用,两者的共同点则是都具有相同的接口,不同的是代理模式着重对代理控制的控制(也可以理解为访问权限的控制)。而装饰模式则是对类的功能进行加强或者减弱,着重的是对类的功能变化。
举例说明
生活中的代理模式
生活中代理模式就太多了,比如媒婆给你找对象,你只要说你要的对象的条件,然后媒婆就按照你的要求去找类似的美女或者帅哥、很多商品流行代理商,很多时候咱们买的东西都是从代理商那里买到的、找人给你代打游戏升级、黄牛卖火车票等等。
生活中的装饰模式
生活中的装饰模式太多了,比如房子没装修之前可能很难看,但是对房子进行装修后,看起来就非常豪华,非常好看了、古话说人靠衣装马靠鞍,这也是装饰模式的实际应用、在开发中用的也是很多的,很多时候我们需要在老的版本基础之上添加一些额外的功能,比如说添加登录日志等等。
代码实现
代理模式之老田玩王者农药
尽管前两年,乃至现在王者荣耀还是挺火的。也许有的人没玩过,但是你绝对见过别人玩过。民间传说“玩Dato
的看不起玩英雄联盟的,然而玩英雄联盟的看不起玩王者荣耀”。哈哈哈,笑笑就可以了,管那么多,自己开心就好。这两个游戏本人都玩过,但是没有很投入。
但是去年在同事老王的带领下搞起了王者荣耀,疯狂的时候可以玩通宵,大号不能玩了玩小号,小号不行就换 QQ 号......一直想上王者,悲哀的是一直没上,记得最疯狂的时候还是到了差一颗星就到星耀二,但是始终是上不了王者。无意间看到一个牛逼的玩家,说给他 388 块就能上王者,才发现原来可以找人代打的,更恐怖的是有专门代打游戏的公司。可以想象,或许很多王者都是别人带着或者代打上去的吧。
下面用java
来实现上面的两种常见:
老田玩王者农药
public interface PlayerService { //登录 void login(); //挑战排位赛 void challenge(); //升级 void upgrade();}public class PlayerServiceImpl implements PlayerService { private String userName; private String password; public PlayerServiceImpl(String userName, String password) { this.userName = userName; this.password = password; } @Override public void login() { //校验用户名和密码 System.out.println("用户:" + this.userName + ",密码:" + this.password); System.out.println(this.userName + "登录了"); } @Override public void challenge() { System.out.println("挑战排位赛比赛中..."); } @Override public void upgrade() { System.out.println(this.userName + "又升级了,到王者段位了 "); }}//Client当做老田public class Client{ public static void main(String[] args) { PlayerService playerService = new PlayerServiceImpl("老田", "123456"); playerService.login(); playerService.challenge(); playerService.upgrade(); }}
老田玩王者农药情况:
用户:老田,密码:123456 老田登录了 挑战排位赛比赛中... 老田又升级了,到王者段位了
老田找人代打上王者
也是要使用老田的账号登录,也是要一场一场的排位,最后达上王者。不知道能代打游戏之前老田是自己傻傻的一场一场打的。
//替人打游戏的人或公司public class PlayerProxy implements PlayerService { private PlayerService playerService; /** * 费用 */ private BigDecimal fee; private String userName; private String password; public PlayerProxy(String userName, String password, BigDecimal fee) { this.playerService = new PlayerServiceImpl(userName, password); this.fee = fee; this.userName = userName; this.password = password; } @Override public void login() { playerService.login(); } @Override public void player() { System.out.println("代理商赚取手续费" + fee); this.login(); playerService.player(); this.upgrade(); } @Override public void upgrade() { playerService.upgrade(); }}//老王public class Client { public static void main(String[] args) { //告诉代理商或者代打的人账户名+密码+费用 PlayerProxy playerProxy = new PlayerProxy("老田", "123456", BigDecimal.valueOf(100)); playerProxy.player(); }}
最后老田只要等着别人给他打好上王者。
代理商收手续费100 用户:老田,密码:123456 老田登录了 挑战排位赛比赛中...老田又升级了,到王者段位了
装饰模式之支付功能加强
简单支付场景,请看代码实现
public interface PayService { //支付 void pay();}//被装饰的类--支付场景public class PayServiceImpl implements PayService { @Override public void pay() { System.out.println("执行PayServiceImpl--的--支付--支付方法"); }}public interface PayParamsMsgService extends PayService { //支付 @Override void pay(); //发站内信 void sendMsg(); //参数校验 void checkParams();}//装饰类public class PayParamsMsgServiceImpl implements PayParamsMsgService { private PayService payService; public PayParamsMsgServiceImpl(PayService payService) { this.payService = payService; } //没有被装饰的支付=支付 //装饰后的支付=支付前进行参数校验-->支付-->支付后发生信息 @Override public void pay() { checkParams(); payService.pay(); sendMsg(); } @Override public void sendMsg() { System.out.println("发送支付成功站内信"); } @Override public void checkParams() { System.out.println("校验余额是否足够,校验密码是否非法登录等"); }}public class DemoClient { public static void main(String[] args) { PayService payService=new PayServiceImpl(); PayService payService1=new PayParamsMsgServiceImpl(payService); payService1.pay(); }}
运行结果:
校验余额是否足够,校验密码是否非法登录等 执行PayServiceImpl--的--支付--支付方法 发送支付成功站内信
优缺点
优点
装饰类和被装饰类可以独立发展,而不会相互耦合。说明白了就是Component类无须知道Decorator类,Decorator类是从外部来扩展Component,而Decorator也不知道具体的构件。
装饰模式是继承关系的一种替代方案。咱们看装饰类,不管装饰多少层,返回的对象还是Component,实现的还是is-a的关系。
装饰模式可以动态扩展一个实现类的功能,这不需多说,装饰模式定义就是这么讲的。
缺点
多层装饰比较复杂。
使用场景
1、扩展一个类的功能
2、动态增加功能,动态撤销。
开发过程中,一般是针对老系统或者已经正常使用的功能之外添加一些新的功能。不建议新系统使用装饰器设计模式。
代理模式 VS 装饰器模式
这两个模式在实现上很容易陷入难分难解的地步,但是请记住他们的本质区别:装饰模式重在装饰、增强,而代理模式重在访问的权限控制。
如果你写了一个方法,后续考虑要给这个方法添加一些功能,可以选择装饰模式。如果你写了一堆方法,后续考虑统一给这些方法添加功能(比如日志记录,资源回收),可以选择代理模式。