面向对象理念中一个低层次的概念叫封装,封装是为了信息隐藏。信息隐藏是一种理念或者说是一种笼统的概念。具体操作起来,首当其冲需要被隐藏的就是属性。
所以,先行者早就告诫我们,应该把一个类的所有属性都设置为私有的,然后提供其set/get方法。比如:
public class Money {
private double dollar;
public double getDollar() {
return dollar;
}
public double setDollar(double dollar) {
this.dollar = dollar;
}
}
这样做的好处是,如果有一天我们对这个类进行改变时,可以尽量少的影响这个类的client。拿上面的例子而言,如果有一天,我们我不再想使用美元作为存储,而是换为人民币,我们可以将类的实现改为:
public class Money {
private double rmb;
private static double exchangeRate = 8.3;
public double getDollar() {
return rmb/exchangeRate;
}
public double setDollar(double dollar) {
this.rmb = dollar*exchangeRate;
}
public static double setExchangeRate(double rate) {
exchangeRate = rate;
}
}
我们可以把方法理解为接口,把属性看作实现。这种把属性隐藏起来的行为就是面向对象设计中最核心的理念:将接口与实现分离,对接口进行编程。
但是,在具体操作的时候,程序员非常厌倦于为每个属性实现默认的set/get方法。实在的讲,这确实不是一件有趣的事情。但为了迎合这种理念,却又不得不硬着头皮,一边诅咒着,一边安慰自己——这样做对未来会有潜在的好处,但,谁知道这种该死的好处什么时候才能出现。谁会喜欢为未知的事情买单呢?
到了需要改变这一切的时候了:编程应该是一件有趣的事情,我们不应该让程序员宝贵的时间和热情花费在这种毫无生趣的地方,这些高度模式化的东西应该交给编译器去做。
传统的OO编程语言将属性看作实现细节,我们首先要改变这种观念:属性也可以是一种接口。
既然是接口,它就必须具备应对变化的能力。所以,我们应该允许程序员变更访问它的方式。而对一个属性的访问方式正是set/get,也就是读取和赋值。我们以上面的例子来展示一下新的方式:
public class Money {
public double dollar;
}
public class client {
Money money = new Money();
public void doSomething() {
money.dollar = 10;
double dollar = money.dollar;
}
}
到现在为止,你一定会质疑,你到底在搞什么?这不就是把属性暴露给client了吗,你在破坏封装。别急,好戏在后面——
当我们需要将dollar变为rmb时,我们可以将类的实现变为:
public class Money {
private double rmb;
private static double exchangeRate = 8.3;
public derived double dollar {
set{
this.rmb = value*exchangeRate;
}
get {
return rmb/exchangeRate;
}
}
}
注意,我们在新版本的Money类中仍然提供了dollar属性,按照需求,dollar是不应该作为一个实际的属性存在的,所以我们在前面使用derived关键字来说明这是一个导出属性。导出属性本身不会占用任何存储空间。
从这个示例可以看出,我们可以为一个导出属性提供set/get方法,client对其进行读取/赋值操作时,其get/set方法会被自动调用。比如:
Money money = new Money();
money.dollar = 3.4; // set方法被潜在的调用;
double dollar = money.dollar; // get方法被潜在的调用;
另外,指明一点,在set方法中,我们使用value关键字来访问传入的参数。
我们已经看出,我们通过提供导出属性的概念,为属性提供了应对变化的能力,让属性具备的接口的能力。这种思路还可以进一步延伸。
我们先来看一看这个用传统的方法实现的例子:
public class Male {
private Female wife;
public void setWife(Female lady) {
if(lady != null) {
if(lady.getHusband() == null) {
lady.setHusband(this);
}
else {
assert lady.getHusband() == this;
}
}
this.wife = lady;
}
public Female getWife() {
if(this.wife != null)
assert this.wife.getHusband() == this;
return this.wife;
}
public void devorce() {
if(this.wife() != null)
this.wife().setHusband(null);
setWife(null);
}
}
public class Female {
private Male husband;
public void setHusband(Male man) {
if(man != null) {
if(man.getWife() == null) {
man.setWife(this);
}
else {
assert man.getWife() == this;
}
}
this.husband = man;
}
public void getHusband() {
if(this.husband != null)
assert this.husband.getWife() == this;
return this.husband;
}
public void devorce() {
if(this.husband() != null)
this.husband().setWife(null);
setHusband(null);
}
}
这个例子比较复杂,因为Male的wife属性,以及Female的husband属性的set/get方法并不仅仅是默认的方式,它们做了更多的事情。基于这样的需要,我们也应该允许程序员为非导出属性提供set/get方法。下面是上面例子的Male class以新方式进行的实现。
public class Male {
public Female wife {
set {
if(value != null) {
if(value.husband == null)
value.husband = this;
else
assert value.husband == this;
}
this.wife = value;
}
get {
if(this.wife != null)
this.wife.husband == this;
return this.wife;
}
}
public void devorce() {
if(this.wife != null)
this.wife.husband = null;
this.wife = null;
}
}
好的,现在我们已经有了两种属性,普通属性和导出属性,它们都可以有自己的set/get方法。还可以有更多类型的属性吗?我们再来看一个以传统方法实现的例子:
public abstract class Operation {
public abstract boolean isAbstract();
...
}
public class ClassOperation extends Operation{
private boolean isAbstract = false;
public boolean isAbstract() {
return this.isAbstract;
}
...
}
public class InterfaceOperation extends Operation{
public boolean isAbstract() {
return true;
}
}
在这个例子中,抽象基类Operation中的isAbstract方法被两个子类赋予了不同的实现。ClassOperation是以查询属性isAbstract值的方式来实现的,而InterfaceOperation则总认为自己是abstract的。
isAbstract是一个查询方法接口,按照我们新的观念,属性同样也是一种接口,并且属性的get方法本身就是无参数的查询方法接口,为什么我们不同在基类中将isAbstract实现为属性呢?
问题在于,非导出属性会造成存储空间的占用,而导出属性又限制了属性本身实现的方式,这对于Operation类中对于isAbstract()方法的抽象实现的概念都不吻合,所以我们需要第三种概念:抽象属性。
抽象属性仅仅指定了属性的存在,并提供了属性的访问接口,具体到继承类中,你可以用普通属性来实现,也可以以到处属性来实现。抽象属性并不关心具体的实现方式。
看一看上面例子的新实现:
public abstract class Operation {
public abstract boolean isAbstract { get; } // readonly
...
}
public class ClassOperation extends Operation{
public boolean isAbstract = false;
...
}
public class InterfaceOperation extends Operation{
public derived boolean isAbstract {
get { return true; }
}
...
}
Operation op1 = new ClassOperation();
if(op1.isAbstract) // ClassOperation的isAbstract的值被读取;
...
Operation op2 = new InterfaceOperation();
if(op2.isAbstract) // InterfaceOperation的导出属性isAbstract的get方法被调用。
...
鉴于抽象属性的性质,我们也可以在Interface中定义抽象属性:
public interface Classifier {
boolean isPublic { get; } // abstract, readonly
...
}
这就是对于属性的新的概念。所有这些概念都围绕着一个基本的思想:属性也是一种接口。这一点与过去把属性看作实现从本质上有着完全的不同。当属性也是一个接口的时候,程序员就可以更加轻松,更加灵活的对系统进行构造。
你可以使用这个链接引用该篇文章 http://publishblog.blogchina.com/blog/tb.b?diaryID=650138