又见手把手系列-面向对象扫盲-通俗的OO第一弹-【封装】
最近针对OO有了太多的讨论,太多的误会,太多的不理解。让我来一次性解决什么是对象,为什么要面向对象的问题吧,这是第一篇。
所谓面向对象的编程、设计、思想。我们用大白话来说。
面向对象就是用 某物(对象),是什么(类),有什么(对象的状态),能干什么(方法)的方式来描述程序的方法。这就是面向对象。
面向对象的程序的特征,封装性:也就是某物状态的改变,必须是他自己的行为来改变。
打个比方,我(人类),有钱(有什么),你(人类),要找我借钱(能干什么)。于是
class Man()
{
private int money;
public int Money
{
get{return money}
set{money=value}
}
public void Borrow(Man Target,int Howmuch)
{
money+=Howmuch;
Target.Money-=Howmuch;
}
}
就是这个样子。这样子的写法是不对的,这样破坏类的封装性,我们绕过了Man的对象,直接去操作了对象的状态。这完全是强盗逻辑,跟你直接到我钱包里掏钱一样不可饶恕。所以,借钱这个动作还需要我来参与,你找我借钱,而我,要借给你钱。我们修改一下类的代码。我们给Man类增加一个Lend的方法,以封装在借出钱的时候对自身状态的改变。然后借钱的行为也要更改了。
class Man()
{
private int money;
public int Money
{
get{return money}
set{money=value}
}
public bool Lend(int Howmuch)
{
if(Howmuch<money)
{
money-=Howmuch;
return true;
}else
{
return false;
}
}
public void Borrow(Man Target,int Howmuch)
{
money+=Howmuch;
Target.Lend(Howmuch);
}
}
这样子的代码就是满足了封装性的原则,注意,封装既是特征,也是必须满足的条件,如果破坏了封装性原则那就是破坏了OO的原则。
这里回到了一个很实际的问题,失血模型为什么不OO。
我们定义了Book类,封装了Book的状态(属性)。如果我们用一个BookManager类去Save这本书(典型的失学模式的做法)。那么,我们在BookManager中如何保存?很多人在BookManager.Save里读取Book的属性,生成SQL,写数据库,返回执行结果。这样子OO么?不OO,因为这种做法完全破坏了封装性的原则。就跟之前借钱的例子一样。所以我们要在Book类增加一个Save方法,这个方法是用来维护Book类本身的状态的。而很多的ORM也能够给我们的类注入这类方法,使我们的的类具备持久化的能力。这样Book的属性,如何在数据库里存储,如何读取,如何删除都是Book自身来负责,因为在数据库里关于Book 的记录都是属于Book的状态,所以都需要Book自身来封装,使其透明化。
很多人在之前我写的Book.Save是否OO的话题里面说,觉得Book.Save方法仅仅是换了个位置。但是哪里知道,这个小小的改变却是一个原则性的问题。而至于双鱼座觉得在Save里加入上下文的参数更加符合实际情况,我个人觉的加和不加对Book类是否符合OO的原则没有太大的影响。
但是有和没有确是天壤之别了。
封装是保证系统的高内聚低耦合的很重要的设计原则。
还是用Book作为例子,如果你的Book需要添加一个属性来表示新的状态(换句俗话就是你在数据库里加列了,数据库变化了)。如果你的Book类自己维护了自己的持久化状态,那么你就只需要修改Book类的持久化状态的方法。如果你使用的VS2005的话,使用重构功能一次性就能把所影响到的地方全部修改(java的用Eclipse也有同样的功能)。而如果没有封装的话。那么你可能需要改的还有BookManager,如果在其他地方还用到了Book,可能还有数不清的代码在等着你改。为什么会麻烦,因为没有了封装,类和类之间产生了耦合。
下一章我们会接下来谈继承(也包括接口的Implement)