博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

第四次读书笔记——《代码大全》(续)

Posted on 2018-03-30 18:59  HelenL  阅读(212)  评论(1编辑  收藏  举报

这本书谈到了一些不太常用的启发性方法,感觉有的更是一种设计思想。首先是高内聚性。之前提到,松散耦合是子系统与子系统间,类与类之间的一种连接关系,而内聚性则是关注一个系统、类的内部。内聚性指的是类内部的子程序或者子程序内的所有代码在支持一个中心目标上的紧密程度。当一个类的代码越集中在一个中心目标时,你就越容易记住这些代码的功能所在。

接下来是分层结构。最通用的或者最抽象的概念表示位于层次关系的最上面,而详细的具有特定意义的概念则放在更低的层次中。分层使你能专注于该层的细节而忽略到其他的细节。

把每个类的接口看作是与程序的其余部分之间的一项契约会有助于更好的洞察程序。其中有两个名词第一次接触:“前条件”和“后条件”。前条件是指类的客户向该类所作出的承诺,后条件指对象向其客户所做的承诺。契约的思想可以让对象安全地忽略掉契约范围之外的任何行为。

为测试而设计。在我看来这是一个重要的思想,我们如果想要进行单元测试,或者对某一个子块进行独立的检查,那么就要使系统与系统之间的依赖达到最小。

创建中央控点。“对于每一段有作用的代码,应该只有唯一的一个地方可以看到它,并且也只能在一个正确的位置去做可能的维护性修改”(Plauger)。控制可以被集中在类,子程序,预处理宏以及#include文件里,甚至一个具名变量。

“黑盒子”是模块化设计的一个形象的比喻,黑盒子有着简洁的接口设计个定义明确的功能,对于给定任何输入,都能准确与其相应的输出结果。模块这个概念和信息隐藏、封装是密切相关的。

以上是启发性设计,接下来是有关设计实践的摘录。

要尝试从高层和底层的不同视角审视问题,从高层视角中得出的大范围图景会有助于你把相关的底层细节纳入考虑。从底层视角中获得的细节也会为你的高层决策奠定基础。,这样所创建的结构远远稳定于单纯自上而下或自下而上创建的结构。

在记录设计成果的一些方法中,书上提到了利用UML的方法。UML提供了一套丰富的形式化的表示法,可用于设计实体及其关系。可以用费亨氏的UML图来帮助讨论和发现设计思路。

 

对于类。使用ADT的好处是:可以隐藏实现细节,改动不会影响到整个程序,让接口提供更多的信息,这也进而让程序的正确性更显而易见,式程序更具有自我说明性,这三点是相辅相成的,也就是说,我们在接口处不体现细节,而是描述这个功能,更方便理解,事实上也更容易检查出错误。ADT的好处在文章有一句话描述的很好:你可以像在现实世界中那样操作实体,而不用在底层实现上操作它。准确的说,应该是程序的绝大部分能完全以“真实世界中的事物”这个概念来操作,而不用关心其底层细节。注意点:尽量让类和访问器子程序的名字与储存数据的方式无关,并值奇迹抽象数据本身,否则相当于暴露太多信息。

类的接口应该展现一致的抽象层次。并要形成习惯性思维:即提供成对的服务。把不相关的信息转移到其他类中。要尽可能让接口可编程,而不是表达语义。这是指“本接口将会被怎么使用”最好不依赖于一些语义上的类似于注释的说明。一个接口中任何无法通过编译器强制实施的部分可能就是一个可能被误用的部分。要想办法把语义接口的元素转换为编程接口的元素,比如说Assert(断言)。

类的良好封装:要尽可能地限制类和成员的可访问性,不要公开暴露成员数据。要避免把私用的实现细节放入类的接口。这里有一个处理C++代码暴露细节的例子:

class Employee{
public:
    ...
private:
    String m_Name;
    String m_Address;
    int m_jobClass;
    ...
};

这段代码将地址信息用String来保存暴露了出来。有一个惯用法:

class Employee{
public:
      ...
private:
      EmploeeImplementation *m_implementation;       
};

这样就将实现细节放到了EmploeeImplementation这个类当中。

不要对类的使用者做出任何假设,类的设计和实现应给符合在类的接口中所隐含的契约,除了这些契约应该避免做出过多的假定。

不要因为一个子程序仅仅使用公用的子程序,就把它归入公开接口,相反,应该问的问题是,把这个子程序暴露给外界后,接口所展示的抽象是否还一致。

要格外注意不要从语义上破坏封装性,这是b编译器没法发现的。比如:不去调用A类的InitializeOperations()子程序,因为你知道A类的performFirstOperation()子程序会自动调用它。

问题在于,调用代码不是依赖于类的公开接口,而是依赖于类的私用实现。当发现通过类的内部来得知如何使用这个类,就不是在针对接口编程,那么封装性就被破坏了。

我的理解大概是,你一但修改了类的内部,你基于这些类的内部信息的一些调用就会出现逻辑错误,比如performFistOperation()被修改成不自动调用,那么外部函数在使用这个类时就会出错,那么久没有了封装的意义。