继承是一种Is-a关系,即类B在语义上可以说是A类,就可以让B类继承A类。例如Cat(猫)是Animals(动物),我们就能让Cat extends Animals。

聚合是一种Has-a(包含)关系,即类B在语义上包含另外一个类,并且类A在类B中以整体的功能充当某个角色的时候。注意:很多时候,A类在B类外面是透明的。例如,一只猫(Cat)有眼睛(Eyes)。那么可以设计成Cat类包含Eyes类,至于为什么A类在B类外是透明的 。聚合代码如下:

 

public class Cat {
private Eyes eyes;

public Cat(Eyes eyes) {
this.eyes = eyes;
}

public void see() {
eyes.see();
}
}
public class Eyes {
public void see() {
System.out.println("see");
}
}

  在使用Cat类对象时,并不需要关注其内部对于see()方法的实现,很明显的猫也不是眼睛(不是Is-a关系)。

  再来看一个问题:栈并不是一个链表,采用继承的方式是不合适的。继承之所以是Is-a关系,是因为它继承了父类全部的方法,即它拥有了父类所有的行为。在上面的继承实现栈的方法中,栈继承了链表的所有方法,这样你就不能保证栈的特性—first in last out(先进后出)了。因为它同时也是链表,可以调用其从父类继承的方法在任一位置做删除和插入。下面是一种更好的has-a实现方式:

public class Stack {
private List list;

public void push(Object object) { // 压栈
list.add(0, object);
}

public Object peek() { // 获取栈顶元素
if (isEmpty()) {
return null;
}
return list.get(0);
}

public boolean isEmpty() { // 判断栈是否为空
return list.size()==0;
}

public Object pop() { // 弹栈
if (isEmpty()) {
return null;
}
return list.remove(0);
}
}

  此时

栈对外的表现就纯粹的是栈了,因为它只具有栈的行为。至于里面是如何实现的,用户无需关心,同时也是安全的,用户除了入栈出栈之外,无法做其他的删除插入操作。这里选用了List作为其属性,没有限制用户使用ArrayList或者LinkedList,其实可以使用Collection属性,用户的选择就更大了。当然也能使用数组。可见对外表现一致的栈,其内部实现可以多样化,即在has-a关系中A类在B类外面是透明的。在利用List实现时你也可以采用与上面不同的策略:每次取出列表的最后一个元素,将元素入栈时加入列表的最后一个位置。

  现在你可以做出艰难的决定了。简单地说,如果在语义上你能说B是A,那么采用继承,否则使用聚合。聚合使用的场合会多一些。




 

 

 

 

 

 

 

 

 

 

posted on 2012-03-01 14:33  王和平  阅读(922)  评论(0编辑  收藏  举报