java设计模式之装饰器模式

装饰器模式的定义:

  装饰器模式也叫作包装器模式,指在不改变原有对象的基础上,动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活,属于结构性设计模式。

装饰器模式提供了比继承更有弹性的替代方案(扩展原有对象的功能)将功能附加到对象上,因此装饰器模式的核心是扩展功能,使用装饰器模式可以透明且动态地扩展类地功能。

装饰器模式地应用场景:

  • 用于扩展一个类地功能,或者给一个类添加职责。

  • 动态地给一个对象添加功能,这些功能可以再动态地撤销。

  • 需要为一批平行的兄弟类进行改装或加装功能。

装饰器模式的UML类图:

 

由上图可以看到,装饰器模式主要包含以下4个角色:

  • 抽象组件(Component):可以是一个接口或者抽象类,充当被装饰类的原始对象,规定了被装饰对象的行为。

  • 具体组件(ConcreteComponent):实现/继承Component的一个具体对象,即被装饰对象。

  • 抽象装饰器(Decorator):通用的装饰concreteComponent的装饰器,其内部必然有一个属性指向Component;其实现一般是一个抽象类,主要是为了让其子类按照其构造形式传入一个Component,这是强制的通用行为。如果系统中装饰逻辑单一,则并不需要实现许多的装饰器,可以直接省略该类,而直接实现一个具体的装饰器即可。

  • 具体装饰器(ConcreteDecorator):Decorator的具体实现类,理论上,每个ConcreteDecorator都扩展了Component对象的一种功能。

装饰器模式的通用写法:

package com.liuyi.designmode.structure.decorator;import javax.swing.*;public class Client {    public static void main(String[] args){        Component c1 = new ConcreteComponent (); //首先创建需要被装饰的原始对象(即要被装饰的对象)        Decorator decoratorA = new ConcreteDecoratorA(c1); //给对象透明的增加功能A并调用        decoratorA .operation();        Decorator decoratorB = new ConcreteDecoratorB(c1); //给对象透明的增加功能B并调用        decoratorB .operation();        Decorator decoratorBandA = new ConcreteDecoratorB(decoratorA);//装饰器也可以装饰具体的装饰对象,此时相当于给对象在增加A的功能基础上在添加功能B        decoratorBandA.operation();    }    //抽象组件    static interface  Component {        /**         * 示例方法         */        public void operation();    }    //具体组件    static class ConcreteComponent implements Component {        public void operation() {            //相应的功能处理            System.out.println("处理业务逻辑");        }    }    static abstract class Decorator implements Component {        /**         * 持有组件对象         */        protected Component component;        /**         * 构造方法,传入组件对象         * @param component 组件对象         */        public Decorator(Component component) {            this.component = component;        }        public void operation() {            //转发请求给组件对象,可以在转发前后执行一些附加动作            component.operation();        }    }    //具体装饰器A    static class ConcreteDecoratorA extends Decorator {        public ConcreteDecoratorA(Component component) {            super(component);        }        private void operationFirst(){ } //在调用父类的operation方法之前需要执行的操作        private void operationLast(){ } //在调用父类的operation方法之后需要执行的操作        public void operation() {            //调用父类的方法,可以在调用前后执行一些附加动作            operationFirst(); //添加的功能            super.operation();  //这里可以选择性的调用父类的方法,如果不调用则相当于完全改写了方法,实现了新的功能            operationLast(); //添加的功能        }    }    //具体装饰器B    static class ConcreteDecoratorB extends Decorator {        public ConcreteDecoratorB(Component component) {            super(component);        }        private void operationFirst(){ } //在调用父类的operation方法之前需要执行的操作        private void operationLast(){ } //在调用父类的operation方法之后需要执行的操作        public void operation() {            //调用父类的方法,可以在调用前后执行一些附加动作            operationFirst(); //添加的功能            super.operation();  //这里可以选择性的调用父类的方法,如果不调用则相当于完全改写了方法,实现了新的功能            operationLast(); //添加的功能        }    }}

分析装饰器模式在IO流中的使用:

  java中的IO流主要分为字符流和字节流,这里我们以常用的字符流的读(Reader)为例来分析。首先我觉得要理解装饰器模式在IO中的使用,首先我们必须要

明白怎么去区分哪些是装饰类,哪些是被装饰类。我们看上面的例子,发现具体的装饰类ConcreteDecoratorA和ConcreteDecoratorB的构造函数的参数都是顶层接

口的实例对象。我们先通过JDK API文档查看Reader接口有哪些实现类,然后我们根据这个规则去判断哪些实现类是装饰类,哪些实现类是被装饰类。

我们先来查看BufferedReader的构造方法,发现传入的参数是顶层抽象的实例对象,所以可以判定BufferedReader是一个装饰类,并且它不是抽象的,所以这里直接忽略

了抽象装饰器,直接实现了具体的装饰器。当然Reader也有抽象的装饰器,比如FilterReader,感兴趣的同学可以自己去看API,但是它的具体的装饰器实现很少用,当然

如果需要自定义Reader装饰器,可以去继承这个抽象装饰器实现。

我们再来看InputStreamReader的构造方法,发现传入的参数不是顶层抽象的实例对象,所以可以判断InputStreamReader是一个被装饰的类,如果想要更详细了解可以查看https://www.xiaoyuani.com/的网站,里面有详细教程。

这里只列举这两个常用的被装饰类和装饰的类,目的只是为了让大家理解装饰器模式在IO流中是怎么实现的,我们来看看具体的使用我们读取下面路径下的

名字为xiaoniu.txt文件里面的内容,然后打印出来,先来看不使用装饰类即用InputStreamReader的情况下怎么使用:

package com.liuyi.designmode.structure.decorator;import java.io.FileInputStream;import java.io.FileReader;import java.io.InputStreamReader;/** * @ClassName IoTest * @description: * @author:liuyi * @Date:2020/11/14 23:01 */public class IOTest {    public static void main(String[] args) throws Exception {        //读取当前项目下的xiaoniu.txt        FileInputStream fileInputStream = new FileInputStream("xiaoniu.txt");        InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);        //设置一次读取1024个字节        char[] chars = new char[1024];        int index;        while ((index=inputStreamReader.read(chars))!=-1){            System.out.println(String.valueOf(chars));        }        fileInputStream.close();        inputStreamReader.close();    }}

先不着急使用装饰类,我们发现使用的时候还要传入一个InputStream的实例对象,通过查看JDK文档,我们发现InputStreamReader还有一个子类FileReader,

看看FileReader怎么使用:

package com.liuyi.designmode.structure.decorator;import java.io.FileReader;/** * @ClassName IoTest * @description: * @author:liuyi * @Date:2020/11/14 23:01 */public class IOTest {    public static void main(String[] args) throws Exception {        //读取当前项目下的xiaoniu.txt        FileReader fileReader = new FileReader("xiaoniu.txt");        //设置一次读取1024个字节        char[] chars = new char[1024];        int index;        while ((index=fileReader.read(chars))!=-1){            System.out.println(String.valueOf(chars));        }        fileReader.close();    }}

FileReader也起到增强的作用,只不过是通过子类的方式实现的,只是这种方式符合开闭原则,如果需要添加功能必须要修改子类,还要在接口添加抽象方法。

我们通常会读取文件里面的内容的时候,需要一行一行的读取,如果只用被装饰类的方法是很难实现的,这个时候我们的装饰器BufferedReader就闪亮登场了。

package com.liuyi.designmode.structure.decorator;import java.io.BufferedReader;import java.io.FileReader;/** * @ClassName IoTest * @description: * @author:liuyi * @Date:2020/11/14 23:01 */public class IOTest {    public static void main(String[] args) throws Exception {        //读取当前项目下的xiaoniu.txt        FileReader fileReader = new FileReader("xiaoniu.txt");        BufferedReader bufferedReader = new BufferedReader(fileReader);        //设置一次读取1024个字节        String readLine = bufferedReader.readLine();        System.out.println(readLine);    }}

装饰器模式与代理模式的区别:

  从代理模式UML类图和通用代码实现来看,代理模式与装饰器模式几乎一模一样。从代码实现来看,代理模式确实与装饰器模式也是一样的,但是这两种设计模式

所面向的功能扩展面是不一样的。

  装饰器模式强调自身的功能扩展,着重类功能的变化,比如添加方法。而代理模式强调对代理过程的控制,主要是对已有的方法进行功能增强,比如spring中通过

AOP实现客户端请求日志的记录。

装饰器模式的优点:

  • 装饰器是继承的有力补充,比继承灵活,在不改变原对象的情况下,动态地给一个对象扩展功能,即插即用。

  • 通过使用不同装饰类及这些装饰类的排列组合,可以实现不同效果。

  • 装饰器模式完全遵守开闭原则。

装饰器模式的缺点:

  • 会出现更多的代码、更多的类,增加程序的复杂性。

  • 动态装饰在多层装饰时会更复杂。

(0)

相关推荐

  • PHP设计模式之装饰器模式

    PHP设计模式之装饰器模式 工厂模式告一段落,我们来研究其他一些模式.不知道各位大佬有没有尝试过女装?据说女装大佬程序员很多哟.其实,今天的装饰器模式就和化妆这件事很像.相信如果有程序媛MM在的话,马 ...

  • PHP设计模式—装饰器模式

    定义: 装饰器模式(Decorator):动态的给一个对象添加一些额外的职责,就增加功能来说,装饰器比生成子类更加灵活. 结构: Component:定义一个对象接口,可以给这些对象动态地添加职责. ...

  • 技术图文:03 结构型设计模式(下)

    结构型设计模式(下) 本教程主要介绍一系列用于如何将现有类或对象组合在一起形成更加强大结构的经验总结. 知识结构: 图1 知识结构 组合模式 -- 树形结构的处理 Sunny 软件公司欲开发一个杀毒( ...

  • [PHP小课堂]PHP设计模式之装饰器模式

    [PHP小课堂]PHP设计模式之装饰器模式 关注公众号:[硬核项目经理]获取最新文章 添加微信/QQ好友:[DarkMatterZyCoder/149844827]免费得PHP.项目管理学习资料

  • 设计模式之装饰器模式

    设计模式之装饰器模式

  • 设计模式-装饰器模式

    装饰器模式 定义 装饰器模式也叫包装模式 在不改变原有对象的基础上,把功能附加到对象上,提供了比继承更有弹性的替代方案 能够扩展原有对象的功能 属于结构型模式 生活中的例子 买煎饼 我们煎饼可以加鸡蛋 ...

  • C#设计模式-装饰器模式(Decorator Pattern)

    引言 当我们完成一个软件产品开发后就需要对其进行各种测试,适配快速迭代下质量的保障.当有一个完善的产品的对象后,如果我们想要给他添加一个测试功能,那么我们可以用一个新的类去装饰它来实现对原有对象职责的 ...

  • 3年工作必备 装饰器模式

    回复"000"获取大量电子书 大家好,我是老田,从今天开始,本公众号每周给大家送福利,送什么呢?肯定是技术书啦,没那么多花里胡哨的,参与方式见文末. 好啦,进入我们的主题,今天我给 ...

  • java设计模式基础--拦截器

    由于动态代理一般比较难理解,一般都会设计一个拦截器接口供开发者使用,这样开发者就只用知道拦截器接口的方法,含义和作用即可,无须知道动态代理是怎么实现的. 以下代码用JDK动态代理来实现一个拦截器的逻辑 ...

  • Java设计模式之004--代理模式

    代理模式(Proxy) : 为其他对象提供一种代理以控制对这个对象的访问. 代理模式说白了就是"真实对象" 的代表, 在访问对象时引入一定程度的间接性, 因为这种间接性可以附加多种 ...