zoukankan      html  css  js  c++  java
  • invariant

    1.3. 类必须实现不变式(Classes Should Enforce Invariants)

    Bjarne Stroustrup: 我的基本原则式真正的类必须有一个接口,有一个隐含的不变式(invariant)

    Bill Venners: 不变式(invariant)是什么东西?

    Bjarne Stroustrup: 什么使一个对象(object)有效?是不变式(invariant)。我以vector举例说明。vector知道自己有n个元素(element),vector也知道自己有一个指针指向这些元素。以上两点就是不变式(invariant)。如果vector实际上竟然有n+1个元素,就出问题了。如果vector包含的指针为0,也表示有bug。所以在定义一个类前,你必须明确什么是不变式(invariant),类的接口必须体现了不变式(invariant)。如果类的成员函数(member function)不能体现不变式,也许这个函数应该放到类的外面比较好。牢记类的不变式,你就能得到一个简洁而小的接口,易于理解和维护。

    Bill Venners: 不变式(invariant)是建立类的必要条件?因为类的责任就是维护不变式(invariant)?

    Bjarne Stroustrup: 说得对。

    Bill Venners: 不变式就是类中的各数据间的关系。

    Bjarne Stroustrup: 对。如果任何数据都可以随意定义自己的值,就没有必要创建类。使用结构体(structure)就行了。考虑有一个数据结构只有姓名和地址两个字段,如果随便什么字符串都可以作为有效的姓名或者地址。那么结构体(structure)就够了。把姓名和地址作为私有成员封装到一个类中,再提供一堆访问私有成员的接口函数,是很愚蠢的。更愚蠢的就是再去设计一个基类,把这些接口函数设计为虚函数。没有必要。

    Bill Venners: 你说没有必要是因为,数据的表示(representation)有且仅有一种。如果你要把这一种表示(representation)定义为一个函数,意味着将来你会修改表示(representation)。

    Bjarne Stroustrup: 对。但是有些表示(representation)并不改变。例如整数,浮点数,复数等等。设计时你不得不做决定。

    仍旧以上面的姓名和地址的数据结构为例。接下来,如果你从简单的结构体(structure)转向了真正的类。也许你不会把那个类的名字叫做“姓名和地址的组合类”。也许你会把那个类命名为“个人通讯录”。然后你觉得需要确保“个人通讯录”中的地址应该是有意义的字符串,等等等等。接下来你必须考虑数据的不同表示(representation)。例如姓名字段定义为私有数据成员吗?是否需要定义虚函数?你必须做设计而不是随意定义一些类和函数,你需要使用C++的语法来表达你的核心的设计思想,而不是简单的定义几个私有数据就完事了。(译者按:原文为semantics that you are defending,字面意思是保卫你所使用的符号的意义。)

    例如,构造函数(constructor)建立了环境供成员函数(member function)在其中操作数据。也就是说,构造函数(constructor)建立了不变式(invariant)。要建立不变式(invariant),你必须获得资源。析构函数(destructor)做的事情正相反。资源可以是内存,文件,锁,socket等等。

    1.4. 设计简单的界面(Designing Simple Interfaces) Bill Venners: 你刚才说不变式(invariant)帮助你决定了什么应该进入界面(interface)。你能进一步讲一讲你是如何设计界面的吗?如果一个函数有责任维护不变式(invariant)就应该在类中,对吗?

    Bjarne Stroustrup: 对。

    Bill Venners: 任何使用了数据,但是不维护(defend)不变式(invariant)的操作,就不需要放在class中?

    Bjarne Stroustrup:让我举个例子。有时候某些操作必须直接访问数据才能完成。例如如果你要改变vector的大小,前提条件你必须能够移动其中的元素(element),修改存储大小的变量。如果你没有直接修改数据的权限,为了完成操作,你必须访问有权限的接口函数。但是一个在vector中查找指定元素的操作最好不要定义为vector的成员函数。

    另一个例子是日期类,修改日期类中的年月日数据的操作当然应该是类的成员函数,但是查找下个周末的函数不应该是成员函数。我曾经看到日期类有60或者70个接口函数。接口函数直接访问数据。也就是说,如果你修改了类的数据结构,你就必须检查并修改所有类的接口函数。

    如果日期类只有比如说10个必需的接口。你可以把其余的50个接口建立在这10个接口之上。其余50个接口可以放在支持库中(supporting library)。现在这种设计思想已经被普遍接受了。甚至在java中也是如此。

    过去二十年我就在鼓吹这种设计思想。但是人们就是喜欢把所有的东西都丢到类和子类中去。比如说前面那个糟糕的日期类。如果你需要一些简单的工具操作日期,你必须继承自那个日期类。最后一切都乱七八糟。我仅仅是想自由的组合一些工具进行简单的操作,为什么需要继承呢?如果我想组合使用我的一些工具和你的一些工具,难道我需要定义一个子类,同时继承你的日期类和我的日期类吗?继承引入了不必要的依赖关系。

  • 相关阅读:
    Java实现 LeetCode 394 字符串解码
    Java实现 LeetCode 394 字符串解码
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 390 消除游戏
    Java实现 LeetCode 390 消除游戏
  • 原文地址:https://www.cnblogs.com/diyingyun/p/2301219.html
Copyright © 2011-2022 走看看