Flash AS_开放-封闭原则(OCP)[下] 教程源码
2.3.5 预测变化和“贴切的”结构
如果我们预测到了这种变化,那么就可以设计出一个抽象来隔离它。我们在之前的shape程序中所选定的抽象对于这种变化来说反到成为一种障碍。可能你会觉得很奇怪:还有什么比定义一个Shape类,并从它派生出Square类和Circle类更贴切的结构呢?为何这个贴切的模型不是最优的呢?很明显,这个模型对于一个形状的顺序比形状类型具有更重要意义的系统来说,就不再是贴切的了。
这就导致了一个麻烦的结果,一般而言,无论模块是多么的“封闭”,都会存在一些无法对之封闭的变化。没有对于所有的情况都贴切的模型。
既然不可能完全封闭,那么就必须有策略的对待这个问题。就是说,设计人员必须对于他们设计的模块应该对哪种变化封闭做出选择。他必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离变化。
这需要设计人员具备一些从经验中获得的预测能力。有经验的设计人员希望自己对用户和应用领域很了解,能够以此来判断各种变化的可能性。然后,他可以让设计对于最有可能发生的变化遵循OCP原则。
这一点不容易做到。因为他意味着要根据经验猜测那些应用程序在生长历程中有可能遭受的变化。如果猜测成功就获得成功,如果失败就会遭受失败。。
遵循OCP的代价也是昂贵的。创建正确的抽象是要花费时间和精力的。同时这些抽象也增加了软件的复杂性。
如何知道那些变化可能发生么?首先我们提出正确的问题,并且使用我们的经验和一般常识。最后,我们会一直等到变化发生时才采取行动。
2.3.6放置吊钩(Hook)
我们怎样去隔离变化?在上个世纪,我们常常是的一句话是,我们会在哦我们认为可能发生变化的地方放置吊钩(hook)。我们觉得这样做会是软件灵活一些。
然而,我们放置的吊钩常常是错误的。更糟糕的是,即使不使用这些吊钩,也必须要去支持和维护他们,从而就具有了不必要的复杂性的臭味·。这不是一件好事。我们不希望设计背着许多不必要的抽象。
1.只受一次愚弄
“愚弄我一次,应该羞愧的是你。再次愚弄我,应该羞愧的是我。”这也是一个有效的对待软件设计的态度。为了防止软件背负不必要的复杂性,我们会允许自己被愚弄一次。这意味着在我们最初编写代码时,假设变化不会发生。当变化发生时,我们就创建抽象来隔离他们。
2.刺激变化
接下来的工作列表如下:
1.我们首先编写测试。测试描绘了系统的一种使用方法。首先编写测试。我们迫使系统成为可测试的。在一个具有可测试新的系统中发生变化时,我们可以坦然对之。因为我们已经构建了使系统可以测试的抽象。
2.我们使用很短的迭代周期进行开发-通常为几天。
3.我们在加入基础结构前就开发特性,并且经常把这些特性展示给大家。
4.首先开发最重要的特性。
5.尽早的发布软件。
2.3.7 使用抽象获得显式封闭
第一次重大修改已经产生,用户需要我们在绘制正方形之前先绘制所有的圆形。现在我们希望可以隔离以后所有同类的变化。
怎样才能使得DrawAllShape函数对于绘制顺序的变化是封闭的呢?请记住封闭时间里在抽象的基础上的。因此,为了让DrawAllShape函数对于绘制顺序封闭,我们需要一种“顺序抽象体”。这个抽象体定义了一个抽象接口,通过这个抽象接口可以表示任何可能的排序策略。
一个排序策略意味着,给定的两个对象可以推导出哪一个应该先绘制。我们可以定义一个Shape类的抽象方法叫Precedes。这个方法以另一个shape作为参数,并返回一个bool型。如果接收消息的shape对象应该先于作为参数传入的shape对象绘制,那么函数返回true。
public class Shape {
public function Draw():void{
return;
}
public function Precedes(shape:Shape):boolean{return false}
}
以上代码显示了基类中的Precedes函数,之后在DrawAllShape中就可以调用该函数来判断绘制的先后顺序。接下来让我们来看一下具体到Circle类Precedes函数应该如何被覆盖呢?
public class Circle extends Shape {
public function Circle() {
}
override function Draw():void{
//绘制圆形
}
override function Precedes(shape:Shape){
if((<code><span style="color: #000000;">typeof shape)=="Square"</span></code>){
return true;
}return false;
}
}
显然这个函数以及所有Shape类的派生类中的Precedes函数都不符合OCP原则,没有办法使得这些函数对于Shape类的新派生类做到封闭。每次创建一个新的Shape类的派生类是,所有的Precedes函数都需要做改动。
我们的设计再次遭受沉重的打击。
3.3.8使用“数据驱动”的方法获取封闭性
如果我们要使Shape类的各个派生类间互不知晓,可以使用表格驱动的方法。下面的程序展示了一种现实。
package com.mj.c9.ocp {
/**
* 主程序,负责绘制现状列表
*/
public class application {
function DrawAllShapes(shapelist:Array){
SortShapes(shapelist);//先对形状列表进行排序
for(var i:Number=0;i<shapelist.length;i++){
var t_shape:Shape = shapelist[i] as Shape;//将列表中的对象全部转换成Shape基类,这样可以调用统一的方法。
t_shape.Draw();
}
}
function SortShapes(shapelist:Array){
//根据类型进行排序
.....排序算法
}
}
}
通过这种方法,我们成功的做到了在一般情况下的DrawAllShapes函数对于顺序绘制问题的封闭,也使得每个shape派生类对于新的shape派生类的创建或者修改是封闭的。
结论
在许多方面,OCP都是面向对象设计的核心。遵循这个原则可以带来面向对象技术所声称的巨大好处。(灵活,可重用,可维护)但是,对于应用程序的每一个部分都肆意的进行抽象同样不是好主意。拒绝不成熟的抽象与抽象本身一样重要。
Word教程网 | Excel教程网 | Dreamweaver教程网 | Fireworks教程网 | PPT教程网 | FLASH教程网 | PS教程网 |
HTML教程网 | DIV CSS教程网 | FLASH AS教程网 | ACCESS教程网 | SQL SERVER教程网 | C语言教程网 | JAVASCRIPT教程网 |
ASP教程网 | ASP.NET教程网 | CorelDraw教程网 |