zoukankan      html  css  js  c++  java
  • Java--23种设计模式之decorator模式

    装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。是在不必改变原类文件不使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象

    趣味解释:DECORATOR—Mary过完轮到Sarly过生日,还是不要叫她自己挑了,不然这个月伙食费肯定玩完,拿出我去年在华山顶上照的照片,在背面写上“最好的的礼物,就是爱你的Fita”,再到街上礼品店买了个像框(卖礼品的MM也很漂亮哦),再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……,我们都是Decorator,最终都在修饰我这个人呀,怎么样,看懂了吗?趣味解释参见:http://blog.jobbole.com/20496/

    Java代码中实现:以Java中的输入输出流为例,Java中的处理流即是对节点流的包装和装饰

    package decorator;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedReader;
    import java.io.DataInputStream;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.LineNumberReader;
    import java.io.PushbackInputStream;
    import java.io.PushbackReader;
    
    public class IOTest {
    	/* test.txt内容:
         * hello world!
         */
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            //文件路径可自行更换
            final String filePath = "F:/eclipse/workspace/decorator/Test.txt";
            
            //InputStream相当于被装饰的接口或者抽象类,FileInputStream相当于原始的待装饰的对象,FileInputStream无法装饰InputStream
            //另外FileInputStream是以只读方式打开了一个文件,并打开了一个文件的句柄存放在FileDescriptor对象的handle属性
            //所以下面有关回退和重新标记等操作,都是在堆中建立缓冲区所造成的假象,并不是真正的文件流在回退或者重新标记
            InputStream inputStream = new FileInputStream(filePath);
            final int len = inputStream.available();//记录一下流的长度
            System.out.println("FileInputStream不支持mark和reset:" + inputStream.markSupported());
            
            System.out.println("---------------------------------------------------------------------------------");
            
            /* 下面分别展示三种装饰器的作用BufferedInputStream,DataInputStream,PushbackInputStream,LZ下面做了三个装饰器的功能演示  */
            
            //首先装饰成BufferedInputStream,它提供我们mark,reset的功能
            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);//装饰成 BufferedInputStream
            System.out.println("BufferedInputStream支持mark和reset:" + bufferedInputStream.markSupported());
            bufferedInputStream.mark(0);//标记一下
            char c = (char) bufferedInputStream.read();
            System.out.println("Test.txt文件的第一个字符:" + c);
            bufferedInputStream.reset();//重置
            c = (char) bufferedInputStream.read();//再读
            System.out.println("重置以后再读一个字符,依然会是第一个字符:" + c);
            bufferedInputStream.reset();
            
            System.out.println("---------------------------------------------------------------------------------");
            
            //装饰成 DataInputStream,我们为了又使用DataInputStream,又使用BufferedInputStream的mark reset功能,所以我们再进行一层包装
            //注意,这里如果不使用BufferedInputStream,而使用原始的InputStream,read方法返回的结果会是-1,即已经读取结束
            //因为BufferedInputStream已经将文本的内容读取完毕,并缓冲到堆上,默认的初始缓冲区大小是8192B
            DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);
            dataInputStream.reset();//这是BufferedInputStream提供的功能,如果不在这个基础上包装会出错
            System.out.println("DataInputStream现在具有readInt,readChar,readUTF等功能");
            int value = dataInputStream.readInt();//读出来一个int,包含四个字节
            //我们转换成字符依次显示出来,可以看到LZ文件的前四个字符
            String binary = Integer.toBinaryString(value);
            int first = binary.length() % 8;
            System.out.print("使用readInt读取的前四个字符:");
            for (int i = 0; i < 4; i++) {
                if (i == 0) {
                    System.out.print(((char)Integer.valueOf(binary.substring(0, first), 2).intValue()));
                }else {
                    System.out.print(((char)Integer.valueOf(binary.substring(( i - 1 ) * 8 + first, i * 8 + first), 2).intValue()));
                }
            }
            System.out.println();
            
            System.out.println("---------------------------------------------------------------------------------");
            
            //PushbackInputStream无法包装BufferedInputStream支持mark reset,因为它覆盖了reset和mark方法
            //因为流已经被读取到末尾,所以我们必须重新打开一个文件的句柄,即FileInputStream
            inputStream = new FileInputStream(filePath);
            PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream,len);//装饰成 PushbackInputStream
            System.out.println("PushbackInputStream装饰以后支持退回操作unread");
            byte[] bytes = new byte[len];
            pushbackInputStream.read(bytes);//读完了整个流
            System.out.println("unread回退前的内容:" + new String(bytes));
            pushbackInputStream.unread(bytes);//再退回去
            bytes = new byte[len];//清空byte数组
            pushbackInputStream.read(bytes);//再读
            System.out.println("unread回退后的内容:" + new String(bytes));
            
            System.out.println("---------------------------------------------------------------------------------");
            
            /*  以上有两个一层装饰和一个两层装饰,下面我们先装饰成Reader,再进行其它装饰   */
            
            //由于之前被PushbackInputStream将流读取到末尾,我们需要再次重新打开文件句柄
            inputStream = new FileInputStream(filePath);
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"utf-8");//先装饰成InputStreamReader
            System.out.println("InputStreamReader有reader的功能,比如转码:" + inputStreamReader.getEncoding());
            
            System.out.println("---------------------------------------------------------------------------------");
            
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);//我们进一步在reader的基础上装饰成BufferedReader
            System.out.println("BufferedReader有readLine等功能:" + bufferedReader.readLine());
            
            System.out.println("---------------------------------------------------------------------------------");
            
            LineNumberReader lineNumberReader = new LineNumberReader(inputStreamReader);//我们进一步在reader的基础上装饰成LineNumberReader
            System.out.println("LineNumberReader有设置行号,获取行号等功能(行号从0开始),当前行号:" + lineNumberReader.getLineNumber());
            
            System.out.println("---------------------------------------------------------------------------------");
            
            //此处由于刚才被readLine方法将流读取到末尾,所以我们再次重新打开文件句柄,并需要将inputstream再次包装成reader
            inputStreamReader = new InputStreamReader(new FileInputStream(filePath));
            PushbackReader pushbackReader = new PushbackReader(inputStreamReader,len);//我们进一步在reader的基础上装饰成PushbackReader
            System.out.println("PushbackReader是拥有退回操作的reader对象");
            char[] chars = new char[len];
            pushbackReader.read(chars);
            System.out.println("unread回退前的内容:" + new String(chars));
            
            pushbackReader.unread(chars);//再退回去
            chars = new char[len];//清空char数组
            pushbackReader.read(chars);//再读
            System.out.println("unread回退后的内容:" + new String(chars));
            
        }
    
    }
    

      运行结果:

    FileInputStream不支持mark和reset:false
    ---------------------------------------------------------------------------------
    BufferedInputStream支持mark和reset:true
    Test.txt文件的第一个字符:H
    重置以后再读一个字符,依然会是第一个字符:H
    ---------------------------------------------------------------------------------
    DataInputStream现在具有readInt,readChar,readUTF等功能
    使用readInt读取的前四个字符:Hell
    ---------------------------------------------------------------------------------
    PushbackInputStream装饰以后支持退回操作unread
    unread回退前的内容:Hello World!
    unread回退后的内容:Hello World!
    ---------------------------------------------------------------------------------
    InputStreamReader有reader的功能,比如转码:UTF8
    ---------------------------------------------------------------------------------
    BufferedReader有readLine等功能:Hello World??
    ---------------------------------------------------------------------------------
    LineNumberReader有设置行号,获取行号等功能(行号从0开始),当前行号:0
    ---------------------------------------------------------------------------------
    PushbackReader是拥有退回操作的reader对象
    unread回退前的内容:Hello World!

     上图中画出了输入输出流的类图,和装饰器模式对照。其中InputStreamReader转换流比较特殊,即是InputStream输入流装饰产生,同时又是Reader字符流的装饰原始类。

    分析此图可以看出:装饰器模式的灵活,我们创建的一个FileInputstream对象,我们可以使用各种装饰器让它具有不同的特别的功能,这正是动态扩展一个类的功能的最佳体现,而装饰器模式的灵活性正是JAVA中IO所需要的,不得不赞一下JAVA类库的建造者实在是强悍。上述的XXXXInputStream的各个类都继承了InputStream,这样做不仅是为了复用InputStream的父类功能(InputStream也是一种模板方法模式,它定义了read(byte[])方法的简单算法,并将read()方法交给具体的InputStream去实现),也是为了可以重叠装饰,即装饰器也可以再次被装饰,而过渡到Reader以后,Reader的装饰器体系则是类似的。

    总结:装饰器模式就是一个可以非常灵活的动态扩展类功能的设计模式,它采用组合的方式取代继承,使得各个功能的扩展更加独立和灵活。

    参考文章:http://www.cnblogs.com/zuoxiaolong/p/pattern11.html

  • 相关阅读:
    Could A New Linux Base For Tablets/Smartphones Succeed In 2017?
    使用libhybris,glibc和bionic共存时的TLS冲突的问题
    6 Open Source Mobile OS Alternatives To Android in 2018
    Using MultiROM
    GPU drivers are written by the GPU IP vendors and they only provide Android drivers
    Jolla Brings Wayland Atop Android GPU Drivers
    How to Use Libhybris and Android GPU Libraries with Mer (Linux) on the Cubieboard
    闲聊Libhybris
    【ARM-Linux开发】wayland和weston的介绍
    Wayland and X.org problem : Why not following the Android Solution ?
  • 原文地址:https://www.cnblogs.com/graceting/p/4120210.html
Copyright © 2011-2022 走看看