zoukankan      html  css  js  c++  java
  • 【模块化那些事】 拆散的模块化

    模块化原则倡导利用集中和分解等手法创建高内聚、低耦合的抽象。

    mark

    为了理解模块化的含义及其很重要的原因,来看看一本书的极端情况。假设一本书像讲一个长故事一样阐述其中的内容,中间没有任何停顿,也没有章节。试问面对这样的图书,读者将作何反应呢?我估计心中一定有千万只草泥马在崩腾吧。如果这本书根据内容分为不同的章节(模块)进行讲述,情况是不是就完全不一样了呢?同样,设计软件时,遵循模块化原则也很重要。需要指出的是模块化通常是一个系统级考虑因素,指的是如何将抽象组织成逻辑模块。但是我们这里的术语模块指的是类级抽象:具体类、抽象类和接口。模块化的目标是创建高内聚、低耦合的抽象。

    应用模块化原则的实现手法

    mark

    • 将相关的数据和方法集中在一起:每个抽象都必须是内聚的,即抽象应将相关的数据和操作它们的方法集中在一起
    • 将抽象分解为易于管理的规模:将大抽象分解为规模适中(既不太大也不太小)的小抽象。大类不仅难以理解,而且难以修改,因为这种类实现的职责可能交织在一起。
    • 创建非循环依赖:抽象之间不应该存在循环依赖。否则修改一个抽象可能引起连锁反应,波及整个设计。
    • 限制依赖关系数:创建扇入和扇出低的抽象。扇入指的是有多少个抽象依赖于当前的抽象,因此修改扇入高的抽象时,可能需要修改大量依赖于它的抽象。扇出指的是当前抽象依赖于多少个其它的抽象,高扇出意味着修改很多抽象时都可能影响当前抽象。为避免潜在的修改引发连锁反应,减少设计中抽象之间的依赖关系数很重要。

    违反模块化原则导致的坏味

    mark

    我们这篇博客主要讲解分析拆散的模块化坏味,对于其它模块化坏味将在后面的博客讲解分析。

    拆散的模块化

    应集中放在一个抽象中的数据和方法分散在多个抽象中,将导致这种坏味。

    常见表现形式如下:

    • 类被用作数据容器,其中没有任何操作这些数据的方法

    • 类的方法更多的被其它类成员调用

    为什么不能有拆散的模块化?

    如果抽象只包含数据成员,而操作这些数据成员的方法分散在多个抽象中时,原本应属于一个抽象的成员分散在多个抽象中时,将导致这些抽象之间紧密耦合。违反了模块化原则。

    拆散的模块化潜在原因

    以过程型思维使用面向对象语言

    过程型语言倾向于将数据和操作它的函数分开,从而导致这种坏味。

    不熟悉既有设计

    大型的项目设计很复杂。在这样的项目中,每位开发人员通常只负责系统中很小的一部分,不了解设计的其它部分。这可能导致成员被放置到错误的类中。

    示例分析

    来看一个设备管理应用程序。在这个应用程序中,与设备相关的数据存储在DeviceData类中,而处理这些设备数据的方法由Device类提供。DeviceData类只有公共数据成员,没有任何方法。而Device类包含一个类型为DeviceData的对象,并提供了访问和操作该数据成员的方法。

    mark

    这些数据和方法原本应该集中放在一个类中,却分散在了Device和DeviceData类中,显然存在"拆散的模块化"坏味。

    代码实现:

    public class DeviceData
    {
        /// <summary>
        /// 设备ID
        /// </summary>
        public string DeviceID { get; set; }
        /// <summary>
        /// 设备位置
        /// </summary>
        public string DevicePath { get; set; }
        /// <summary>
        /// 是否可用
        /// </summary>
        public bool Enabled { get; set; }
    }
    
    public class Device
    {
        private DeviceData deviceData = new DeviceData();
        
        /// <summary>
        /// 获取设备ID
        /// </summary>
        /// <returns></returns>
        public string GetDeviceID()
        {
            return deviceData.DeviceID;
        }
        /// <summary>
        /// 设置设备ID
        /// </summary>
        /// <param name="deviceID">设备ID</param>
        /// <returns></returns>
        public bool SetDeviceID(string deviceID)
        {
            deviceData.DeviceID = deviceID;
            return true;
        }
        /// <summary>
        /// 是否可用
        /// </summary>
        /// <returns></returns>
        public bool IsEnabled()
        {
            return deviceData.Enabled;
        }
    }
    

    重构"拆散的模块化"

    • 如果一个方法更多地被另一个类(Target类)而不是定义它的类(Source类)调用,就采用“移动方法”,将这个方法从Source类移到Target类中。

    • 如果一个字段更多地被另一个类(Target类)而不是定义它的类(Source类)使用,就采用“移动字段”,将这个字段从Source类移到Target类中。

    mark

    mark

    重构后的代码实现:

    public class Device
    {
        /// <summary>
        /// 设备ID
        /// </summary>
        private string DeviceID { get; set; }
        /// <summary>
        /// 设备位置
        /// </summary>
        private string DevicePath { get; set; }
        /// <summary>
        /// 是否可用
        /// </summary>
        private bool Enabled { get; set; }
    
        /// <summary>
        /// 获取设备ID
        /// </summary>
        /// <returns></returns>
        public string GetDeviceID()
        {
            return DeviceID;
        }
        /// <summary>
        /// 设置设备ID
        /// </summary>
        /// <param name="deviceID">设备ID</param>
        /// <returns></returns>
        public bool SetDeviceID(string deviceID)
        {
            DeviceID = deviceID;
            return true;
        }
        /// <summary>
        /// 是否可用
        /// </summary>
        /// <returns></returns>
        public bool IsEnabled()
        {
            return Enabled;
        }
    }
    

    现实考虑

    数据传输对象

    在使用远程接口的情况下,常常使用数据传输对象(DTO)在进程之间传输数据,以减少远程调用数。DTO聚合数据但不包含行为。这是有意为之,为了方便数据同步。

    来源:http://songwenjie.cnblogs.com/
    声明:本文为博主学习感悟总结,水平有限,如果不当,欢迎指正。如果您认为还不错,不妨点击一下下方的推荐按钮,谢谢支持。转载与引用请注明出处。
    微信公众号:
  • 相关阅读:
    几个简单的递归题目
    HDOJ_2222 AC自动机
    展开字符串 字符模拟
    NKOJ_1437 校长杯 赛事安排
    POJ_1979 Red and Black 迷宫类
    看了LINQ Project 的Overview, 我要疯了!
    在PSP上看视频真的爽吗?
    SQL Server用户使用ORACLE应注意的几点:
    一个巨牛的招聘题[转]
    再一次看到DevExpress的控件,我傻了!!
  • 原文地址:https://www.cnblogs.com/songwenjie/p/9011866.html
Copyright © 2011-2022 走看看