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

  • 相关阅读:
    Unity3D游戏制作(四)——Asset Server搭建
    查询开户银行的现代化支付行号
    专业版Unity技巧分享:使用定制资源配置文件
    如何建立一个完整的游戏AI
    实现简易而强大的游戏AI——FSM,有限状态机
    iOS 开发 初级:应用内购买 In-App Purchase
    linux每日命令(14):less命令
    flask上传excel文件,无须存储,直接读取内容
    linux每日命令(13):more命令
    linux每日命令(12):nl命令
  • 原文地址:https://www.cnblogs.com/graceting/p/4120210.html
Copyright © 2011-2022 走看看