在面向对象的设计中,会遇到很多代码库写到hook(钩子)这个东西。对新手来讲,明白hook是什么很重要,它意味着更加精细的“隔离”设计,而不是粗放地直接做继承。
在Thinking in Java中,谈到Iterator的时候提出了一个问题:如果某个方法需要去一个个地处理一堆元素,直接继承Collection和还是实现Iterator好用一点呢?
直观来看,直接继承Collection是一个方便的选择。
class InterfaceVsIterator {
public static void display(Iterator<Pet> it) {
while (it.hasNext()) {
Pet p = it.next();
System.out.println(p.id() + ":" + p + " ");
}
}
public static void display(Collection<Pet> pets) {
for (Pet pet : pets) {
System.out.println(p.id() + ":" + p + " ");
}
System.out.println();
}
}
这里的InterfaceVsIterator
其实是一个消费者,专门消费提供的Collection
或者Iterator
。那么,消费哪一种东西会让你的代码变得更加灵活呢?
如果是直接继承AbstractCollection
则需要实现size()
和iterator()
方法,例如:
class CollectionSequence extends AbstractCollection<Pet> {
private Pet[] pets = Pets.createArray(8);
@Override
public Iterator<Pet> iterator() {
return new Iterator<Pet>() {
private int index = 0;
public boolean hasNext() {
return index < pets.length;
}
public Pet next() {
return pets[index++];
}
};
}
@Override
public int size() {
return pets.length;
}
}
不仅是要实现display()
用不到的size()
方法,并且同时需要提供Iterator
的一个实例。
另一方面,Java的语法是不支持多重继承的。所以,如果你的类已经继承了某个别的不同于AbstractCollection
的类,则它就无法再去继承AbstractCollection
了。而接口则是可以实现多个。
class PetSequence {
protected Pet[] pets = new Pet[] {new Pet(), new Pet()};
}
public class NonCollectionSequence extends PetSequence {
public Iterator<Pet> iterator() {
return new Iterator<Pet>() {
private int index = 0;
public boolean hasNext() {
return index < pets.length;
}
public Pet next() {
return pets[index++];
}
};
}
public static void main(String[] args) {
NonCollectionSequence nc = new NonCollectionSequence();
InterfaceVsIterator.display(nc.iterator());
}
}
使用Iterator
有更多的灵活性,本质上它体现了Interface
在Java中的灵活性。Interface
可以将真正被使用于business logic的部分最小化,不必实现非业务的多余部分。另一方面,由于可以实现多个Interface
,它可以为已有的组件提供更多的功能。
这里,这个被复用的部分就是hook。
总结起来,Interface
更像是一个强制实现某个方法的工具。有了它,就可以在语法层级强制实现某个方法,从而保证了hook上达成一致。
网友评论