装饰者模式可以动态地将功能附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的代替方案。java
听懂了这句话就不用往下看了,说明你会了。算法
听不懂我以为也正常,若是用一句话能学会就没人看书了。像我这种笨人,都是学会了一个模式,而后往它的定义上套。安全
在说明何时使用装饰者模式以前,先举一个咱们平时都在使用的例子。框架
Java的I/O框架中就大量的使用了装饰者模式,例如《Head First》中所说的:函数
如上图,装饰者从表面上看就是有一个原始的被装饰者对象,而后可能通过多层的装饰附加不少新的功能,还能不修改被装饰者。测试
虽然继承关系也能达到这种预期,可是Java中的继承是很宝贵的,由于一个类只能继承一个父类。有可能父类已经用于模版方法或者其余用途了。this
装饰者能够简介的避免继承滥用的问题。因此使用了装饰者模式,可以在不修改任何底层代码的状况下,给你的(或别人的)对象赋予新的功能。加密
总结一下何时须要使用装饰者:spa
以上是可使用装饰者模式的场景,能够还不是很清晰,下面举个例子来看看如何使用装饰界解决问题,并举一个不使用装饰者时的反面教材。设计
以一个简单的需求来实现装饰者模式,要求作一个通用的API解密组件,实现2种功能:
需求很常见,大多API调用作安全策略时,给咱们的数据都是加密的,咱们须要经过解密算法拿到数据的明文,而后大多数作法是将参数根据名称排序,放到一个字符串中,经过算法进行加密,而后与数据中的一个sign属性的值进行对比,若是一直表明验证签名经过,就能够证实本次请求是安全的。
这里主要将加密和验证签名两个动做拿出来看成需求,具体实现不进行实现。
设计一个多级的继承类,RSADecode和AESDecode这一层实现了普通的RSA或AES解密,下一层实现了几种解密和验签方法。
这种设计有个问题,那就是容易类爆炸,每当多一种新的实现方式,都要成倍的增长类的数量,,例如加一个Base64的Decode,须要加3个类(Base64Decode类,Base64AndSHADecode类,Base64AndMD5Decode类),实现越多,须要加的类就越多,这和桥接模式中的类爆炸缘由是同样的。
能够把须要的算法,以布尔值的方式声明到父类中,而后在子类经过if,else进行实现。这样就不会有类爆炸的弊端。先来看代码:
public abstract class Decode { boolean isRSA; boolean isAES; abstract void decode(); }
作一个父类,其中声明解密须要的算法的布尔值,以及一个抽象方法。
public class DecodeImpl extends Decode { boolean isMD5; boolean isSHA; void decode() { if (isRSA) { // 进行RSA操做 } else if (isAES) { // 进行AES操做 } } }
解密的对象,实现抽象方法,而后判断布尔值来执行具体的解密操做。随便定义好验证签名须要的布尔值。
public class Sign extends DecodeImpl { void decode() { super.decode(); if (isAES) { // 进行RSA操做 } else if (isMD5) { // 进行AES操做 } } }
最后定义验证签名的对象,作法根解密对象是同样的,先调用解密方法,而后根据布尔值执行验证签名的业务。
这么作会有几个问题:
如图所示,这就是装饰者模式的实现类图:
具体的代码以下:
/** * 装饰者模式父级组件。 */ public abstract class Component { public abstract void decode(final String data); } // ----------------------------------------------------------------- // 这里将几种解密的方法做为被装饰者,被装饰者继承了Component对象 // ----------------------------------------------------------------- /** * 被装饰者。 */ public class RSADecode extends Component { public void decode(final String data) { System.out.println("RSA解密:" + data); } } /** * 被装饰者。 */ public class AESDecode extends Component { public void decode(final String data) { System.out.println("AES解密:" + data); } } // ----------------------------------------------------------------- // 这里将验证签名做为装饰者,也继承了Component对象。 // 另外,装饰者父类中,还定义了被装饰者,须要经过构造函数将它传递进来。 // ----------------------------------------------------------------- /** * 装饰者抽象类。 */ public abstract class ValidateSign extends Component { private Component component; /** * 在构造函数中传入被装饰者。 */ public ValidateSign(Component component) { this.component = component; } public Component getComponent() { return this.component; } } /** * 装饰者。 */ public class SHASign extends ValidateSign { public SHASign(Component component) { super(component); } public void decode(String data) { // 先调用被装饰者 super.getComponent().decode(data); // 在实现装饰者的功能 System.out.println("SHA验签:" + data); } } /** * 装饰者。 */ public class MD5Sign extends ValidateSign { public MD5Sign(Component component) { super(component); } public void decode(String data) { // 先调用被装饰者 super.getComponent().decode(data); // 在实现装饰者的功能 System.out.println("MD5验签:" + data); } }
使用装饰者模式进行业务调用:
public class Client { public static void main(String[] args) { // 普通的RSA解密 System.out.println("普通调用,不使用装饰者模式。"); Component component = new RSADecode(); component.decode("Hello World."); System.out.println(""); // 装饰者模式应用,用SHASign来装饰RSADecode // 为组件加入了SHA验签的能力 System.out.println("装饰者模式调用。"); Component component1 = new SHASign(new RSADecode()); component1.decode("Hello World."); System.out.println(""); // 解密后,还能够进行两次验签,很灵活 System.out.println("装饰者模式调用。"); Component component2 = new MD5Sign(new SHASign(new RSADecode())); component2.decode("Hello World."); } } // 输出: 普通调用,不使用装饰者模式。 RSA解密:Hello World. 装饰者模式调用。 RSA解密:Hello World. SHA验签:Hello World. 装饰者模式调用。 RSA解密:Hello World. SHA验签:Hello World. MD5验签:Hello World.
回顾概念:
装饰者模式可以动态地将功能附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的代替方案。
动态添加功能:动态将功能附加到对象上,并且能够随意组合,在Main函数中咱们也作到了,能够屡次对被装饰者进行装饰,装饰的顺序也能够随意调整。
比继承有弹性:装饰者中使用了关联关系的方式,将被装饰者经过构造函数传入,创建关联。
弊端:但它也有一些弊端,会出现不少小类。具体的使用须要根据业务场景进行权衡。
以上就是装饰者模式的一些理解, 有不足之处请你们矫正,谢谢。