zoukankan      html  css  js  c++  java
  • 【封装那些事】 缺失封装

    mark

    缺失封装

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

    表现形式通常如下:

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

    为什么不能缺失封装?

    开闭原则(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)进行加密的设计。

    最简单最直观的的设计:

    mark

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

    mark

    使用桥接模式进行封装:

    mark

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

    总结

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

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

    参考:《软件设计重构》

    来源:http://songwenjie.cnblogs.com/
    声明:本文为博主学习感悟总结,水平有限,如果不当,欢迎指正。如果您认为还不错,不妨点击一下下方的推荐按钮,谢谢支持。转载与引用请注明出处。
    微信公众号:
  • 相关阅读:
    Shader_ShaderForge_NGUI_流光&波纹&消融
    “PurMVC”在Unity中的应用
    springboot整合jdbc
    ajax属性详解
    FreeMarker 日期转换失败
    freemarker404解决方案(全面)
    @RequestParam,@PathParam,@PathVariable等注解区别
    @RestController和@Controller区别
    通过code去获取他的枚举
    Servlet(三)ServletContext
  • 原文地址:https://www.cnblogs.com/songwenjie/p/8975365.html
Copyright © 2011-2022 走看看