缺失封装

没有将实现变化封装在抽象和层次结构中时,将导致这种坏味。

表现形式通常如下:

  • 客户程序与其需要的服务变种紧密耦合,每当需要支持新变种或修改既有变种时,都将影响客户程序。
  • 每当需要在层次结构中支持新变种时,都添加了大量不必要的类,这增加了设计的复杂度。

为什么不能缺失封装?

开闭原则(OCP)指出,类型应对扩展开放,对修改关闭。也就是说应该通过扩展(而不是修改)来改变类型的行为。没有在类型或层次结构中封装实现变化时,便违反了OCP。

缺失封装潜在的原因

未意识到关注点会不断变化

没有预测到关注点可能发生变化,进而没有在设计中正确封装这些关注点。

混合关注点

将彼此独立的各个关注点聚合在一个层次结构中,而不是分开时,如果关注点发生变化,可能导致类的数量呈爆炸式增长。

幼稚的设计决策

采用过于简单的方法,如为每种变化组合创建一个类时,可能导致设计无谓的复杂。

示例分析一

假设有一个Entryption类,它需要使用加密算法对数据进行加密。可供选择的加密算法有很多,包括DES(数据加密标准)、AES(高级加密标准)、TDES(三重数据加密标准)等。Entryption类使用DES算法对数据进行加密。

public class Encryption
{/// <summary>/// 使用DES算法进行加密/// </summary>public void Encrypt(){// 使用DES算法进行加密}
}

假设出现了新需求,要求使用AES算法对数据进行加密。

最差的方案出现了:

public class Encryption
{/// <summary>/// 使用DES算法进行加密/// </summary>public void EncryptUsingDES(){// 使用DES算法进行加密}/// <summary>/// 使用AES算法进行加密/// </summary>public void EncryptUsingAES(){// 使用AES算法进行加密}
}

这种方案有很多不尽如人意的地方:

  • Encryption类变得更大、更难以维护,因为它实现了多种加密算法,但是每次只使用一种。
  • 难以添加新算法以及修改既有算法,因为加密算法是Encryption类不可分割的部分。
  • 加密算法向Encryption类提供服务,但是与Encryption类紧紧耦合在一起,无法在其它地方重用。

不满意就重构,首先使用继承进行重构,会有2种方案可以选择:

选择1:

让Encryption类根据需求继承AESEncryptionAlgorithm或DESEncryptionAlgorithm类,并提供方法Encrypt()。这种方案带来的问题是Encryption类在编译阶段就将关联到特定的加密算法,更严重的是类之间的关系并不是is-a关系。

/// <summary>
/// AES算法加密类
/// </summary>
public class AESEncryptionAlgorithm
{/// <summary>/// 使用AES算法进行加密/// </summary>public void EncryptUsingAES(){// 使用AES算法进行加密}
}/// <summary>
/// DES算法加密类
/// </summary>
public class DESEncryptionAlgorithm
{/// <summary>/// 使用DES算法进行加密/// </summary>public void EncryptUsingDES(){// 使用DES算法进行加密}
}public class Encryption: AESEncryptionAlgorithm
{/// <summary>/// 使用算法进行加密/// </summary>public void Encrypt(){EncryptUsingAES();}
}

选择2:

创建子类AESEncryption和DESEncryption,它们都扩展了Encryption类,并分别包含加密算法AES和DES的实现。客户程序可创建Encryption的引用,这些引用指向特定子类的对象。通过添加新的子类,很容易支持新的加密算法。但是这种方案的问题是AESEncryption和DESEncryption将继承Encryption类的其它方法,降低了加密算法的可重用性。

public abstract class Encryption
{/// <summary>/// 使用算法进行加密/// </summary>public abstract void Encrypt();
}/// <summary>
/// AES算法加密类
/// </summary>
public class AESEncryption : Encryption
{/// <summary>/// 使用 AES算法进行加密/// </summary>public override void Encrypt(){// 使用 AES算法进行加密}
}/// <summary>
/// DES算法加密类
/// </summary>
public class DESEncryption : Encryption
{/// <summary>/// 使用 DES算法进行加密/// </summary>public override void Encrypt(){// 使用 DES算法进行加密}
}

最佳的选择是使用策略模式:

  • 可在运行阶段给Encryption对象配置特定的加密算法
  • 可在其它地方重用层次结构EncryptionAlgorithm中定义的算法
  • 很容易根据需要支持新的算法
/// <summary>
/// 算法加密接口
/// </summary>
public interface EncryptionAlgorithm
{void Encrypt();
}/// <summary>
/// DES算法加密类
/// </summary>
public class DESEncryptionAlgorithm : EncryptionAlgorithm
{public void Encrypt(){//使用 DES算法进行加密}
}
/// <summary>
/// AES算法加密类
/// </summary>
public class AESEncryptionAlgorithm : EncryptionAlgorithm
{public void Encrypt(){//使用 AES算法进行加密}
}public class Encryption
{private EncryptionAlgorithm algo;public Encryption(EncryptionAlgorithm algo){this.algo = algo;}/// <summary>/// 使用算法进行加密/// </summary>public void Encrypt(){algo.Encrypt();}
}

示例分析二

支持使用不同算法(DES和AES)对各种内容(Image和Text)进行加密的设计。

最简单最直观的的设计:

在这个设计中,有两个变化点:支持的内容类型和加密算法类型。对于这两个变化点的每种可能组合,都使用了一个类来表示。这样会有一个严重的问题:假设现在要求支持新加密算法TDES和新内容类型Data,类的数量呈爆炸性增长。因为变化点混在了一起,没有分别进行封装。

使用桥接模式进行封装:

使用桥接模式,分别封装这两个关注点的变化。现在要引入新内容类型Data和新加密算法TDES,只需要添加两个新类。既解决了类数量呈爆炸增长的问题,又增加了根为接口EncryptionAlgorithm层次结构中的加密算法的可重用性。

总结

  1. 不相关的关注点混在一起,抽象将变得难以重用。

  2. 对业务中可能的变化点,要给予扩展点,保证开闭原则(OCP),对扩展开放,对修改关闭。

参考:《软件设计重构》



作者:撸码那些事


来源:http://songwenjie.cnblogs.com/

声明:本文为博主学习感悟总结,水平有限,如果不当,欢迎指正。如果您认为还不错,不妨点击一下下方的【推荐】按钮,谢谢支持。转载与引用请注明出处。


微信公众号:

转载于:https://www.cnblogs.com/songwenjie/p/8975365.html

【封装那些事】 缺失封装相关推荐

  1. 【关于封装的那些事】 缺失封装 【关于封装的那些事】 泄露的封装 【关于封装的那些事】 不充分的封装 【图解数据结构】二叉查找树 【图解数据结构】 二叉树遍历...

    [关于封装的那些事] 缺失封装 目录 - 缺失封装 为什么不能缺失封装? 缺失封装潜在的原因 未意识到关注点会不断变化 混合关注点 幼稚的设计决策 示例分析一 示例分析二 总结 缺失封装 没有将实现变 ...

  2. 网站转APP软件封装网页转APP打包H5封装分发平台APP封装生成双端

    APP封装 什么是 APP 封装? 什么是APP打包 其实APP打包.APP封装.在线打包 都是一个意思,就是把网站封装成APP 1.APP 封装是指把网页形式的网站通过技术封装成为可以下载 到手机上 ...

  3. SpringJdbc持久层封装,Spring jdbcTemplate封装,springJdbc泛型Dao,Spring baseDao封装

    SpringJdbc持久层封装,Spring jdbcTemplate封装,springJdbc泛型Dao,Spring baseDao封装 >>>>>>>& ...

  4. java如何实现封装_java如何实现封装

    Java中类的封装是如何实现的封装是将对象的信息隐藏在对象内部,禁止外部程序直接访问对象内部的属性和方法. java封装类通过三个步骤实现: (1)修改属性的可见性,限制访问. (2)设置属性的读取方 ...

  5. html封装windows,windows 系统封装,打造一份属于自己的系统!

    在电脑的使用过程中,由于我们每个人的使用习惯和使用方式不同,所以我们都会对Windows系统进行自己的设置,尤其是一些搞数码软件的,如果不小心系统坏了,重装系统后,还得一一去进行重新设置,非常麻烦.如 ...

  6. java封装概念_Java面向对象----封装概念

    封装 信息隐藏,隐藏对象的细节 访问修饰符 public private protected 默认 属性封装的实现 方法封装的目的(隐藏方法实现细节) package com.tanlei.newer ...

  7. httpurlconnection 封装_不要再封装各种Util工具类了,看看这个框架

    不要再封装各种Util工具类了,看看这个框架 Hutool 谐音 "糊涂",寓意追求 "万事都作糊涂观,无所谓失,无所谓得" 的境界.    Hutool 是一 ...

  8. .net 多个dll 封装成一个dll_C#封装YOLOv4算法进行目标检测

    C#封装YOLOv4算法进行目标检测 概述 官网:https://pjreddie.com/darknet/ Darknet:[Github] C#封装代码:[Github] YOLO: 是实现实时物 ...

  9. axios 上传文件 封装_axios使用及封装

    axios跨域及封装 1.安装 npm install axios --save 2.main.js引用 import axios from 'axios'; 3.axios基本用法 function ...

最新文章

  1. JAVA操作properties文件
  2. Codevs 3269 混合背包
  3. 查看CentOS的系统版本(亲测)
  4. 二分+最大化最小值 River Hopscotch POJ - 3258
  5. org.springframework.amqp.AmqpConnectException java.net.ConnectException的解决办法
  6. 大数据学习(0)-大数据知识框图
  7. python中shutil模块_Python中shutil模块的学习笔记教程
  8. 图论算法(三)--最短路径 的Bellman-Flod [ 带负权值图 ] 的解法(JAVA )
  9. 【java】Applet显示阶乘结果
  10. Codeforces-GYM101873 G Water Testing 皮克定理
  11. 你不专业并且自以为是,所以被坑
  12. Oracle 11g R1/R2 真正应用集群(RAC)基础
  13. 如何用iso文件制作U盘启动
  14. 程序员必须知道的八件事
  15. 情商和逆商比智商更重要
  16. Python员工离职数据分析
  17. 微信开放平台创建应用时应用官网的问题
  18. 【关于理想】别让你的理想显得太掉价,每个人都很值钱
  19. 微信公众号的留言功能
  20. 线程与全局解释器锁(GIL)

热门文章

  1. Byte和bit的区别?
  2. 设计模式:命令模式(Command Pattern)
  3. spring10: 引用类型的自动注入
  4. 使用Postman完成接口测试
  5. nginx+awstats多域名日志分析2
  6. “寒假”建站创业 选购虚拟主机是关键
  7. android 如何适配屏幕
  8. 从程序语言排行榜来解读IT及Web的发展
  9. 向页面中添加音乐或flash
  10. java基础学习(二)数组