说明

装饰器设计模式是使用比较频繁的设计模式,它在不改变原物体的情况对原物体实现了功能的增加,原物体还是一个可单独使用的个体。在JAVA的类库中应该会马上想到IO类,各种的InputStrem,OutputStrem。

装饰器模式

在用户登录的中过程,我们需要对用户输入的密码进行加密操作,在设计时会提供好多种加密的算法,例如简单的BASE64加密,复杂的如MD5、DSA加密。直接把用户的密码存入数据库是真的在耍流氓。在使用这些算法时,我们想灵活的使用,比如使用完简单加密还可以再进行一下MD5的加密。针对这样的需求,可以使用装饰器模式进行实现。大体思路如下:

  1. 加密接口,定义加密方法。
  2. 在装饰器中,最原始的被装饰者是目标实现,它们一般会实现加密定义的具体方法。
  3. 装饰者会针对目标对象进行装饰,所以它要同样实现加密接口的方法,一般情况下这个装饰器会定义为抽象的形式,在这个装饰器父类中维护着对目标对象的引用。
  4. 具体装饰器继承抽象装饰器来实现对目标对象的真正装饰操作。

以上的情况在代码上体现为如下形式:

  • 加密接口
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package cn.imcompany.decorator.encrypt;

/**
 * Created by tomyli on 2018/6/25.
 * Github: https://github.com/peng051410
 */
public interface Encryptor {

    void encrypt();
}
  • 具体加密类,实现加密处理接口
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package cn.imcompany.decorator.encrypt;

/**
 * Created by tomyli on 2018/6/25.
 * Github: https://github.com/peng051410
 */
public class ConcreteEncryptor implements Encryptor {

    @Override
    public void encrypt() {
        System.out.println("base encrypt!");
    }
}
  • 抽象加密装饰器
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package cn.imcompany.decorator.encrypt;

/**
 * Created by tomyli on 2018/6/25.
 * Github: https://github.com/peng051410
 */
public abstract class EncryptDecorator implements Encryptor {

    private Encryptor encrypt;

    public EncryptDecorator(Encryptor encrypt) {
        this.encrypt = encrypt;
    }

    @Override
    public void encrypt() {
        encrypt.encrypt();
    }
}

上面的抽象装饰器实现的加密接口,在这个类中维护加密接口的实体,加密方法直接调用其维护实体的加密方法。

  • 具体的装饰器实现类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package cn.imcompany.decorator.encrypt;

/**
 * Created by tomyli on 2018/6/25.
 * Github: https://github.com/peng051410
 */
public class ModEncryptDecorator extends EncryptDecorator {

    public ModEncryptDecorator(Encryptor encrypt) {
        super(encrypt);
    }

    @Override
    public void encrypt() {
        super.encrypt();
        modEncrypt();
    }

    public void modEncrypt() {
        System.out.println("取模加密!");
    }
}


package cn.imcompany.decorator.encrypt;

/**
 * Created by tomyli on 2018/6/25.
 * Github: https://github.com/peng051410
 */
public class ConverseEncryptDecorator extends EncryptDecorator {

    public ConverseEncryptDecorator(Encryptor encrypt) {
        super(encrypt);
    }

    @Override
    public void encrypt() {
        super.encrypt();
        converseEncrypt();
    }

    public void converseEncrypt() {
        System.out.println("逆向加密!");
    }
}

上面的代码定义了两个具体的装饰器实体,在这两个装饰器类中,加密方法中都调用了父类的加密方法,然后调用了自己实现的加密方法,这样在先前加密的基础上又进行了一次加密。达到了双重加密的效果。让我们来进行一下测试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package cn.imcompany.decorator.encrypt;

/**
 * Created by tomyli on 2018/6/25.
 * Github: https://github.com/peng051410
 */
public class EncryptDecoratorTest {

    public static void main(String[] args) {

        Encryptor encryptor = new ConcreteEncryptor();
        //进行一次简单加密
        Encryptor encryptDecorator = new SimpleEncryptDecorator(encryptor);
        encryptDecorator.encrypt();

        //对上一次加密进行二次加密(反转加密)
        ConverseEncryptDecorator converseEncryptDecorator = new ConverseEncryptDecorator(encryptDecorator);
        converseEncryptDecorator.encrypt();
    }
}

在测试中先进行了一次简单的加密,又在第一次加密的基础上进行了二次加密,如果再想使用第三次加密,直接把二次加密的对象引用传入即可。这样可以形成很多种组合,可以满足很多加密的需求。 上面的加密类的类图如下:

总结

优点

  1. 针对抽象编程,装饰器可以使多个类的功能进行组合,比继承更加的灵活。
  2. 可以对一个对象进行多次的装饰,可以创造出具有强大功能的类。
  3. 具体的构建类与装饰器分隔,想要增加一个新功能时不需要修改原代码,符合开闭原则。

缺点

  1. 增加一个小的功能也需要增加一个类来实现,有些时候会有些浪费。
  2. 由于实现也多重装饰,在出现问题时对于定位问题要一层一层的来处理。