先从一个简单案例开始:
某公司开发了一个模拟鸭子程序,这个游戏可以展示很多种鸭子的游泳方式和呷呷叫声。系统最初的设计者们使用了标准的面向对象技术,他们创建了一个Duck基类供所有其它类型的鸭子继承。
[img]http://dl.iteye.com/upload/attachment/301553/0d7ab849-bddb-3259-b194-93dff52ac89d.bmp[/img]
为了提高竞争力,我们现在需要鸭子可以飞。
想法一:仅仅只需要在Duck类里增加fly()方法,然后所有其他鸭子就都可以继承它了。
[img]http://dl.iteye.com/upload/attachment/301557/e66b1dec-001c-3fd1-921f-99c85f2a0139.bmp[/img]
但是这没有注意到并不是所有Duck的子类都需要fly()方法。当给Duck基类增加新行为的时候,同时给那些不需要这些行为的Duck的子类增加了。
局部的代码更新导致了非局部的效果(会飞的橡皮鸭子)!
[img]http://dl.iteye.com/upload/attachment/301563/36bfa2d0-5cf6-33a7-85c2-39765cfb3121.bmp[/img]
想法二:利用接口。
把fly()从Duck基类里拿出来,然后创建一个有fly()方法的Flyable()接口。这样,只有那些需要飞的鸭子才会通过实现这个接口来获得fly()方法…并且,最好再创建一个Quackable接口,因为并不是所有的鸭子都会呷呷叫。
[img]http://dl.iteye.com/upload/attachment/301573/5d9b11b1-661b-36ba-8add-0b9e928bb6c5.bmp[/img]
我们知道并不是所有的子类都有飞行和呷呷叫的行为,所以继承不是正确的方法。但是,尽管让子类实现Flyable或者Quackable解决了部分问题(不会再有会飞的橡皮鸭子),但是这种方法彻底破坏了行为的重用,所以它只是制造了另一个维护上的噩梦。当然,鸭子可能有一种以上的飞行行为…
[b]设计原则一: 识别你的应用程序里变化的部分,并且把它们与不变的部分隔离开。[/b]
从另一个角度考虑这个原则:找到变化并且封装起来,稍后你就可以在不影响其他部分的情况下修改或扩展封装的变化部分。
尽管概念很简单,但是它几乎是所有设计模式的基础。所有模式都提供了使系统里变化的部分独立于其它部分的方法。
好的,是把鸭子的行为从Duck类里拖出来的时候了!拿走变化部分并封装它,所以它将不会影响你的代码。这样做的结果则是:使你的系统只会因代码改变带来的少许影响而有更大的灵活性。
[b]分开变化和不变的部分[/b]
我们从那里开始?在我们知道的范围内,除了fly()和quack()的问题,Duck类工作的很好,它没有其他表现出要变化或频繁改变的地方。所以,除了一些微小的改变,我们很恰当的留下Duck类自己。
现在,从那些保持不变的部分分离出变化的部分,我们将创建两组类(完全从Duck分离出来),一组给飞行,一组给呷呷叫。每组类都将拥有它们各自行为的实现。例如,我们可能有一个实现呷呷叫的类,另一个实现吱吱叫的类,还有一个实现保持沉默的类。
我们知道fly()和quack()都是Duck类在鸭子里不断变化的部分。通过从Duck类分离出这些行为,我们将把两个方法都从Duck类里拖出来并且创建一组新的类来表示每个行为。
[img]http://dl.iteye.com/upload/attachment/301571/2313a9ed-cfe1-3afd-a63d-f0e07206dc3a.bmp[/img]
[b]设计原则二: 面向接口而不是面向实现编程。[/b]
我们将使用一个接口来表示每个行为 – 例如,FlyBehavior和QuackBehavior –每个行为的的执行者都将实现其中一个接口。
在我们的新设计里,Duck基类将使用一个接口(FlyBehavior和QuackBehavior)来表示行为,所以行为(换句话说,具体行为是编写在实现了FlyBehavior接口和QuackBehavior接口的类里)的实际实现者将不再受制于Duck基类。
从现在开始,Duck类的行为将实现在一个单独了类里 - 一个实现了特殊行为接口的类。
另一个方面,Duck类不再需要知道太多关于它们自己实现的细节。
[img]http://dl.iteye.com/upload/attachment/301576/114cf0c9-ac74-38fc-8b4d-c921de40e51a.bmp[/img]
从而只要在Duck类中持有接口的引用,并在初始化Duck子类的时候确定具体的实现FlyBehavior的子类就可以了。具体代码见附件。
某公司开发了一个模拟鸭子程序,这个游戏可以展示很多种鸭子的游泳方式和呷呷叫声。系统最初的设计者们使用了标准的面向对象技术,他们创建了一个Duck基类供所有其它类型的鸭子继承。
[img]http://dl.iteye.com/upload/attachment/301553/0d7ab849-bddb-3259-b194-93dff52ac89d.bmp[/img]
为了提高竞争力,我们现在需要鸭子可以飞。
想法一:仅仅只需要在Duck类里增加fly()方法,然后所有其他鸭子就都可以继承它了。
[img]http://dl.iteye.com/upload/attachment/301557/e66b1dec-001c-3fd1-921f-99c85f2a0139.bmp[/img]
但是这没有注意到并不是所有Duck的子类都需要fly()方法。当给Duck基类增加新行为的时候,同时给那些不需要这些行为的Duck的子类增加了。
局部的代码更新导致了非局部的效果(会飞的橡皮鸭子)!
[img]http://dl.iteye.com/upload/attachment/301563/36bfa2d0-5cf6-33a7-85c2-39765cfb3121.bmp[/img]
想法二:利用接口。
把fly()从Duck基类里拿出来,然后创建一个有fly()方法的Flyable()接口。这样,只有那些需要飞的鸭子才会通过实现这个接口来获得fly()方法…并且,最好再创建一个Quackable接口,因为并不是所有的鸭子都会呷呷叫。
[img]http://dl.iteye.com/upload/attachment/301573/5d9b11b1-661b-36ba-8add-0b9e928bb6c5.bmp[/img]
我们知道并不是所有的子类都有飞行和呷呷叫的行为,所以继承不是正确的方法。但是,尽管让子类实现Flyable或者Quackable解决了部分问题(不会再有会飞的橡皮鸭子),但是这种方法彻底破坏了行为的重用,所以它只是制造了另一个维护上的噩梦。当然,鸭子可能有一种以上的飞行行为…
[b]设计原则一: 识别你的应用程序里变化的部分,并且把它们与不变的部分隔离开。[/b]
从另一个角度考虑这个原则:找到变化并且封装起来,稍后你就可以在不影响其他部分的情况下修改或扩展封装的变化部分。
尽管概念很简单,但是它几乎是所有设计模式的基础。所有模式都提供了使系统里变化的部分独立于其它部分的方法。
好的,是把鸭子的行为从Duck类里拖出来的时候了!拿走变化部分并封装它,所以它将不会影响你的代码。这样做的结果则是:使你的系统只会因代码改变带来的少许影响而有更大的灵活性。
[b]分开变化和不变的部分[/b]
我们从那里开始?在我们知道的范围内,除了fly()和quack()的问题,Duck类工作的很好,它没有其他表现出要变化或频繁改变的地方。所以,除了一些微小的改变,我们很恰当的留下Duck类自己。
现在,从那些保持不变的部分分离出变化的部分,我们将创建两组类(完全从Duck分离出来),一组给飞行,一组给呷呷叫。每组类都将拥有它们各自行为的实现。例如,我们可能有一个实现呷呷叫的类,另一个实现吱吱叫的类,还有一个实现保持沉默的类。
我们知道fly()和quack()都是Duck类在鸭子里不断变化的部分。通过从Duck类分离出这些行为,我们将把两个方法都从Duck类里拖出来并且创建一组新的类来表示每个行为。
[img]http://dl.iteye.com/upload/attachment/301571/2313a9ed-cfe1-3afd-a63d-f0e07206dc3a.bmp[/img]
[b]设计原则二: 面向接口而不是面向实现编程。[/b]
我们将使用一个接口来表示每个行为 – 例如,FlyBehavior和QuackBehavior –每个行为的的执行者都将实现其中一个接口。
在我们的新设计里,Duck基类将使用一个接口(FlyBehavior和QuackBehavior)来表示行为,所以行为(换句话说,具体行为是编写在实现了FlyBehavior接口和QuackBehavior接口的类里)的实际实现者将不再受制于Duck基类。
从现在开始,Duck类的行为将实现在一个单独了类里 - 一个实现了特殊行为接口的类。
另一个方面,Duck类不再需要知道太多关于它们自己实现的细节。
[img]http://dl.iteye.com/upload/attachment/301576/114cf0c9-ac74-38fc-8b4d-c921de40e51a.bmp[/img]
从而只要在Duck类中持有接口的引用,并在初始化Duck子类的时候确定具体的实现FlyBehavior的子类就可以了。具体代码见附件。