专业编程基础技术教程

网站首页 > 基础教程 正文

别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式

ccvgpt 2024-08-04 12:16:00 基础教程 10 ℃

背景


别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式

你还在写满屏的爆炸类吗?


就是不管三七二十一,把所有代码写在一个类里面,这样代码不优雅不说,如果改动涉及到老代码,可能还会影响线上的系统稳定性。


其实,很多情况,我们巧妙地利用设计模式就能解决很多潜在的系统问题,今天栈长就教大家使用装饰器模式,在不改动老代码的前提下扩展功能,不但能提升代码优雅性,还能不影响现有的功能,谁用谁知道,真香!!


什么是装饰器模式?


装饰器模式,从字面上理解,顾名思义,就是一种装饰模式,它可以在不改动原有代码的情况下,对现有的对象、行为进行新的层次的包装、装饰,增强原有的基本功能以提供更丰富的能力。


举个简单的装修的小例子:


清理 > 刮腻子 > 涂油漆 > 挂壁画


也可以是:


清理 > 刮腻子 > 贴大理石 > 挂电视


或者可以是:


清理 > 刮腻子 > 贴墙纸


这是一步步的简单装修墙面过程(哈哈,大概如此,我不是专业的),这就是装饰器模式,装饰的每个步骤互不干涉,但后面的步骤需要依赖前一个步骤的完成,后面的步骤可以不断在前一个的装饰基础上进行增加。


装饰器模式结构图如下:


?


装饰器模式类结构如下:


  • Component:组件接口类,定义被装饰类的基本功能
  • ConcreteComponent:组件接口的基本实现类
  • Decorator:装饰器角色类, 实现并持有一个 Component 对象实例
  • ConcreteDecorator:装饰器的实现类


装饰器模式的优点:


1、不改动原有代码,动态增加功能;


2、对象之间不会相互依赖,松耦合,够优雅;


3、符合开闭原则,扩展性好、便于维护;


装饰器模式的缺点:


1、装饰环节如果很多的话,会造成装饰器类膨胀;


2、装饰器层层嵌套比较复杂,使用者必须清楚所有的装饰器类及其用途;


装饰器模式实战


我们把上面的装修的案例用装饰器模式实现一下。


组件接口类:


/**
 * 墙面装修接口
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public interface WallBeautify {

    /**
     * 装修操作
     * @author: 栈长
     * @from: 公众号Java技术栈
     */
    void operation();

}



组件接口的基本实现类:


/**
 * 墙面装修基本实现(清理墙面)
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class WallBeautifyClean implements WallBeautify {

    @Override
    public void operation() {
        System.out.println("开始清理墙面");
    }

}



装饰器角色类:


这是一个抽象类,实现并持有一个 Component 对象实例,这里使用的是聚合,而不是继承,这也是装饰器模式的要点所在。


/**
 * 墙面装修装饰器角色
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public abstract class WallBeautifyDecorator implements WallBeautify {

    /**
     * 持有一个 Component 对象实例
     * @author: 栈长
     * @from: 公众号Java技术栈
     */
    private WallBeautify wallBeautify;

    public WallBeautifyDecorator(WallBeautify wallBeautify) {
        this.wallBeautify = wallBeautify;
    }

    @Override
    public void operation() {
        wallBeautify.operation();
        decoration();
    }

    /**
     * 装饰器实现类自定义实现方法
     * @author: 栈长
     * @from: 公众号Java技术栈
     */
    public abstract void decoration();

}



覆写原操作方法,在原操作之后再进行装饰,所以需要提供一个抽象的 decoration 方法供不同的装饰器的实现类去实现。


装饰器的实现类:


这里定义了 3 个装修过程:


刮腻子 > 涂油漆 > 挂壁画


所以各自去继承 装饰器角色类 并实现其装饰方法:


/**
 * 墙面装修装饰器角色实现(刮腻子)
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class WallBeautifyPutty extends WallBeautifyDecorator {

    public WallBeautifyPutty(WallBeautify wallBeautify) {
        super(wallBeautify);
    }

    @Override
    public void decoration() {
        System.out.println("开始刮腻子");
    }

}



/**
 * 墙面装修装饰器角色实现(涂油漆)
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class WallBeautifyPaint extends WallBeautifyDecorator {

    public WallBeautifyPaint(WallBeautify wallBeautify) {
        super(wallBeautify);
    }

    @Override
    public void decoration() {
        System.out.println("开始涂油漆");
    }

}



/**
 * 墙面装修装饰器角色实现(挂壁画)
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class WallBeautifyHang extends WallBeautifyDecorator {

    public WallBeautifyHang(WallBeautify wallBeautify) {
        super(wallBeautify);
    }

    @Override
    public void decoration() {
        System.out.println("开始挂壁画");
    }

}



测试一下:


/**
 * 装饰器模式测试类
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class DecoratorTest {

    public static void main(String[] args) {
        // 清理墙面
        WallBeautify wallBeautifyClean = new WallBeautifyClean();
        wallBeautifyClean.operation();
        System.out.println("--------------");

        // 刮腻子
        WallBeautify wallBeautifyPutty = new WallBeautifyPutty(wallBeautifyClean);
        wallBeautifyPutty.operation();
        System.out.println("--------------");

        // 涂油漆
        WallBeautify wallBeautifyPaint = new WallBeautifyPaint(wallBeautifyPutty);
        wallBeautifyPaint.operation();
        System.out.println("--------------");

        // 挂壁画
        WallBeautify wallBeautifyHang = new WallBeautifyHang(wallBeautifyPaint);
        wallBeautifyHang.operation();
        System.out.println("--------------");

        // 多层嵌套
        WallBeautify wbh = new WallBeautifyHang(new WallBeautifyPaint(
                new WallBeautifyPutty(new WallBeautifyClean())));
        wbh.operation();
        System.out.println("--------------");
    }

}



本节教程所有实战源码已上传到这个仓库:


https://github.com/javastacks/javastack


输出结果:


开始清理墙面
--------------
开始清理墙面
开始刮腻子
--------------
开始清理墙面
开始刮腻子
开始涂油漆
--------------
开始清理墙面
开始刮腻子
开始涂油漆
开始挂壁画
--------------
开始清理墙面
开始刮腻子
开始涂油漆
开始挂壁画
--------------



结果输出正常!


可以看到,装饰器模式的使用还是相对比较简单的,使用装饰器模式可以达到不同的装饰效果,这样即满足了不同客户的需求,而又不用改动原有的代码,还是挺香的。


后续《设计模式》系列文章在公众号Java技术栈陆续更新中,请大家持续关注哦!


装饰器模式在 JDK 中的应用


现在我们知道如何使用装饰器模式了,现在我们再看看 JDK 哪些地方运用了装饰器模式呢。


1、IO 流


最经典的装饰器模式应用莫过于 JDK 中的 IO 流了(InputStream/ OutputStream)


常用的 InputStream 类结构类如下:


?


InputStream 和 FileInputStream 是基本的组件接口和实现。


FilterInputStream 就是一个实现组件接口并持有实例引用的装饰器角色:


?


BufferedInputStream、DataInputStream 都是不同的 FilterInputStream 的装饰实现。


OutputStream 也是同样的原理。


2、同步集合


要对非线程安全的集合(如:List、Set)简单提供线程安全的功能,使用装饰器模式也能轻松实现。


来看同步集合工具类方法:


java.util.Collections#synchronizedList(List)

java.util.Collections#synchronizedSet(Set)


?


它们都是 SynchronizedCollection 的装饰器实现类:


?


SynchronizedCollection 是装饰器角色:


?


SynchronizedCollection 实现了集合组件接口并持有集合实例引用,而 Collection(List) 和 ArrayList 是基本的组件接口和实现。


总结


本文介绍了装饰器模式的基本概念,也做了一个基本实战,并且举了两个 JDK 中的装饰器模式的例子,相信大家对装饰器模式有了一个基本认识了,怎么运用到项目中,大家应该有谱了吧?


当然,设计模式只是给大家一个设计的参考,并不能盲目运用,否则适得其反。话说,你是怎么在项目中应用装饰器模式的呢?欢迎留言分享案例!


本节教程所有实战源码已上传到这个仓库:


https://github.com/javastacks/javastack

?

最近发表
标签列表