【封装那些事】 缺失封装

缺失封装

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

表现形式通常如下:

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

为什么不能缺失封装?

开闭原则(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),对扩展开放,对修改关闭。

参考:《软件设计重构》

原文发布于微信公众号 - 撸码那些事(lumanxs)

原文发表时间:2018-05-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏北京马哥教育

大型网站的HTTPS实践(一)---HTTPS协议和原理

1前言 百度已经于近日上线了全站HTTPS的安全搜索,默认会将HTTP请求跳转成HTTPS。本文重点介绍HTTPS协议,并简单介绍部署全站HTTPS的意义。 ...

2996
来自专栏木可大大

Web安全概述

互联网刚开始是安全的,但是伴随着黑客(Hacker)的诞生,互联网变得越来越不安全。任何一个事情都有两面性,黑客也有好有坏,好的黑客叫白帽子,坏的黑客叫黑帽子。...

47913
来自专栏岑志军的专栏

数据安全及各种加密算法对比

1543
来自专栏java达人

我的Android进阶之旅------&gt;Android采用AES+RSA的加密机制对http请求进行加密

作者:欧阳鹏 来源:http://blog.csdn.net/ouyang_peng/article/details/50983574(点击文末阅读原文前往) ...

5009
来自专栏FreeBuf

破解之美:利用ECB加密缺陷突破cookie加密

密码学诞生于古人类社会,例如古希腊斯巴达的密码棒、欧洲中世纪时期的密码栏、阿伯提发明的密码盘等。古代的密码学是一门加密隐蔽的学科,而现代密码学定义为加密与解密,...

2538
来自专栏Vamei实验室

协议森林17 我和你的悄悄话 (SSL/TLS协议)

作者:Vamei 出处:http://www.cnblogs.com/vamei 严禁任何形式转载。

852
来自专栏Keegan小钢

读《图解密码技术》(二):认证

原创文章,转载请注明:转载自Keegan小钢 并标明原文链接:http://keeganlee.me/post/reading/20160705 微信订阅号:k...

842
来自专栏北京马哥教育

加密传输原理

数字签名,就是通过在数据单元上附加数据,或对数据单元进行秘密变换,从而使接收者可以确认数据来源和完整性。简单说来,数字签名是防止他人对传输的文件进行破坏,以及确...

31210
来自专栏草根专栏

RxJS速成

RxJS是ReactiveX编程理念的JavaScript版本。ReactiveX是一种针对异步数据流的编程。简单来说,它将一切数据,包括HTTP请求,DOM事...

54418
来自专栏撸码那些事

【封装那些事】 缺失封装

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

34615

扫码关注云+社区