软件设计模式修炼 -- 装饰模式


装饰者模式是一种用于替代继承的技术,它经过一种无须定义子类的方式给对象动态增长职责,使对象之间的关联关系取代类之间的继承关系。java


模式动机

装饰者能够在不改变一个对象自己的基础上给对象增长额外的新行为,如一张照片,能够不改变照片自己,给她增长一个相框,使得它具备防潮功能,并且用户能够根据须要增长不一样类型的相框。在软件开发中,相似给照片增长相框的状况随处可见,如给一个图形界面构件增长边框、滚动等新特性。通常有两种方式实现给一个类或对象增长行为:算法

  1. 继承机制编程

    经过继承一个类现有类可使子类在拥有自身方法的同时还拥有父类方法。但这种方式是静态的,用户不能控制增长行为的方式和时机。数组

  2. 关联机制缓存

    将一个类的对象嵌入另外一个新对象中,由另外一个对象来决定是否调用嵌入对象的行为并扩展本身的行为,咱们称这个新对象(即另外一个对象)为装饰类(Dectorator)。app


模式定义

动态地给一个对象增长一些额外的职责(Responsibility),就增长对象功能来讲,装饰者模式比生成子类对象实现更灵活。其别名也能够称为包装器(Wrapper)。ide


模式结构

在这里插入图片描述

  1. Component(抽象构件)函数

    抽象构件定义了对象的接口,能够给这些对象动态增长职责(方法)。抽象构件是具体构件和和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法。测试

  2. ConcreteComponent(具体构件)this

    具体构件定义了具体构件对象,实如今抽象构件中声明的方法。

  3. Decorator(抽象装饰类)

    抽象装饰类是抽象构件类的子类,用于给具体构件增长职责,但具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,经过该引用调用装饰以前构件对象的方法,并经过子类扩展该方法。

  4. ConcreteDecorator(具体装饰类)

    具体装饰类是抽象装饰类的子类,负责向构件添加新的职责。每个具体装饰类都定义了一些新行为,它能够调用在抽象装饰类中定义的方法,并增长新的方法以扩充对象的行为。


实例之多重加密系统

某系统提供一个数据加密功能,能够对字符串进行加密。该系统分别提供了简单的移位加密算法、稍复杂的逆向输出加密和更高级的求模加密。用户先使用最简单的移位加密算法对字符串进行加密,若是以为不够能够对加密后结果进行二次乃至三次加密。
在这里插入图片描述

  1. 抽象构件类 Cipher(抽象加密类)
    public interface Cipher {
    	
        //方法为待加密字符串,返回值为加密后密文
        public String encrypt(String plantTetx);
    }
  2. 具体构件类 SimpleCipher(简单加密类)
    public class SimpleCipher implements Cipher {
    	
        /*
         *	以凯撒加密的方式实现加密方法
         */
        @Override
        public String encrypt(String plantTetx) {
            String str = "";
            for (int i = 0; i < plantTetx.length(); i++) {
                char c = plantTetx.charAt(i);
                if (c >= 'a' && c <= 'z') {
                    c += 6;
                    if (c > 'z') c -= 26;
                    if (c < 'a') c += 26;
                }
                if (c >= 'A' && c <= 'Z') {
                    c += 6;
                    if(c > 'Z') c -= 26;
                    if(c < 'A') c += 26;
                }
                str += c;
            }
            return str;
        }
    }
  3. 抽象装饰类 CipherDecorator(加密装饰类)
    public class CipherDecorator implements Cipher {
    
        private Cipher cipher;
    
        public CipherDecorator(Cipher cipher) {
            this.cipher = cipher;
        }
    	
        @Override
        public String encrypt(String plantTetx) {
            // 调用 cipher 对象的 encrypt() 方法
            return cipher.encrypt(plantTetx);
        }
    }
  4. 具体装饰类 ComplexCipher(复杂加密类)
    public class ComplexCipher extends CipherDecorator {
    
        public ComplexCipher(Cipher cipher) {
            super(cipher);
        }
    	
        // 调用了父类的 encrypt() 方法
        // 并经过新增的 reserve() 方法对加密后字符串作进一步处理
        public String encrypt(String plainText) {
            String result = super.encrypt(plainText);
            result = reverse(result);
            return result;
        }
    
        public String reverse(String text) {
            String str = "";
            for (int i = text.length(); i > 0; i--) {
                str += text.substring(i - 1, i);
            }
            return str;
        }
    }
  5. 具体装饰类 AdvancedCipher(高级加密类)
    public class AdvancedCipher extends CipherDecorator {
    
        public AdvancedCipher(Cipher cipher) {
            super(cipher);
        }
    	
        // 调用了父类的 encrypt() 方法
        // 并经过新增的 mod() 方法对加密后字符串作进一步处理
        @Override
        public String encrypt(String plantTetx) {
            String result = super.encrypt(plantTetx);
            result = mod(result);
            return result;
        }
    
        public String mod(String text) {
            String str = "";
            for (int i = 0; i < text.length(); i++) {
                String c = String.valueOf(text.charAt(i) % 6);
                str += c;
            }
            return str;
        }
    }
  6. 测试代码 Client
    public class Client {
    
        public static void main(String[] args) {
    
            String password = "sunnyLiu";   //明文
            String cpassword;   //密文
    
            Cipher sc = new SimpleCipher();
            cpassword = sc.encrypt(password);
    
            System.out.println(cpassword);
            System.out.println("---------------------");
    
            Cipher cc = new ComplexCipher(sc);
            cpassword = cc.encrypt(password);
            System.out.println(cpassword);
            System.out.println("---------------------");
    		
            //能够对装饰以后的 cc 对象继续进行装饰
            //从而进一步对字符串进行处理,得到更复杂的加密结果
            Cipher ac = new AdvancedCipher(cc);
            cpassword = ac.encrypt(password);
            System.out.println(cpassword);
            System.out.println("---------------------");
        }
    }
  7. 运行结果

    在这里插入图片描述


模式优缺点

装饰模式优势以下:

  1. 在扩展对象功能方面,装饰者模式比继承模式更具灵活性
  2. 能够经过动态的方式扩展对象功能,经过配置文件在运行时选择不一样的装饰器,从而实现不一样的行为
  3. 可使用多个具体装饰类装饰同一对象,获得功能更强大的对象
  4. 用户根据须要添加新的具体构件类和具体装饰类,原有代码无需改变,符合开闭原则

装饰模式缺点以下:

  1. 使用装饰者模式进行系统设计将产生不少小对象与装饰类,增长了系统的复杂度
  2. 程序更加易于出错,排查错误也更加困难

模式适用场景

如下状况能够考虑使用装饰模式

  1. 在不影响其余对象的状况下,以透明、动态的方式给单个对象添加职责
  2. 当不能采用继承对系统进行扩充或者采用继承不利于系统扩展和维护时。不能采用继承的状况主要有两类:第一类是系统存在大量独立的扩展,为支持每一种组合将产生大量子类;第二类是由于类不能继承(final 类)

装饰模式的简化

大多数状况下,装饰模式的实现比标准的结构图要简单,能够对装饰模式进行简化。简化过程当中要注意以下几个问题:

  1. 一个装饰类的接口必须与被装饰类接口保持相同。对于客户端来讲,不管是装饰以前的对象仍是装饰以后的对象均可以同等对待

  2. 不要把太多的逻辑和状态放在具体构件类中,能够经过装饰类进行扩展

  3. 若是只有一个具体构件类而没有抽象构件类,那么抽象装饰类能够做为具体构件类的子类
    在这里插入图片描述
    若是只有一个具体装饰类,那也就不必再设计一个单独的抽象装饰类,能够把抽象装饰类和具体装饰类的职责合并在一个类中
    在这里插入图片描述


透明装饰模式和半透明装饰模式

在透明装饰模式中,要求客户端彻底针对抽象编程,装饰模式的透明性要求客户端程序不该该声明具体构件类型和具体装饰类型,而应所有声明为抽象构件类型。如上述加密系统实例就是透明装饰模式

Cipher sc = new SimpleCipher();
Cipher cc = new ComplexCipher(sc);
Cipher ac = new AdvancedCipher(cc);

装饰模式的用意是在不改变接口的前提下加强原有类的功能。在加强功能时用户每每需建立新的方法,如但愿直接使用复杂加密算法中的 reverse() 方法,这时就要采用半透明装饰模式

SimpleCipher sc = new SimpleCipher();
ComplexCipher cc = new ComplexCipher(sc);
AdvancedCipher ac = new AdvancedCipher(cc);

Java IO 对装饰模式的应用

这里对 IO 流再也不作过多介绍,以 InputStream 和 OutputStream 为例,它们只提供了最简单的流处理方法,只能读入和写出字符,没有缓冲处理、没法处理文件。
在这里插入图片描述
InputStream 是全部字节输入流的父类,其中声明的读取以及操做数据方法会被全部字节输入流继承。子类中有一个 FilterInputStream 类,它又包含了一些子类,如用于给一个输入流添加缓冲功能的 BufferedInputStream,用于读取原始类型的数据的 DataInputStream 等。

InputStream 的层次结构对应装饰模式的结构,其中 InputStream 是一个抽象类,它对应装饰模式中的抽象构件类。而 FilterInputStream、ByteArrayInputStream 等都直接继承 InputStream 类,它们实现了在 InputStream 中定义的 read() 方法。FilterInputStream 类也是 InputStream 的子类,对应抽象装饰类的角色。

public class FilterInputStream extends InputStream {
	
    protected volatile InputStream in;
	
    // 构造函数须要传递一个 InputStream 对象的引用
    // in 对象能够是任何继承自 InputStream 类型的引用
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }
    ...
}

BufferInputStream 是 FilterInputStream 的子类,至关于装饰模式中的具体装饰类,对传入的输入流作不一样的装饰。BufferInputStream 类提供了一个缓存机制,使用一个数组做为数据读入的缓冲区,并覆盖了父类的 read() 方法,在调用输入流读取数据前都会检查缓存是否已满,实现了对输入流对象动态添加新功能的目的,在此处的新功能即为缓冲控制。

FileInputStream inFS = new FileInputStream("temp/fileSrc.txt");
BufferedInputStream inBS = new BufferedInputStream(inFS);
//定义一个字节数组,用于存放缓冲数据
byte[] data = new byte[1024];
inBS.read(data);

在 Java IO 中,不只 InputStream 用到了装饰模式,OutputStream、Reader、Writer 等都用到了此模式。

相关文章
相关标签/搜索