资讯专栏INFORMATION COLUMN

设计模式系列之装饰者模式

ytwman / 838人阅读

摘要:测试类设计模式装饰者模式工厂模式只能读啦会报错只读异常可以正确运行第二部分定义抽象组件是具体组件和抽象装饰类的共同父类,声明了在具体组件中实现的方法。

前言

本篇文章分为四个部分:第一部分会举一个例子引出装饰者模式,让读者对装饰者模式有个感官上的认识;第二部分会给出装饰者模式的定义(当然我们主要不是来背定义,就当做积累专业名词来记吧,我个人是很不喜欢下定义的);第三部分,我会拿jdk中两个使用装饰者模式的例子进行分析,让读者在学习模式的同时熟悉一下jdk源码。第四部分,我会结合装饰者模式理清楚java I/O各种流之间的关系(说实话,工作中用得最多,但是记了又忘,因为I/O类实在太多)后面学习到其他框架的时候再补充。

第一部分

假如有这样的需求:要求实现只能够读的List,要是你来完成这个任务你会如何做?
看下面代码实现:

package decorate;
import java.util.*;

public class ReadOnlyList implements List{

    private List target;

    public ReadOnlyList(List target) {
        super();
        this.target = target;
    }

    @Override
    public int size() {
        return target.size();
    }

    @Override
    public boolean isEmpty() {
        return target.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
        return target.contains(o);
    }

    @Override
    public Iterator iterator() {
        return target.iterator();
    }

    @Override
    public Object[] toArray() {
        return target.toArray();
    }

    @Override
    public  T[] toArray(T[] a) {
        return target.toArray(a);
    }

    @Override
    public boolean add(E e) {
        throw new RuntimeException("only read");
    }

    @Override
    public boolean remove(Object o) {
        throw new RuntimeException("only read");
    }

    @Override
    public boolean containsAll(Collection c) {
        return target.containsAll(c);
    }

    @Override
    public boolean addAll(Collection c) {
        throw new RuntimeException("only read");
    }

    @Override
    public boolean removeAll(Collection c) {
        throw new RuntimeException("only read");
    }

    @Override
    public boolean retainAll(Collection c) {
        throw new RuntimeException("only read");
    }

    @Override
    public void clear() {
        throw new RuntimeException("only read");
    }

    @Override
    public boolean addAll(int index, Collection c) {
        throw new RuntimeException("only read");
    }

    @Override
    public E get(int index) {
        return target.get(index);
    }

    @Override
    public E set(int index, E element) {
        throw new RuntimeException("only read");
    }

    @Override
    public void add(int index, E element) {
        throw new RuntimeException("only read");
    }

    @Override
    public E remove(int index) {
        throw new RuntimeException("only read");
    }

    @Override
    public int indexOf(Object o) {
        return target.indexOf(o);
    }

    @Override
    public int lastIndexOf(Object o) {
        return target.lastIndexOf(o);
    }

    @Override
    public ListIterator listIterator() {
        return target.listIterator();
    }

    @Override
    public ListIterator listIterator(int index) {
        return target.listIterator(index);
    }

    @Override
    public List subList(int fromIndex, int toIndex) {
        return target.subList(fromIndex, toIndex);
    }

}

这里的set,add,remove和addAll等操作都被限制了,只能够读。
测试类

package decorate;

import java.util.ArrayList;
import java.util.List;

public class ReadOnlyListMain {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add("设计模式");
        list.add("装饰者模式");
        list.add("工厂模式");
        ReadOnlyList onlyList = new ReadOnlyList<>(list);
//        onlyList.add("只能读啦"); // 会报错只读异常
        System.out.println(onlyList.size()); // 可以正确运行
    }
}
第二部分 定义

Component(抽象组件): 是具体组件和抽象装饰类的共同父类,声明了在具体组件中实现的方法。比如第一部分的List
ConcreteComponent(具体组件): 抽象组件的子类,实现抽象组件的方法,装饰器可以给他加新的功能。比如ReadOnlyList
Decorator(抽象装饰者): 抽象组件的子类,用来装饰具体组件或者装饰其他装饰组件
ConcreteDecorator(具体装饰者):抽象装饰类的子类
具体的可以看下面的结构图:

第三部分 jdk 例子分析

第一部分我们引出装饰者模式,ReadOnlyList 只读List将List给包装起来,提供了只读的功能,jdk Collection中也有个类似的实现:
public static List unmodifiableList(List list)

让我来看看它的源码:

public static  List unmodifiableList(List list) {
        return (list instanceof RandomAccess ?
                new UnmodifiableRandomAccessList<>(list) :
                new UnmodifiableList<>(list));
    }

我们进入 UnmodifiableList

 /**
     * @serial include
     */
    static class UnmodifiableList extends UnmodifiableCollection
                                  implements List {
        private static final long serialVersionUID = -283967356065247728L;

        final List list;

        UnmodifiableList(List list) {
            super(list);
            this.list = list;
        }

        public boolean equals(Object o) {return o == this || list.equals(o);}
        public int hashCode()           {return list.hashCode();}

        public E get(int index) {return list.get(index);}
        public E set(int index, E element) {
            throw new UnsupportedOperationException();
        }
        public void add(int index, E element) {
            throw new UnsupportedOperationException();
        }
        public E remove(int index) {
            throw new UnsupportedOperationException();
        }
        public int indexOf(Object o)            {return list.indexOf(o);}
        public int lastIndexOf(Object o)        {return list.lastIndexOf(o);}
        public boolean addAll(int index, Collection c) {
            throw new UnsupportedOperationException();
        }

        @Override
        public void replaceAll(UnaryOperator operator) {
            throw new UnsupportedOperationException();
        }
        @Override
        public void sort(Comparator c) {
            throw new UnsupportedOperationException();
        }

        public ListIterator listIterator()   {return listIterator(0);}

        public ListIterator listIterator(final int index) {
            return new ListIterator() {
                private final ListIterator i
                    = list.listIterator(index);

                public boolean hasNext()     {return i.hasNext();}
                public E next()              {return i.next();}
                public boolean hasPrevious() {return i.hasPrevious();}
                public E previous()          {return i.previous();}
                public int nextIndex()       {return i.nextIndex();}
                public int previousIndex()   {return i.previousIndex();}

                public void remove() {
                    throw new UnsupportedOperationException();
                }
                public void set(E e) {
                    throw new UnsupportedOperationException();
                }
                public void add(E e) {
                    throw new UnsupportedOperationException();
                }

                @Override
                public void forEachRemaining(Consumer action) {
                    i.forEachRemaining(action);
                }
            };
        }

        public List subList(int fromIndex, int toIndex) {
            return new UnmodifiableList<>(list.subList(fromIndex, toIndex));
        }

        
        private Object readResolve() {
            return (list instanceof RandomAccess
                    ? new UnmodifiableRandomAccessList<>(list)
                    : this);
        }
    }

你们发现了啥,没错,就是它:addAll、replaceAll和sort等都抛出 UnsupportedOperationException异常,说明只支持读。

还没结束,再举个例子,Collections中还有将线程不安全的集合转换成线程安全的集合synchronizedList,也就是使用装饰者模式,本质上就是在方法加上synchronized 同步锁

让我们看源码:

public static  List synchronizedList(List list) {
        return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<>(list) :
                new SynchronizedList<>(list));
    }

老规矩,进入SynchronizedList

/**
     * @serial include
     */
    static class SynchronizedList
        extends SynchronizedCollection
        implements List {
        private static final long serialVersionUID = -7754090372962971524L;

        final List list;

        SynchronizedList(List list) {
            super(list);
            this.list = list;
        }
        SynchronizedList(List list, Object mutex) {
            super(list, mutex);
            this.list = list;
        }

        public boolean equals(Object o) {
            if (this == o)
                return true;
            synchronized (mutex) {return list.equals(o);}
        }
        public int hashCode() {
            synchronized (mutex) {return list.hashCode();}
        }

        public E get(int index) {
            synchronized (mutex) {return list.get(index);}
        }
        public E set(int index, E element) {
            synchronized (mutex) {return list.set(index, element);}
        }
        public void add(int index, E element) {
            synchronized (mutex) {list.add(index, element);}
        }
        public E remove(int index) {
            synchronized (mutex) {return list.remove(index);}
        }

        public int indexOf(Object o) {
            synchronized (mutex) {return list.indexOf(o);}
        }
        public int lastIndexOf(Object o) {
            synchronized (mutex) {return list.lastIndexOf(o);}
        }

        public boolean addAll(int index, Collection c) {
            synchronized (mutex) {return list.addAll(index, c);}
        }

        public ListIterator listIterator() {
            return list.listIterator(); // Must be manually synched by user
        }

        public ListIterator listIterator(int index) {
            return list.listIterator(index); // Must be manually synched by user
        }

        public List subList(int fromIndex, int toIndex) {
            synchronized (mutex) {
                return new SynchronizedList<>(list.subList(fromIndex, toIndex),
                                            mutex);
            }
        }

        @Override
        public void replaceAll(UnaryOperator operator) {
            synchronized (mutex) {list.replaceAll(operator);}
        }
        @Override
        public void sort(Comparator c) {
            synchronized (mutex) {list.sort(c);}
        }
private Object readResolve() {
            return (list instanceof RandomAccess
                    ? new SynchronizedRandomAccessList<>(list)
                    : this);
        }

嘿嘿,我们现在已经学会从设计模式的角度看jdk源码啦,开心。

第四部分 Java I/O流中的装饰者模式

我们来看看Java I/O中的流图

我们以输入流来分析,首先InputStream 相当于我们的抽象组件,FileInputStream、StringBufferInputStream、ByteArrayInputStream都是可以被装饰者包装起来的组件,FilterInputStream相当于抽象装饰者,PushbackInputStream、BufferedInputStream、DataInputStream和LineNumberInputStream都是具体的装
饰者

他们可以这样用: 一层装饰一层

    InputStream ip = new DataInputStream(new BufferedInputStream(new FileInputStream(new File("/file_path"))));

如下图所示:

有了这样的思路,我们以后想编写自己的I/O,给流增加新的特性,我们就可以继承FilterInputStream,

package decorate;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * 将输入的流内所有的大写字符转成小写字符
 */
public class LowerCaseInputStream extends FilterInputStream{

    public LowerCaseInputStream(InputStream in) {
        super(in);
    }

    @Override
    public int read() throws IOException {
        int c = super.read();
        if (c == -1) {
            return c;
        } else {
            return Character.toLowerCase((char)c);
        }
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        int result = super.read(b, off, len);
        for (int i = off; i < off + result; i++) {
            b[i] = (byte) Character.toLowerCase((char)b[i]);
        }
        return result;
    }
}

测试类:把文件路径替换成你自己的路径

package decorate;

import java.io.*;

/**
 * 测试类
 */
public class MyInputStreamTest {

    public static void main(String[] args) {
        int c;
        try {
            InputStream in = new LowerCaseInputStream(new BufferedInputStream(new FileInputStream("file_path")));
            while((c = in.read()) >= 0) {
                System.out.println((char)c);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75010.html

相关文章

  • 前端也要学系列设计模式装饰模式

    摘要:什么是装饰者模式今天我们来讲另外一个非常实用的设计模式装饰者模式。就增加功能来说,装饰者模式相比生成子类更为灵活。下面,装饰者模式就要正式登场了。下一步,我们可以愉快的去使用装饰者模式啦 什么是装饰者模式 今天我们来讲另外一个非常实用的设计模式:装饰者模式。这个名字听上去有些莫名其妙,不着急,我们先来记住它的一个别名:包装器模式。 我们记着这两个名字来开始今天的文章。 首先还是上《设计...

    高胜山 评论0 收藏0
  • 设计模式装饰模式

    摘要:相关设计模式装饰者模式和代理模式装饰者模式关注再一个对象上动态添加方法代理模式关注再对代理对象的控制访问,可以对客户隐藏被代理类的信息装饰着模式和适配器模式都叫包装模式关于新职责适配器也可以在转换时增加新的职责,但主要目的不在此。 0x01.定义与类型 定义:装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的...

    chuyao 评论0 收藏0
  • Java 设计模式装饰模式

    摘要:装饰者模式组成结构抽象构件给出抽象接口或抽象类,以规范准备接收附加功能的对象。装饰者模式图解装饰者模式应用场景需要扩展一个类的功能,或给一个类添加附加职责。装饰者对象接受所有来自客户端的请求。参考资料设计模式 一、了解装饰者模式 1.1 什么是装饰者模式 装饰者模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰者来包裹真实的对...

    kumfo 评论0 收藏0
  • 美颜相机中的设计模式 —— 装饰模式

    摘要:这是设计模式系列的第二篇,系列文章目录如下用一句话总结那些殊途同归的设计模式工厂策略模版方法美颜相机中的设计模式装饰者模式几乎所有的设计模式都是通过增加一层抽象来解决问题。 这是设计模式系列的第二篇,系列文章目录如下: 用一句话总结那些殊途同归的设计模式:工厂=?策略=?模版方法 美颜相机中的设计模式——装饰者模式 几乎所有的设计模式都是通过增加一层抽象来解决问题。 上一篇中提...

    anonymoussf 评论0 收藏0
  • 每天一个设计模式装饰模式

    摘要:作者按每天一个设计模式旨在初步领会设计模式的精髓,目前采用和两种语言实现。诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式原文地址是每天一个设计模式之装饰者模式欢迎关注个人技术博客。 作者按:《每天一个设计模式》旨在初步领会设计模式的精髓,目前采用javascript和python两种语言实现。诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式...

    brianway 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<