【封装那些事】 缺失封装
缺失封装
没有将实现变化封装在抽象和层次结构中时,将导致这种坏味。
表现形式通常如下:
- 客户程序与其需要的服务变种紧密耦合,每当需要支持新变种或修改既有变种时,都将影响客户程序。
- 每当需要在层次结构中支持新变种时,都添加了大量不必要的类,这增加了设计的复杂度。
为什么不能缺失封装?
开闭原则(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层次结构中的加密算法的可重用性。
总结
不相关的关注点混在一起,抽象将变得难以重用。
对业务中可能的变化点,要给予扩展点,保证开闭原则(OCP),对扩展开放,对修改关闭。
参考:《软件设计重构》
转载于:https://www.cnblogs.com/songwenjie/p/8975365.html
【封装那些事】 缺失封装相关推荐
- 【关于封装的那些事】 缺失封装 【关于封装的那些事】 泄露的封装 【关于封装的那些事】 不充分的封装 【图解数据结构】二叉查找树 【图解数据结构】 二叉树遍历...
[关于封装的那些事] 缺失封装 目录 - 缺失封装 为什么不能缺失封装? 缺失封装潜在的原因 未意识到关注点会不断变化 混合关注点 幼稚的设计决策 示例分析一 示例分析二 总结 缺失封装 没有将实现变 ...
- 网站转APP软件封装网页转APP打包H5封装分发平台APP封装生成双端
APP封装 什么是 APP 封装? 什么是APP打包 其实APP打包.APP封装.在线打包 都是一个意思,就是把网站封装成APP 1.APP 封装是指把网页形式的网站通过技术封装成为可以下载 到手机上 ...
- SpringJdbc持久层封装,Spring jdbcTemplate封装,springJdbc泛型Dao,Spring baseDao封装
SpringJdbc持久层封装,Spring jdbcTemplate封装,springJdbc泛型Dao,Spring baseDao封装 >>>>>>>& ...
- java如何实现封装_java如何实现封装
Java中类的封装是如何实现的封装是将对象的信息隐藏在对象内部,禁止外部程序直接访问对象内部的属性和方法. java封装类通过三个步骤实现: (1)修改属性的可见性,限制访问. (2)设置属性的读取方 ...
- html封装windows,windows 系统封装,打造一份属于自己的系统!
在电脑的使用过程中,由于我们每个人的使用习惯和使用方式不同,所以我们都会对Windows系统进行自己的设置,尤其是一些搞数码软件的,如果不小心系统坏了,重装系统后,还得一一去进行重新设置,非常麻烦.如 ...
- java封装概念_Java面向对象----封装概念
封装 信息隐藏,隐藏对象的细节 访问修饰符 public private protected 默认 属性封装的实现 方法封装的目的(隐藏方法实现细节) package com.tanlei.newer ...
- httpurlconnection 封装_不要再封装各种Util工具类了,看看这个框架
不要再封装各种Util工具类了,看看这个框架 Hutool 谐音 "糊涂",寓意追求 "万事都作糊涂观,无所谓失,无所谓得" 的境界. Hutool 是一 ...
- .net 多个dll 封装成一个dll_C#封装YOLOv4算法进行目标检测
C#封装YOLOv4算法进行目标检测 概述 官网:https://pjreddie.com/darknet/ Darknet:[Github] C#封装代码:[Github] YOLO: 是实现实时物 ...
- axios 上传文件 封装_axios使用及封装
axios跨域及封装 1.安装 npm install axios --save 2.main.js引用 import axios from 'axios'; 3.axios基本用法 function ...
最新文章
- JAVA操作properties文件
- Codevs 3269 混合背包
- 查看CentOS的系统版本(亲测)
- 二分+最大化最小值 River Hopscotch POJ - 3258
- org.springframework.amqp.AmqpConnectException java.net.ConnectException的解决办法
- 大数据学习(0)-大数据知识框图
- python中shutil模块_Python中shutil模块的学习笔记教程
- 图论算法(三)--最短路径 的Bellman-Flod [ 带负权值图 ] 的解法(JAVA )
- 【java】Applet显示阶乘结果
- Codeforces-GYM101873 G Water Testing 皮克定理
- 你不专业并且自以为是,所以被坑
- Oracle 11g R1/R2 真正应用集群(RAC)基础
- 如何用iso文件制作U盘启动
- 程序员必须知道的八件事
- 情商和逆商比智商更重要
- Python员工离职数据分析
- 微信开放平台创建应用时应用官网的问题
- 【关于理想】别让你的理想显得太掉价,每个人都很值钱
- 微信公众号的留言功能
- 线程与全局解释器锁(GIL)