设计模式解密(9)- 装饰者模式

1、简介

定义:装饰模式是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

拆分定义,总结特点:

       1、不改变原类文件。

2、不使用继承。

3、动态扩展。

主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:在不想增加很多子类的情况下扩展类。

如何解决:将具体功能职责划分,同时继承装饰者模式。

英文:Decrator

类型:结构型模式

2、类图及组成

(引)类图:

角色:

  ●抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。 
  ●具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。 
  ●装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。 
  ●具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。

代码结构:

抽象构件角色
public interface Component {

    public void sampleOperation();

}

具体构件角色
public class ConcreteComponent implements Component {

    @Override
    public void sampleOperation() {
        // 写相关的业务代码
    }

}

装饰角色
public class Decorator implements Component{
    private Component component;

    public Decorator(Component component){
        this.component = component;
    }

    @Override
    public void sampleOperation() {
        // 委派给构件
        component.sampleOperation();
    }

}

具体装饰角色
public class ConcreteDecoratorA extends Decorator {

    public ConcreteDecoratorA(Component component) {
        super(component);
    }

    @Override
    public void sampleOperation() {
      super.sampleOperation();
        // 写相关的业务代码
    }
}

具体装饰角色
public class ConcreteDecoratorB extends Decorator {

    public ConcreteDecoratorB(Component component) {
        super(component);
    }

    @Override
    public void sampleOperation() {
      super.sampleOperation();
        // 写相关的业务代码
    }
}

3、实例引入

本例中 抽象构建角色由Coder程序员接口扮演

具体构件角色由类SpecificCoder扮演

装饰构件由类Derector扮演,它必须也实现抽象构件接口

具体装饰构件角色由类JavaCoder(程序员)和类 JavaArchitect(架构师)扮演

具体程序员xxx有编程能力,通过学习和实践,他渐渐的获得很多种能力,每多一种能力,都通过装饰类实现的

package com.designpattern.Decorator;
/**
 * 抽象构建角色
 * 程序员接口
 * @author Json
*/
public interface Coder {

    /**
     * 写代码
     */
    public void writeCode();
}

具体构建角色:

package com.designpattern.Decorator;
/**
 * 具体构建角色
 * 一个具体的程序员
 * @author Json
*/
public class SpecificCoder implements Coder {
    @Override
    public void writeCode() {
        System.out.println("我是一个程序员,我能写代码...");
    }
}

一个具体的装饰角色:

package com.designpattern.Decorator;
/**
 * 一个具体的装饰角色
 * java普通程序员
 * @author Json
*/
public class JavaCoder extends Decorator {

    public JavaCoder(Coder coder) {
        super(coder);
    }

    @Override
    public void writeCode(){
        super.writeCode();
        //可以追加功能
        System.out.println("我是Java程序员...");
    }
}

一个具体的装饰角色:

package com.designpattern.Decorator;
/**
 * 一个具体的装饰角色
 * java架构师
 * @author Json
*/
public class JavaArchitect extends Decorator {

    public JavaArchitect(Coder coder) {
        super(coder);
    }

    @Override
    public void writeCode(){
        super.writeCode();
        //可以追加功能
        System.out.println("我是Java架构师...");
    }
}

测试:

package com.designpattern.Decorator;
/**
 * 测试
 * @author Json
*/
public class Test {
    public static void main(String[] args) {
        Coder coder = new SpecificCoder();

        System.out.println("第一次装饰 ↓↓↓");
        Decorator javacoder = new JavaCoder(coder);
        javacoder.writeCode();

        System.out.println("第二次装饰 ↓↓↓");
        Decorator javaarchitect = new JavaArchitect(javacoder);
        javaarchitect.writeCode();

        System.out.println("------------------------------------");
        System.out.println("一步装饰多次 ↓↓↓");
        //一步装饰多次
        Decorator javaarchitect_1 = new JavaArchitect(new JavaCoder(new SpecificCoder()));
        javaarchitect_1.writeCode();
    }
}

结果:

第一次装饰 ↓↓↓
我是一个程序员,我能写代码...
我是Java程序员...
第二次装饰 ↓↓↓
我是一个程序员,我能写代码...
我是Java程序员...
我是Java架构师...
------------------------------------
一步装饰多次 ↓↓↓
我是一个程序员,我能写代码...
我是Java程序员...
我是Java架构师...

4、优缺点

优点:

  1、 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。

  2、 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为。

  3、 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。

  4、 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。

缺点:

  1、 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能。

  2、 装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。

5、应用场景

1、 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

2、 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的 扩展,为支持每一种扩展或者扩展之间的组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类已定义为不能被继承(如 Java 语言中的 final 类)。

6、装饰者模式与继承

1、装饰模式是一种动态行为,对已经存在类进行随意组合,而类的继承是一种静态的行为,一个类定义成什么样的,该类的对象便具有什么样的功能,无法动态的改变。

2、装饰模式扩展的是对象的功能,不需要增加类的数量,而类继承扩展是类的功能,在继承的关系中,如果我们想增加一个对象的功能,我们只能通过继承关系,在子类中增加方法。

3、装饰模式是在不改变原类文件和使用继承的情况下,动态的扩展一个对象的功能,它是通过创建一个包装对象,也就是装饰来包裹真是的对象。

PS:一定情况下,可代替继承。

7、与其他模式对比

装饰者模式是在稳定接口上扩展:

  原有的不能满足现有的需求,对原有的进行增强。
  不会改变接口,而是将一个一个的接口进行装饰,也就是添加新的功能。
  装饰器模式特点在于增强,他的特点是被装饰类和所有的装饰类必须实现同一个接口,而且必须持有被装饰的对象,可以无限装饰。

适配器模式是接口的转换:

  适配器的特点在于兼容,从代码上的特点来说,适配类与原有的类具有相同的接口,并且持有新的目标对象。
  在使用适配器模式的时候,我们必须同时持有原对象,适配对象,目标对象......

代理模式是封装对象的访问过程:

  代理模式的特点在于隔离,隔离调用类和被调用类的关系,通过一个代理类去调用。

8、总结

装饰模式降低了系统的耦合度,可以动态增加或删除对象的职责,并使得需要装饰的具体构件类和具体装饰类可以独立变化,以便增加新的具体构件类和具体 装饰类。

在软件开发中,装饰模式应用较为广泛,例如:

装饰者模式在JDK中的运用:
  Java当中的 IO中输入流和输出流 是运用了装饰者模式的最典型的例子。

  下面是一个简单的例子,通过BufferedReader对象来装饰InputStreamReader对象:

  BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
  //System.in is an InputStream object

其他场景:

  Swing包中图形界面构件功能

  Servlet API中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,增强了request对象的功能。

  Struts2中,request,response,session对象的处理。

PS:源码地址   https://github.com/JsonShare/DesignPattern/tree/master

时间: 2024-10-23 02:48:55

设计模式解密(9)- 装饰者模式的相关文章

设计模式C++实现——装饰者模式

模式定义: 装饰者模式动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的替代方案. 装饰者和被装饰者有相同的超累类型. 可以用一个或多个装饰者包装一个对象. 既然装饰者和被装饰者对象有相同的超累类型,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它. 装饰者可以委托被装饰者的行为之前与或之后,加上自己的行为,以达到特定的目的. 对象可以在任何时候被装饰,所以可以在运行时动态地.不限量地用你喜欢的装饰者来装饰对象. 模式结构: 举例: 购买咖啡时,可以在其中加入

设计模式篇——初探装饰器模式

文章目录 1.装饰器模式介绍 2.装饰器模式类图 3.装饰器模式Demo实现(一个小镇的拉面馆) 4.装饰器模式总结 装饰器模式介绍:装饰器模式可以在不修改任何底层代码的情况下,给对象赋予新的职责(程序运行时的扩展,动态的将责任附加到对象上).属于结构型设计模式. 类图: 我们来看下装饰器模式的类图: 一个简单的Demo(故乡小镇的一个面馆): 在故乡的一个小镇上面,有一家面馆,主营拉面.在这里你可以只点清汤面(SoupNoodle),也可以往里面加佐料,佐料有牛肉(Beef),鱼丸(FishB

javascript设计模式学习之——装饰者模式

一.装饰者模式定义 装饰者模式可以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象.这种为对象动态添加职责的方式就称为装饰者模式.装饰者对象和它所装饰的对象拥有一致的接口,对于用户来说是透明的. 和java等语言不同的是,java的装饰器模式是给对象动态添加职责,javascript中,给对象添加职责的能力是与生俱来的,更侧重于给函数动态添加职责. 二.java中的装饰者模式实现 package com.bobo.shejimoshi.derector; public cl

【设计模式】之装饰器模式

为什么会有装饰模式? 装饰模式是为了解决继承强依赖性和出现大量子类不方便管理问题而出现的.   1. 概述 动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活. 原理:增加一个修饰类包裹原来的类,包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数.装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法.修饰类必须和原来的类有相同的接口. 2. 模式中的角色 2.1 抽象构建(Component):定义一个抽象接口,用以给这些对象动态

设计模式之(Decorator)装饰者模式

1.需求 设计一个计算一块手机价格的程序,不同的存储容量(16GB和32GB)价格不同,不同的颜色(白色和黑色)价格不同,不同的网络制式(联通和电信)价格不同.当用户选定好配置后,程序计算其价格. 2.分析 考虑到未来存储容量可能升级到64GB,网络制式可能增加移动或者其他运营商,颜色可能增加更多的颜色,所以我们需要一种弹性的设计,可以自由添加存储容量,颜色和制式,同时程序不需要做太大改动.我们需要隔离程序可变部分和不可变部分,可变部分是配置,不可变部分是计算价格的过程,下面我们用装饰者模式来实

JAVA设计模式初探之装饰者模式

定义:动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活.设计初衷:通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的. 要点: 装饰者与被装饰者拥有共同的超类,继承的目的是继承类型,而不是行为 实际上Java 的I/O API就是使用Decorator实现的. //定义被装饰

设计模式入门之装饰器模式Decorator

//装饰模式定义:动态地给一个对象加入一些额外的职责. //就添加功能来说.装饰模式比生成子类更为灵活 //这也提现了面向对象设计中的一条基本原则,即:尽量使用对象组合,而不是对象继承 //Component:组件对象的接口.能够给这些对象动态加入职责 //ConcreateComponet:详细的组件对象.实现组件对象接口.通常就是被装饰器装饰的对象.也就是能够给这个对象加入职责 //Decorator:全部装饰器的抽象父类,须要定义一个与组件接口一致的接口,并持有一个Component对象,

设计模式三之装饰者模式1

装饰者模式,真是越来越生活化了,其实设计不就是源于生活高于生活吗?人类,一般总是把生活中观察的东西作为原料才能抽象出东西.装饰者模式,就是用各种装饰者对象来给被装饰者装饰,达到人们的多种多样的需求.不举咖啡的例子,实在没喝过几杯正式的咖啡.考虑炒饭,主要的原材料就是饭,但是变种非常多,近几天我就吃过和有深印象的,扬州炒饭.生牛肉炒饭.五香肉丁炒饭.XO酱海鲜炒饭.黄金炒饭.蛋炒饭等,像什么牛肉.葱花.肉丁.鸡蛋就是装饰者了,厨师用他们炒出(装饰出)各种饭.这种模式的优点是可以订制各种各样的需求,

[设计模式] javascript 之 装饰者模式

装饰者模式说明 说明:通一个类来动态的对另一个类的功能对象进行前或后的修饰,给它辅加一些额外的功能; 这是对一个类对象功能的装饰,装饰的类跟被装饰的类,要求拥有相同的访问接口方法(功能),这在动态面向对象类里,一般以实现同一个接口(interface)来约束实现:装饰类的要有对被装饰类的引用,用于在装饰类的相应方法,调用相应被装饰类的方法,然后对其进行修饰: 场景举例: 1>. 比如我们生活中的穿衣服, 一件衬衣,一件西装外套,一条裤子,一条领带,一双漂亮的皮鞋; 每多穿一件,都是对前面一件或全

设计模式整理_装饰者模式

装饰者模式将责任附加在对象上,若要扩展功能,装饰者提供了比继承更加有弹性的替代方案. 采用装饰者模式的时候,需要注意以下几点: 装饰者和被装饰者对象,有相同的超类型.(继承是为了有正确的类型,而不是继承行为,行为来自于被装饰者和基础的组件). 可以用一个或多个装饰者包装一个对象. 装饰者可以在所委托被装饰者的行为前或者行为后,增加自己的行为. 如果代码是依赖于具体的组件类型而不是针对抽象组件类型编程的时候,就会导致程序出现问题.因此应该具体类型具体分析. 装饰者模式体现了对于扩展开放和对于修改关