刚开始接触OOP的时候,打心底里我不喜欢private与protected。我声明一个public然后不直接用它,不就跟private一样吗?在某些场合下,我还能偷偷地用一下public变量,这不是更方便吗?所以,以前写的class,除了class几个字母外,其它的跟struct没啥区别。做了几个小项目之后,我终于浪子回头。下面进入今天的正题——爱上private吧。
理由1。。。
假如一个member value不是public,唯一能够访问它的就是friend和API。我想,没有人会希望在写程序的时候喜欢因考虑是否需要()而浪费时间。所以,将member value声明为private,直观上能帮助我们减少这方面搔首挠耳的时间。比如,下面的这个例子,
class AccessLevels
{
public:
int getReadOnly();
int setReadOnly();
int setWriteOnly();
...
private:
int readOnly;
int writeOnly;
...
};
使用的时候,我们会毫不犹豫地加上()。还不止这些,假如你将member value设置为public,有一天你头晕晕地在A脚本中对readOnly修改为0,然后再B脚本百思不得其解的时候,你就会发现private在保护你的menber value时起到的作用了。
理由2。。。
也许,上面的例子还不足以让你信服,就像当初我刚学OOP时抵触private一般。下面我们换个高级一点的角度来看待这个问题:封装。再来一个简单的例子.
class SpeedDataCollection
{
public:
void addValue(int speed); //添加新的数据
double averageSoFar() const; //返回平均速度
};
averageSoFar()的实现方式有两种,一种是添加一个成员变量,记录到现在为止所有速度的平均值。一种是调用的时候,收集所有速度值,然后再求平均值。
第一种方法会让SpeedDataCollection对象变大,多出的空间用于存放目前的平均值,累积总量,数据数量。但是averageSoFar十分高效。第二种方法不用记录很多内容,但是每次都需要计算,执行比较慢。
试想一下,在一台路边的测速装置上,能有多少内存能够使用。并且也不是每时每刻都需要平均速度,对于这种仪器,使用第一种方法比较合适。而假如我们要在屏幕上面显示当前赛车的平均速度,对于averageSoFar的性能要求就会非常高,所以采用第二种方式比较合适。而客户使用averageSoFar的时候,只会知道效果不错,但是却不用关心背后的实现方式。这就是使用private变量,也就是封装带来的好处,将member隐藏在接口后面,让所有可能的实现变得有弹性。
理由3。。。
最近在使用Unity3D开发游戏,虽然使用的是C#,但是由于使用了过多的public变量让人的确非常头痛。比如,NPC的状态使用一个state变量进行标记,在跑道上,NPC的状态千变万化,也就是在客户的代码上,不断地修改着NPC的状态。比如,使用了一个保护罩,在保护的时间再使用一个保护罩,每个保护罩在消失的时候就会将state从保护状态更改为非保护状态。那么第一个保护罩消失的时候发生了什么事呢?第二个保护罩还在,但是状态已经是非保护状态了。这个时候就非常头疼了,因为使用到state的客户端代码非常多,很难在茫茫人海中找到那个它(bug)。
假如state为private,那么访问它的只有SetState()与GetState(),那么当程序出问题的时候,我或许只需要关注这两个函数的实现方式即可。这多么让人惊讶!!!一开始使用public是为了方便,最后却发现private提供了更多的便利!!!
再举个栗子,我们在程序的后期取消了public ... state变量,那么有多少代码会受到影响呢?那往往是一个不可知的大量。
最后,为什么说使用private而不使用protected呢?##
客户是有相对的,比如,主函数中的代码可以是类的客户,而派生类也可以是客户。假如我们使用了protected,意味着在derived class中可以随心所欲地修改base class中的protected成员。把理由3照搬到这里来依然成立。由此可以看出,当存在继承的时候,base class中的protected与public同样不具备封装性。