对装饰器模式的理解

一、场景

  • 在Java中,提供某种功能/服务给客户端(应用层的类)去使用,一般都是先定义接口,然后实现该接口。
// 应用层
@Component
public class DataSourceApplication {
    @Autowired
    private DataSource dataSource;

    public void write(String data) {
        dataSource.writeData(data);
    }

    public String read() {
        return dataSource.readData();
    }
}
// 服务层
public interface DataSource {
    void writeData(String data);
    String readData();
}

@Service
public class FileDataSourceImpl implements DataSource {
    @Override
    public void writeData(String data) {
        System.out.println("[FileDataSourceImpl.writeData]");
    }

    @Override
    public String readData() {
        System.out.println("[FileDataSourceImpl.readData]");
        return "FileDataSourceImpl.readData";
    }
}
  • 这时候,产品经理要求写入数据前要先加密,读出数据后要解密。

二、面对场景中的新需求,我们怎么办?

1、暴力法:直接修改原有的代码。

@Service
public class FileDataSourceImpl implements DataSource {
    @Override
    public void writeData(String data) {
    	// 对数据加密
    	...
        System.out.println("[FileDataSourceImpl.writeData]");
    }

    @Override
    public String readData() {
        System.out.println("[FileDataSourceImpl.readData]");
        // 对数据进行解密
        ...
        return "FileDataSourceImpl.readData";
    }
}
  • 虽然这种做法存在一些问题,但在公司可能很普遍…(不然屎山怎么形成的?😃)
    • 问题1:之前辛苦写的单测白费了。(虽然并不是所有程序员都会写单测 😃)
    • 问题2:逻辑耦合,数据的读写和数据的加密,从客观上来说,是相互独立的逻辑。
      • 如果我们耦合在一起,那么方法会越来越冗长,逻辑也越来越不清晰。例如,产品经理又改需求了,要求支持多种加密算法。

2、子类继承法:既然要增强行为,那我搞一个子类,覆写不就完事了?

@Primary
@Service
public class EncryptFileDataSourceImpl extends FileDataSourceImpl {
    @Override
    public void writeData(String data) {
        // 对数据进行加密
        String encryptedData = null;
        ...
        super.writeData(encryptedData);
        
        System.out.println("[EncryptFileDataSourceImpl.writeData]");
    }

    @Override
    public String readData() {
		super.readData();
		
        // 对数据进行解密
        ...
        
        System.out.println("[EncryptFileDataSourceImpl.readData]");
        return "EncryptFileDataSourceImpl.readData";
    }
}
  • 如果产品经理要求先压缩数据,再加密,最后写入数据呢?难道咱再搞一个子类?
    • 咱最好避免这种链式的继承。

3、装饰器模式

  • 实现:
    • (1)压缩数据 -> 加密数据 -> 写入数据
    • (2)读取数据 -> 解密数据 -> 解压数据
  • 代码:
public interface DataSource {
    void writeData(String data);
    String readData();
}

public class FileDataSourceImpl implements DataSource {
    @Override
    public void writeData(String data) {
        System.out.println("[FileDataSourceImpl.writeData] 写入数据");
    }

    @Override
    public String readData() {
        return "[FileDataSourceImpl.readData] 读取数据";
    }
}

public class EncryptDataSourceImpl implements DataSource {
    private DataSource dataSource;

    public EncryptDataSourceImpl(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void writeData(String data) {
        System.out.println("[EncryptFileDataSourceImpl.writeData] 加密数据");
        dataSource.writeData(data);
    }

    @Override
    public String readData() {
        System.out.println(dataSource.readData());
        return "[EncryptFileDataSourceImpl.readData] 解密数据";
    }
}

public class CompressDataSourceImpl implements DataSource {
    private DataSource dataSource;

    public CompressDataSourceImpl(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void writeData(String data) {
        System.out.println("[CompressDataSourceImpl.writeData] 压缩数据");
        dataSource.writeData(data);
    }

    @Override
    public String readData() {
        System.out.println(dataSource.readData());
        return "[CompressDataSourceImpl.readData] 解压数据";
    }
}
@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource compressDataSourceImpl() {
        return new CompressDataSourceImpl(new EncryptDataSourceImpl(new FileDataSourceImpl()));
    }
}
@ComponentScan
public class Application {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class);
        DataSource dataSource = applicationContext.getBean(DataSource.class);
        dataSource.writeData("hello world");
        System.out.println("-----------------------------");
        System.out.println(dataSource.readData());
    }
}

/*
[CompressDataSourceImpl.writeData] 压缩数据
[EncryptFileDataSourceImpl.writeData] 加密数据
[FileDataSourceImpl.writeData] 写入数据
-----------------------------
[FileDataSourceImpl.readData] 读取数据
[EncryptFileDataSourceImpl.readData] 解密数据
[CompressDataSourceImpl.readData] 解压数据
*/
  • 一旦产品经理要改需求了,例如:
    • (1)压缩数据 -> 写入数据
    • (2)读取数据 -> 解压数据
  • 咱只要修改DataSourceConfig即可:
@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource compressDataSourceImpl() {
//        return new CompressDataSourceImpl(new EncryptDataSourceImpl(new FileDataSourceImpl()));
        return new CompressDataSourceImpl(new FileDataSourceImpl());
    }
}

/*
[CompressDataSourceImpl.writeData] 压缩数据
[FileDataSourceImpl.writeData] 写入数据
-----------------------------
[FileDataSourceImpl.readData] 读取数据
[CompressDataSourceImpl.readData] 解压数据
*/

三、对装饰器模式的思考

1、从代码结构上来看,咋和代理模式这么像呢?

  • 装饰器模式:
    • (1)实现和被装饰类一样的接口(如上述的:implements DataSource
    • (2)持有被装饰的类(如上述的:private DataSource dataSource;
    • (3)增强接口的方法
  • 代理模式:【详见:对代理模式的理解
    • (1)实现和被代理类一样的接口
    • (2)持有被代理的类
    • (3)增强接口的方法

像,太像了!

  • 真的是这样吗?
    • 看看客户端是如何使用的吧~
      • 装饰器模式:我们要根据需求,“装饰”出接口对应的实现类。
        • 通过层层套娃,丰富基础功能。结合上文,从基本的写入数据功能,丰富为:压缩数据 -> 加密数据 -> 写入数据。
      • 代理模式:接口原本的实现类是类A,代理后,客户端真正使用的是实现类B(通常,客户端感知不到这种变化)。
  • 一图胜前言:
    在这里插入图片描述
    • 一个接口的实现类,从逻辑上说,存在组合逻辑,例如:
      • 加密数据 + 写入数据
      • 压缩数据 + 写入数据
      • 压缩数据 + 加密数据 + 写入数据
    • 如果采用继承的方式,会导致定义很多子类,那么用组合吧!用装饰器模式吧!【Java的io便是这种场景~】

2、设计原则 >> 设计模式

  • 我也学了一阵子设计模式了,已经感受到设计模式的局限性了。以上文为例,如果我们拿到的FileDataSourceImpl已经是一坨屎山了,里面写入数据的逻辑并不纯粹,那么,通过装饰器模式丰富写入数据的能力可能会出问题(例如,写入数据前做了一些特殊处理;需求是我们先做特殊处理,再加密后写入;使用装饰器模式就变成了,先加密,然后特殊处理,再写入。)。此时,还不如暴力法来得简洁高效。

破罐子破摔,世界是熵增的…

  • 因此,设计模式非常依赖场景。场景稍微变一下,设计模式就失效了… 而真正有用的是,设计模式遵循的设计原则,以及背后的终极奥义:高内聚,低耦合
  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
4S店客户管理小程序-毕业设计,基于微信小程序+SSM+MySql开发,源码+数据库+论文答辩+毕业论文+视频演示 社会的发展和科学技术的进步,互联网技术越来越受欢迎。手机也逐渐受到广大人民群众的喜爱,也逐渐进入了每个用户的使用。手机具有便利性,速度快,效率高,成本低等优点。 因此,构建符合自己要求的操作系统是非常有意义的。 本文从管理员、用户的功能要求出发,4S店客户管理系统中的功能模块主要是实现管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理,用户客户端:首页、车展、新闻头条、我的。门店客户端:首页、车展、新闻头条、我的经过认真细致的研究,精心准备和规划,最后测试成功,系统可以正常使用。分析功能调整与4S店客户管理系统实现的实际需求相结合,讨论了微信开发者技术与后台结合java语言和MySQL数据库开发4S店客户管理系统的使用。 关键字:4S店客户管理系统小程序 微信开发者 Java技术 MySQL数据库 软件的功能: 1、开发实现4S店客户管理系统的整个系统程序; 2、管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理等。 3、用户客户端:首页、车展、新闻头条、我的 4、门店客户端:首页、车展、新闻头条、我的等相应操作; 5、基础数据管理:实现系统基本信息的添加、修改及删除等操作,并且根据需求进行交流信息的查看及回复相应操作。
现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本微信小程序医院挂号预约系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此微信小程序医院挂号预约系统利用当下成熟完善的SSM框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的MySQL数据库进行程序开发。微信小程序医院挂号预约系统有管理员,用户两个角色。管理员功能有个人中心,用户管理,医生信息管理,医院信息管理,科室信息管理,预约信息管理,预约取消管理,留言板,系统管理。微信小程序用户可以注册登录,查看医院信息,查看医生信息,查看公告资讯,在科室信息里面进行预约,也可以取消预约。微信小程序医院挂号预约系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值