开发之路(设计模式一:策略模式)

并不是每个自称自己是个OO的人,他就一定能运用好OOP。

普通程序员写的东西好比一把普通钥匙,一把钥匙只能开一个门,而高级程序员就会“造”万能钥匙。
我以前所理解的“简单”就是一把钥匙开一把锁的模式,仅仅只是着眼于解决现在的问题,而设计模式的“复杂”在于它需要造出具有“通用性”的万能钥匙。而我以前写的代码就是前者,这个“简单”不是功能的简单,而是设计的简单。简单的设计意味着缺少灵活性,代码很钢硬,仅仅只是面向过程,一步操作紧挨着下一步。
要使代码可被反复使用,请用’设计模式’对你的代码进行设计.

模拟鸭子程序开始了。
首先来一张类图,这是鸭子程序原始状态
《开发之路(设计模式一:策略模式)》

而此时我们需要添加一个新功能:鸭子飞功能(吐槽:明明很多鸭子不会飞)
这很简单,难不倒我们“有OO思想”的人
《开发之路(设计模式一:策略模式)》

但这样的设计运用在下面拓展的程序上会变成什么呢?
《开发之路(设计模式一:策略模式)》

哇哦,很明显橡皮鸭子不能飞,而在此设计中使用继承(extends)就变得不合理了,
但是我们可以投机取巧一点,变成如下图
《开发之路(设计模式一:策略模式)》

这样小聪明的方法,恐怕我自己看的都不能直视了….
很明显对于继承在这个设计当中很几个致命的地方
1:各个鸭子子类继承父类时,使得代码在多个子类中重复
2:没有拓展性,对于改变会费劲
3:往往改变一点地方很多地方就都要跟着改变,找出其他鸭子类中不想要的改变,牵一发而动全身。

设计原则一:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起
注:当每次有新的需求的时候,就会使某方面发生变化,那么你就可以确定,这部分的代码就需要被抽出来,和其他稳定的代码有所区分,这样使得代码变化引起的不经意后果变少,系统变得更加富有弹性。
《开发之路(设计模式一:策略模式)》

设计原则二:针对接口编程,而不是针对实现编程。
注:从现在开始我们编写的代码要富有弹性,让其运行时能“动态”的改变飞行或者呱呱叫行为

进行一点小改变
《开发之路(设计模式一:策略模式)》
这里将飞行和叫的行为各自“抽象”出来成接口,这两个行为被分开成两个类,这两个类专门为提供某行为的实现提供接口。不同与之前设计:行为来自Duck父类中子类的具体实现,或是继承某个接口并由子类自行实现而来。这两种做法都依赖“实现”。而此时子类将使用接口所代表的行为,所以实际的“实现”不会被绑死在鸭子的子类中。(特定的具体行为编写在实现了FlyBehavior和QuackBehavior接口的实现类中)

这里放出具体行为设计
《开发之路(设计模式一:策略模式)》
注:此时飞行和叫的动作(接口)可以被其他对象复用,此时这两个行为和鸭子类(Duck)无关了,而且往后加入新的行为也不怕了大量修改代码了。

鸭子父类:

package Entity;

import Interface.FlyBehavior;
import Interface.QuackBehavior;

//鸭子父类
public class Duck {
    // 添加两个接口变量
    FlyBehavior flyBehavior;// 每只鸭子都会引用完成FlyBehavior接口的对象
    QuackBehavior quackBehavior;// 每只鸭子都会引用完成QuackBehavior接口的对象

    public FlyBehavior getFlyBehavior() {
        return flyBehavior;
    }

    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    public QuackBehavior getQuackBehavior() {
        return quackBehavior;
    }

    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }

    public Duck() {
    }

    public void swim() {
        System.out.println("鸭子游泳。。。。。。");
    }

    // 动作
    public void display() {
        System.out.println("每只鸭子都会动");
    }

    // 这些方法取代fly(),quack()
    // 鸭子对象不亲自处理呱呱呱叫行为,而是委托给quackBehavior引用对象
    public void performQuack() {        
        quackBehavior.quack();
    }

    // 鸭子对象不亲自处理飞行为,而是委托给flyBehavior引用对象
    public void performFly() {
        flyBehavior.fly();
    };

}

飞行和叫动作接口

package Interface;
//飞行接口
public interface FlyBehavior {
    public void fly();
}
package Interface;
//鸭子叫接口
public interface QuackBehavior {
    public void quack();
}

//绿头鸭和橡皮鸭类

package Entity;

import Implements.FlyWithWings;
import Implements.Quack;
//绿头鸭子类
public class MallardDuck extends Duck {

    @Override
    public void display() {
        System.out.println("绿头鸭子");
    }

    /* 当MallardDuck被实例化时,
     * 构造器会把继承来的quackBehavior,flyBehavior 
     * 实例变量初始化成Quack,FlyWithWings的类型
     * Quack,FlyWithWings分别是接口的实现类
     * */
    public MallardDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }

}
package Entity;

import Implements.FlyNoWay;
import Implements.Squeak;

public class RubberDuck extends Duck {
//橡皮鸭类
    @Override
    public void display() {
        System.out.println("我是一只橡皮鸭");
    }

    public RubberDuck() {
        quackBehavior = new Squeak();
        flyBehavior = new FlyNoWay();
    }

}

各个行为的具体实现类,都实现了接口

package Implements;

import Interface.FlyBehavior;

public class FlyNoWay implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("不会飞");
    }

}
package Implements;

import Interface.FlyBehavior;

public class FlyWithWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("鸭子飞起来了。。。。。。");
    }

}
package Implements;

import Interface.QuackBehavior;

public class MuteQuack implements QuackBehavior {

    @Override
    public void quack() {
        System.out.println("不会叫");
    }
}
package Implements;

import Interface.QuackBehavior;

public class Quack implements QuackBehavior {

    @Override
    public void quack() {
        System.out.println("鸭子呱呱呱叫。。。。。。");
    }
}
package Implements;

import Interface.QuackBehavior;

public class Squeak implements QuackBehavior {

    @Override
    public void quack() {
        System.out.println("鸭子吱吱吱叫。。。。。。");
        
    }

}

测试类

package TestMain;

import Entity.Duck;
import Entity.MallardDuck;
import Entity.RubberDuck;

public class TestMain {
    public static void main(String[] args) {
        //这里使用多态,让Duck知道我们要创建一只绿头鸭
        Duck d = new MallardDuck();
        //重载方法
        d.display();
        d.performQuack();
        d.performFly();        
        System.out.print("\n");
        
        Duck a=new RubberDuck();
        a.display();
        a.performQuack();
        a.performFly();

    }
}

效果:
《开发之路(设计模式一:策略模式)》

设计原则三:多用组合,少用继承。
注:使用组合建立系统具有很大的弹性,不仅可将算法封装成类,更可以“动态的改变行为”,只要组合对象符合正确的接口标准即可。

此时我刚刚写的例子基于一个策略模式思想写的。当然你可能会问:“用这个设计模式到底有什么用呢?这样功能我也能实现了,干嘛要搞的那么复杂?”。对此我不能用专业的思想去回答这样的疑问,我只能说随着自己学习的不断深入往往很多以前自认为对的东西,其实是不正确的。

策略模式要点:
1:良好的OO设计必须具备可复用性、可扩充性、可维护性三个特征
2:模式不是代码,而是针对设计问题的通用解决方案。
3:大多数的模式都允许系统局部改变独立于其他部分。
4:记着把系统中会改变的部分抽出来封装
5:模式会让开发人员之间有共同的语言,而且会让自己少走很多坑

这里我想说一点,很多人都觉得设计模式跟算法一样都是可有可无的东西,我觉得用设计模式是着眼于未来,可以提高系统的扩展性,减少重复劳动,虽然有的时候可能会增加工作量,但比起后期无止境的维护,这点反而不算什么,而设计模式是不会提高系统运行速度的,但我认为设计模式非常重要,包括带我的前辈程序员都教导我脑中要有模式概念,当然这个要因人而异。

感谢你看到这里,策略模式部分结束,本人文笔随便,若有不足或错误之处望给予指点,90度弯腰~很快我会发布下一个设计模式内容,生命不息,编程不止!

参考书籍:《Head First 设计模式》

    原文作者:设计模式
    原文地址: https://segmentfault.com/a/1190000010639305
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞