您好,欢迎访问三七文档
当我们掌握了Java的语法,当我们了解了面向对象的封装、继承、多态等特性,当我们可以用Swing、Servlet、JSP技术构建桌面以及Web应用,不意味着我们可以写出面向对象的程序,不意味着我们可以很好的实现代码复用,弹性维护,不意味着我们可以实现在维护、扩展基础上的代码复用。一把刀,可以使你制敌于无形而于江湖扬名,也可以只是一把利刃而使你切菜平静。Java,就是这把刀,它的威力取决于你使用的方式。当我们陷入无尽无止重复代码的泥沼,当我们面临牵一发而动全身的维护恶梦,你应该想起“设计模式”这个行动秘笈。面向对象的精义,看似平淡,其实要经过艰苦实践才能成功。而构造OO系统的隐含经验于是被前人搜集而成并冠以“设计模式”之名。我们应该在编码行动初始就携带以它。接下来,让我们步“四人组”先行者之后,用中国文字、用实际案例领略模式于我们代码焕然一新的改变:设计模式解读之一:策略模式1.模式定义把会变化的内容取出并封装起来,以便以后可以轻易地改动或扩充部分,而不影响不需要变化的其他部分;2.问题缘起当涉及至代码维护时,为了复用目的而使用继承,结局并不完美。对父类的修改,会影响到子类型。在超类中增加的方法,会导致子类型有该方法,甚至连那些不该具备该方法的子类型也无法免除。示例,一个鸭子类型:publicabstractclassDuck{//所有的鸭子均会叫以及游泳,所以父类中处理这部分代码publicvoidquack(){System.out.println(Quack);}publicvoidswim(){System.out.println(Allducksfloat,evendecoys.);}//因为每种鸭子的外观是不同的,所以父类中该方法是抽象的,由子类型自己完成。publicabstractvoiddisplay();}publicclassMallardDuckextendsDuck{//野鸭外观显示为绿头publicvoiddisplay(){System.out.println(Greenhead.);}}publicclassRedHeadDuckextendsDuck{//红头鸭显示为红头publicvoiddisplay(){System.out.println(Redhead.);}}publicclassRubberDuckextendsDuck{//橡皮鸭叫声为吱吱叫,所以重写父类以改写行为publicvoidquack(){System.out.println(Squeak);}//橡皮鸭显示为黄头publicvoiddisplay(){System.out.println(Yellowhead.);}}上述代码,初始实现得非常好。现在我们如果给Duck.java中加入fly()方法的话,那么在子类型中均有了该方法,于是我们看到了会飞的橡皮鸭子,你看过吗?当然,我们可以在子类中通过空实现重写该方法以解决该方法对于子类型的影响。但是父类中再增加其它的方法呢?通过继承在父类中提供行为,会导致以下缺点:a.代码在多个子类中重复;b.运行时的行为不容易改变;c.改变会牵一发动全身,造成部分子类型不想要的改变;好啦,还是刚才鸭子的例子,你也许想到使用接口,将飞的行为、叫的行为定义为接口,然后让Duck的各种子类型实现这些接口。这时侯代码类似于:publicabstractclassDuck{//将变化的行为fly()以及quake()从Duck类中分离出去定义形成接口,有需求的子类中自行去实现publicvoidswim(){System.out.println(Allducksfloat,evendecoys.);}publicabstractvoiddisplay();}//变化的fly()行为定义形成的接口publicinterfaceFlyBehavior{voidfly();}//变化的quack()行为定义形成的接口publicinterfaceQuackBehavior{voidquack();}//野鸭子会飞以及叫,所以实现接口FlyBehavior,QuackBehaviorpublicclassMallardDuckextendsDuckimplementsFlyBehavior,QuackBehavior{publicvoiddisplay(){System.out.println(Greenhead.);}publicvoidfly(){System.out.println(Fly.);}publicvoidquack(){System.out.println(Quack.);}}//红头鸭子会飞以及叫,所以也实现接口FlyBehavior,QuackBehaviorpublicclassRedHeadDuckextendsDuckimplementsFlyBehavior,QuackBehavior{publicvoiddisplay(){System.out.println(Redhead.);}publicvoidfly(){System.out.println(Fly.);}publicvoidquack(){System.out.println(Quack.);}}//橡皮鸭不会飞,但会吱吱叫,所以只实现接口QuackBehaviorpublicclassRubberDuckextendsDuckimplementsQuackBehavior{//橡皮鸭叫声为吱吱叫publicvoidquack(){System.out.println(Squeak);}//橡皮鸭显示为黄头publicvoiddisplay(){System.out.println(Yellowhead.);}}上述代码虽然解决了一部分问题,让子类型可以有选择地提供一些行为(例如fly()方法将不会出现在橡皮鸭中).但我们也看到,野鸭子MallardDuck.java和红头鸭子RedHeadDuck.java的一些相同行为代码不能得到重复使用。很大程度上这是从一个火坑跳到另一个火坑。在一段程序之后,让我们从细节中跳出来,关注一些共性问题。不管使用什么语言,构建什么应用,在软件开发上,一直伴随着的不变的真理是:需要一直在变化。不管当初软件设计得多好,一段时间之后,总是需要成长与改变,否则软件就会死亡。我们知道,继承在某种程度上可以实现代码重用,但是父类(例如鸭子类Duck)的行为在子类型中是不断变化的,让所有子类型都有这些行为是不恰当的。我们可以将这些行为定义为接口,让Duck的各种子类型去实现,但接口不具有实现代码,所以实现接口无法达到代码复用。这意味着,当我们需要修改某个行为,必须往下追踪并在每一个定义此行为的类中修改它,一不小心,会造成新的错误。设计原则:把应用中变化的地方独立出来,不要和那些不需要变化的代码混在一起。这样代码变化引起的不经意后果变少,系统变得更有弹性。按照上述设计原则,我们重新审视之前的Duck代码。1)分开变化的内容和不变的内容Duck类中的行为fly(),quack(),每个子类型可能有自己特有的表现,这就是所谓的变化的内容。Duck类中的行为swim()每个子类型的表现均相同,这就是所谓不变的内容。我们将变化的内容从Duck()类中剥离出来单独定义形成接口以及一系列的实现类型。将变化的内容定义形成接口可实现变化内容和不变内容的剥离。其实现类型可实现变化内容的重用。这些实现类并非Duck.java的子类型,而是专门的一组实现类,称之为行为类。由行为类而不是Duck.java的子类型来实现接口。这样,才能保证变化的行为独立于不变的内容。于是我们有:变化的内容://变化的fly()行为定义形成的接口publicinterfaceFlyBehavior{voidfly();}//变化的fly()行为的实现类之一publicclassFlyWithWingsimplementsFlyBehavior{publicvoidfly(){System.out.println(I'mflying.);}}//变化的fly()行为的实现类之二publicclassFlyNoWayimplementsFlyBehavior{publicvoidfly(){System.out.println(Ican'tfly.);}}-----------------------------------------------------------------//变化的quack()行为定义形成的接口publicinterfaceQuackBehavior{voidquack();}//变化的quack()行为实现类之一publicclassQuackimplementsQuackBehavior{publicvoidquack(){System.out.println(Quack);}}//变化的quack()行为实现类之二publicclassSqueakimplementsQuackBehavior{publicvoidquack(){System.out.println(Squeak.);}}//变化的quack()行为实现类之三publicclassMuteQuackimplementsQuackBehavior{publicvoidquack(){System.out.println(Slience);}}通过以上设计,fly()行为以及quack()行为已经和Duck.java没有什么关系,可以充分得到复用。而且我们很容易增加新的行为,既不影响现有的行为,也不影响Duck.java。但是,大家可能有个疑问,就是在面向对象中行为不是体现为方法吗?为什么现在被定义形成类(例如Squeak.java)?在OO中,类代表的东西一般是既有状态(实例变量)又有方法。只是在本例中碰巧东西是个行为。既使是行为,也有属性及方法,例如飞行行为,也需要一些属性记录飞行的状态,如飞行高度、速度等。2)整合变化的内容和不变的内容Duck.java将fly()以及quack()的行为委托给行为类处理。不变的内容:publicabstractclassDuck{//将行为类声明为接口类型,降低对行为实现类型的依赖FlyBehaviorflyBehavior;QuackBehaviorquackBehavior;publicvoidperformFly(){//不自行处理fly()行为,而是委拖给引用flyBehavior所指向的行为对象flyBehavior.fly();}publicvoidperformQuack(){quackBehavior.quack();}publicvoidswim(){System.out.println(Allducksfloat,evendecoys.);}publicabstractvoiddisplay();}Duck.java不关心如何进行fly()以及quack(),这些细节交由具体的行为类完成。publicclassMallardDuckextendsDuck{publicMallardDuck(){flyBehavior=newFlyWithWings();quackBehavior=newQuack();}publicvoiddisplay(){System.out.println(Greenhead.);}}测试类:publicclassDuckTest{publicstaticvoidmain(String[]args){Duckduck=newMallardDuck();duck.performFly();duck.performQuack();}}在Duck.java子类型MallardDuck.java的构造方法中,直接实例化行为类型,在编译的时侯便指定具体行为类型。当然,我们可以:1)我们可以通过工厂模式或其它模式进一步解藕(可参考后续模式讲解)
本文标题:java_策略模式
链接地址:https://www.777doc.com/doc-794292 .html