对为什么使用访问器(getter),以及什么是继承的一点看法

我们知道,一个高内聚、低耦合的模块是一个可复用性,可维护性都比较高的模块。

使用访问器有助于使一个类降低耦合性。

我们来看一个例子:

1class Line
2{
3public:
4    Point start;
5    Point end;
6    double length;
7}
;
8

这个类看起来比较合理,因为一个线段具有起点、终点和长度(即使长度为0)等属性。

然而,线段的长度却是由起点和终点的位置决定的。

所以一个看起来比较好的实现方式可能是这样的:

1class Line
2{
3public:
4    Point start;
5    Point end;
6    double getLength() return start.distanceTo(end); }
7}

8

然而,在以后的开发过程中,你可能由于性能的考虑而修改这个类,比如说这样:

从上一个类变化到这个类会引起非常大的变化。

但是,如果在一开始,我们就使用访问器的话,这个变化就被限制在了这个类内部,而不会扩散到整个程序。

这说明了,在面向对象的语言中,我们应该尽可能的使用访问器而不是字段(field)。

为什么访问器这么有效?

我们来看看上一个例子中,究竟发生了什么变化,使得这种变化影响了整个程序。

这个变化就是,我们原本使用一个字段来获得数据,但是后来,我们使用了一个方法来获得数据。

这种本质上的改变,使得我们必须得重新检查我们的程序。

但是访问器使得我们通过一个统一的接口去访问类中的数据,并且,我们不知道这个数据是通过存储还是计算来实现的。

也就是说,访问器掩盖了数据的存储形式和计算过程,使得这个类的内聚性提高了,从而降低了与系统的其他部分的耦合。

最近翻了翻书,发现各种书里面,关于继承的说法都很复杂。

在我看来,父类就是对子类的抽象,子类是具体的事物,父类是抽象的事物。

父类与子类的关系是抽象和具体的关系,是一般和特殊的关系。

例如:

Line是一个一般的线段,描述了一般的线段都具有的行为和属性。

Line2D是一个2D的线段,它实现了一个画一个2D线段的方法。

Line3D是一个3D的线段,它实现了一个画一个3D线段的方法(很炫是么,呵呵)。

可以看出,Line是对所有的线段的一个抽象,Line2D是一个具体的2D线段,Line3D是个具体的3D线段。

Line是一个一般的线段,而Line2D、Line3D是特殊的线段。

继承没什么神秘的,就是建立了这种一般到特殊的关系。

我们用is-a原则判断继承关系的时候,实际上就是判断子类是不是一个特殊的父类,或者说子类是不是父类的一个具体实现。

下面我们再说Liskov替换原则

Liskov替换原则:子类对象能够替换父类的对象而被使用。

这个原则是什么意思呢?

子类对象能够替换父类对象,意味着,子类对象实现的各种方法的意义与父类相同,他们的意图一致。

显而易见,因为子类是特殊,父类是一般,所以父类的含义应该比子类更广。

这也是为什么父类又被称为超类。

也就是说,子类的含义是被其父类所包含的,子类的能力不能超出其父类的能力的含义所包含的范围。

并且,子类的的含义应该与其父类相同。

比如说上面的例子。

Line要求Draw方法画一条线段。那么Line2D中的Draw方法,就不能画一个圆,只能也是画一个线段。

但是Line对于Draw方法画一条什么样的线段并没有规定,所以Line2D中的Draw方法可以画任意一种线段。

这个理解起来比较复杂,我觉得还是没办法用清晰地语言去描述。

但是,只要牢牢抓住一点就可以了:子类是父类的特殊形式。子类必须在父类所涵盖的范围内。

原文地址:https://www.cnblogs.com/HCOONa/p/1593827.html