考虑如下情境:
class A{
functionA(){
1;
}
}
1,我们想要拓展functionA()的功能;2,我们希望在不同的情况下functionA()有不同的实现(多态)。想到多态,我们可以想到用继承实现:
class childA extends A{
functionA(){
1;
2;
}
}
class childB extends A{
functionA(){
1;
3;
}
}
上面的方式在平时实现各种多态功能时经常用到,面向对象编程的一个基本原则是面向接口编程,而不是面向对象,因此我们对上述方式进行抽象,并且用template method模板方法模式实现上述情境:
abstract class A{
functionA(){
1;
functionB()
}
abstract functionB();
}
class childA extends A{
functionA(){
1;
2;
functionB();
}
functionB(){
...
}
}
记得《Java编程思想》一书中提到,使用继承是因为childA和A在逻辑上有很强的关联性,也就是说childA is like A。如果二者并没有很强的关联性,而仅仅只是为了使用继承实现上述情境。那么使用继承并不是一个恰当的选择,严格的说违背了“继承”这一词的意义。我们知道,除了继承,组合也是面向对象语言中一个重要的实现多态的方法。先看如下用组合实现的上述情境:
class A{ B b = new childB1(); functionA(){ b.functionB(); } } interface B{ functionB(); } class childB1 implements B{
functionB(){
}}
仔细观察上述代码,我们可以把B抽象成接口,然后对于B b = new childB1()。不同情境下我们给出不同的实现,从而很好的解决了上述问题,但这依然不是完美的解决方案,因为我们发现在new childB1()生成B的子类对象时,这个子类是写死了的,也就是说A和childB1的依赖性很强,这对于程序设计来说是一个很不好的现象,为了降低这种依赖性,我们考虑下述解决方案:发现二者的区别了么,在类A中专门开辟了一个attach()方法动态的加载B对象,说到此,与平时想见的许多Android源码似乎有种似曾相识的感觉。如果换一下函数和类名称就明白了:class A{ B b; functionA(){ attach(new childB1()); b.functionB(); } attach(B b){ this.b = b; } } interface B{ functionB(); } class childB1 implements B{ functionB(){ } }
class A{ OnClickListener listener; functionA(){ setOnClickListener(new myOnClickListener()); listener.onClick(); } setOnClickListener(OnClickListener listener){ this.listener = listener; } } interface OnClickListener{ onClick(); } class myOnClickListener implements OnClickListener{ onClick(){ } }
这就是观察者模式。这中模式的来由我们可以从上述实际情境得到,它的优势我们可以借助GOF对该模式的总结说明:“我们不希望为了维持一致性而使各类紧密耦合,因为这样降低了它们的可重用性”,也就是说我们保持类A在功能上对类B的依赖性,但我们不希望二者耦合性太强。观察者模式很好的做到了这一点。