第五章--软件构建中的设计

5.1-5.2--设计相关概念


一、理想设计的特征

设计范畴内的特征有:

(1)最小复杂度:设计应该简单且易于理解。

(2)易于维护

(3)松散耦合:合理抽象、封装、信息隐藏,设计出相互关联尽可能少得类。

(4)可扩展性

(5)可重用性

(6)高扇入:让大量的类使用某个给定的类(如工具类)。

(7)低扇入:一个类里适量使用其他类。

(8)可移植性

(9)精简性

(10)层次性

(11)标准技术:尽量用标准化、常用的方法。


二、设计的层次

第一层:软件系统。

第二层:分解为子系统或包。如用户界面、数据库、业务规则、对系统的依赖因素、企业级的工具等。同时,             不同的子系统之间应该制定相应的规则进行限制,使得子系统之间的交互变得简单。

第三层:分解为类。

第四层:分解成子程序。

第五层:子程序内部的设计。


5.3--设计构造块:启发式方法

一、找出现实世界的对象

使用对象进行设计的步骤:

(1)找出对象,确定其属性及方法

(2)确定每个对象可以对其他对象进行的操作:最常见的关系为包含关系和继承关系。

(3)确定哪些部分对其他对象可见。对数据和方法都要明确是public,private还是protected。

        public:可以被任意实体访问

         protected:只允许子类及本类的成员函数访问

         private:只允许本类的成员函数访问

(4)定义每个对象的接口。

实践心得:

在定义类的时候,要确定哪些数据是对象的属性,哪些方法是与对象操作相关的,哪些方法是为了实现对象的某个操作而存在的工具方法。尽量避免将用到的数据或者方法一股脑儿全定义在类的.h文件里,否则类的功能会变得很不清晰。


二、形成一致的抽象

    用一种简化的观点来考虑复杂的概念。


三、封装实现细节

    封装:不只是让你能用简化的视图来看复杂的概念,同时还不能让你看到复杂概念的任何细节。你能看到的就是你能全部得到的。


四、当继承能简化设计时就继承

   注意多态的使用。

   多态:允许将子类类型的指针赋值给父类类型的指针。C++通过虚函数来实现多态。


五、信息的隐藏

   1. 隐藏起来的信息可能是某个易变的区域、文件格式、数据类型的实现方式,或者是需要隔离的区域(这个区域中发生的错误不会给程序其余部分带来太大的损失)。

    类的职责就是把这些信息隐藏起来并保护自己的隐私权!。

   2. 要隐藏的信息主要有两大类:隐藏复杂度(如复杂的数据类型、文件结构、算法等)和隐藏变化源(将变化带来的影响限制在局部范围)。

   3.信息隐藏的障碍:

        一是信息在系统中过度分散。

        二是循环依赖。即A类中得程序调用了B类中的程序,而B类中的程序又调用了A类中的程序。

        三是把类内数据误认为全局数据。

              全局数据会带来两类问题:(1)子程序对全局数据进行操作时,不知道还有其他子程序对这些全局变量进行了操作。(2)子程序知道其他子程序也在用全局数据进行操作,但不明确都进行了哪些操作。

              而类内数据只有类内部的少数几个子程序才能直接访问这些数据,所以很少出现全局数据出现的两个问 题。但是如果类内的子程序过于庞大,那也会出现类似问题。

        四是试图在系统架构层和编码层均避免性能损耗。

               这种担心其实不必要。因为:(1)在系统架构层按照信息隐藏的目标去设计并不会和按照性能目标去设计相冲突。(2)在编码层,能为性能目标所做的最好准备,便是做出高度模块化的设计来。而不是担心因为有了额外层次的对象实例化和子程序调用而带来性能上得损耗。

    4.信息隐藏的价值:

       (1) 按照信息隐藏的原则来思考,可以激发和促进某些设计决策的形成。    

     (2)信息隐藏有助于设计类的公开接口。接口设计的核心:这个类需要隐藏些什么?


    请养成问:“我该隐藏些什么?”的习惯!!!

       

PS:信息隐藏的实践心得(欢迎指正和补充)

   在实际使用,可以通过下面的一些方式来实现信息的隐藏:

(1)将实现细节封装成一个函数:比如要获取ID号,可以用id = newID();如果以后需要改动生成ID的方式,只要在newID函数中进行修改即可。

(2)将具名常量代替字面常量:如在程序的好多代码中都用了“100”这个数,则可以把100写入常量MAX_COUNT中,这样对这个值进行改动时,只需要修改一处即可。

(3)利用typedef来隐藏数据类型:

    例如,如果类的一个公开接口pt_fopen()返回了一个指向文件索引信息的结构体FileIndex(该结构体反映了文件存储格式),这就暴露了你的文件存储格式。进行隐藏时,可以将这个结构体的定义放在类的私有成员中,然后定义typedef void* TPHandle。在子程序中通过强制类型转换的方式来传递想要传递的数据,而调用者只能看到TPHandle为void * ,却不知道其具体的结构。

    再如,可以通过typedef int IdType;来隐藏ID号的类型。以后如果要将ID号修改为string类型,只要修改一处即可。

(4)确定类的公开接口:比如资源包打包工具的类,暴露给外面的接口就是执行打包packFile(),因此可将其访问权限设为pubic,而其具体实现细节则可以放在private中。


六、找出容易改变的区域

   将不稳定的区域隔离开来,将其影响限制在一个子程序、类或者包的内部。

容易改变的区域通常有:

    (1)业务规则:如税率结构改变,合同内容的改变等。

    (2)对硬件的依赖性

    (3)输入和输出

    (4)非标准的语言特性

    (5)困难的设计区域和构建区域

    (6)状态变量:

            不要使用布尔变量作为状态变量,请换用枚举类型。

            使用访问器子程序取代对状态变量的直接检查。(避免硬编码)

    (7)数据量的限制:如定义100个元素的数据的时候,实际上就向外界透露了100这个上限的信息。



七、保持松散耦合

耦合度表示模块之间的关系紧密程度。

耦合标准:

(1)规模:指模块间的连接数。通过一个参数连接起来的两个模块之间的耦合度要小于2个,3个参数的。

具有4个公用方法的类与它的调用者的偶尔度要低于有40个公用方法的。

(2)可见性:指两个模块之间连接的显著程度。程序开发应该通过把模块之间的连接关系变得广为人知而获取信任。因此,应该尽可能通过参数表来传递数据,而不要通过全局数据这种“鬼鬼祟祟”的方法。

(3)灵活性:指模块间是否容易改动。理想状态就像热插拔USB连接器。


耦合的常见种类:

(1)简单数据参数耦合:通过参数传递数据。

(2)简单对象耦合:一个模块实例化一个对象。

(3)对象参数耦合:相对简单数据参数耦合来说,这种更为紧密。

(4)语义上的耦合:一个模块不仅使用了另一个模块的语法元素,还使用了有关那个模块内部工作细节的语义知识。



八、其他设计启发方法

(1)查阅常用的设计模式

(2)高内聚性

(3)构造分层结构

(4)分配职责

(5)为测试而设计

(6)避免失误

(7)有意识的选择绑定时间

(8)创建中央控制点

(9)考虑使用蛮力突破

(10)画图

(11)保持设计的模块化

(12)自上而下

(13)自下而上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值