• [head first 设计模式] 第一章 策略模式


    [head first 设计模式] 第一章 策略模式

    让我们先从一个简单的鸭子模拟器开始讲起。

    假设有个简单的鸭子模拟器,游戏中会出现各种鸭子,此系统的原始设计如下,设计了一个鸭子超类,并让各种鸭子继承此超类。

    1.jpg

    若此时我们有了一个新的需求,我们需要鸭子会飞,那么我们该如何修改代码呢?

    最初,我们想在基类上加上fly方法,使得所有子类鸭子都拥有相应的fly方法。但这样错误产生了,即使是本不该会飞的橡皮鸭子也拥有了fly方法。

    或许我们可以把橡皮鸭中的fly方法覆盖掉,但这样每次新加入的不会飞的新鸭子类型,难道都要额外覆盖一次fly方法吗?太麻烦了。

    利用继承来提供duck的行为,会导致运行时的行为不容易改变,且改变容易牵一发动全身。

    那么,利用接口如何?

    若经常需要更新产品,那么每次覆盖fly简直是噩梦。那么,我们将fly单独写成一个接口,只有会飞的鸭子实现这个接口如何?

    2.jpg

    但这样其实重复的代码会变得非常多,造成fly代码无法复用,每个会飞的鸭子都要实现fly方法。

    那么我们该如何解决这个问题?在使用设计模式之前,不妨先求索于OO原则!

    软件开发中,什么是永恒真理?

    唯一不变的是变化本身——约翰逊·斯宾塞

    现在我们已经知道了继承无法很好的解决问题,因为鸭子的行为在子类中不断改变,并且有的行为子类不应该拥有。使用接口初看挺不错的,但继承接口无法达到代码的复用。这意味着,无论合适你需要修改某个行为,你必须向下追踪并在每一个定义此行为的类中修改它。

    但还好,有一个OO设计原则正好适用于此种情况:

    找出系统中可能需要变化之处,把他们独立出来,不要和那些不变化的代码堆在一起。

    也就是把会变化的部分取出来,好让其他部分不会受此影响。

    把会变化的部分取出来并封装,以后可以轻易地改动或扩充此部分,而不影响其他不需要变化的部分。

    那么,现在是时候把鸭子的行为从Duck类中取出了。

    分开变化和不会变化的部分

    目前而言,除了fly()和quack()以外,duck类其他部分看起来不怎么变动,所以我们仅做些小改变。

    为此,我们准备建立两组类,一个是和fly相关的,另一个和quack相关的,每一组类都实现各自的动作。

    3.jpg

    设计鸭子的行为

    如何设计

    那组实现飞行和叫声的类呢?我们希望一切能有弹性,并且能够将行为指定到鸭子的实例。并且可以让鸭子的行为动态的改变。

    有了这些目标要实现,我们看第二个设计原则

    针对接口编程,而不是针对实现编程。

    从现在开始,鸭子的行为将被放在分开的类中,此类专门提供某行为接口的实现。这样,鸭子类就不再需要知道行为的具体细节。

    这次鸭子类不会负责实现flying和quacking接口,而是由我们制造一组其他类专门实现flybehavior和quackbeavior,这就称为行为类,由行为类而不是duck类来实现行为接口。

    这种做法和以往不同,以往是行为来自于duck超类的具体实现,或是继承某个接口并由子类自行实现而来。这两种方法都是依赖于实现,没法变更行为。

    在我们的新设计中,鸭子的子类将使用接口所表示的行为,所以具体的实现不会被绑定在鸭子的子类中。

    4.jpg

    整合鸭子的行为

    关键在于,鸭子会将飞行和叫声行为委托给其他对象处理。而不是由自己定义。

    在Duck类中加入flyBehavior和quackBehavior变量,声明为接口类型。每个鸭子对象动态设置这些变量以在运行时引用正确的行为类型。

    代码如下

    public interface FlyBehavior {
        public void fly();
    }
    
    public interface QuackBehavior {
        public void quack();
    }
    
    public class FlyWithWings implements FlyBehavior{
        @Override
        public void fly() {
            System.out.println("I'm flying");
        }
    }
    
    public class FlyNoWay implements FlyBehavior{
        @Override
        public void fly() {
            System.out.println("I can't fly");
        }
    }
    
    public class Quack implements QuackBehavior{
        @Override
        public void quack() {
            System.out.println("quack!");
        }
    }
    
    public class MuteQuack implements QuackBehavior{
        @Override
        public void quack() {
            System.out.println("<<silence>>");
        }
    }
    
    public class Squeak implements QuackBehavior{
    
        @Override
        public void quack() {
            System.out.println("Squeak!");
        }
    }
    
    public abstract class Duck {
        protected FlyBehavior flyBehavior;
        protected  QuackBehavior quackBehavior;
        abstract void display();
        public void performFly()
        {
            flyBehavior.fly();
        }
        public void performQuack()
        {
            quackBehavior.quack();
        }
    }
    
    public class MallardDuck extends Duck{
        @Override
        public void display() {
            System.out.println("I'm a real mallard duck");
        }
        public MallardDuck(){
            flyBehavior = new FlyWithWings();
            quackBehavior  = new Quack();
        }
    }
    
    public class MiniDuckSimulator {
        public static void main(String[] args) {
            Duck mallardDuck = new MallardDuck();
            mallardDuck.performFly();
            mallardDuck.performQuack();
        }
    }
    

    动态设定行为

    在鸭子子类中为两个behavior加入set方法,而不是在构造器中进行实例化。有了这个,我们就能在运行时随时改变鸭子的行为。

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

    整体设计

    现在我们来看看整体结构

    5.jpg

    我们不再把鸭子的行为说成是行为,而是一族算法。算法代表鸭子能做的事情。在本例中,我们鸭子的行为是组合来的,而不是继承来的。

    我们得到第三个OO设计原则

    多用组合,少用继承

    学习完以上部分,我们正式定义策略模式

    6.jpg

  • 相关阅读:
    @WebFilter注解
    Value '0000-00-00' can not be represented as java.sql.Date解决办法
    项目配置 xml文件时 报错提示(The reference to entity "useSSL" must end with the ';' delimiter.)
    eclipse中取消自动生成的TODO Auto-generated method stub
    eclipse中把选中的代码全部变成大写或者小写的快捷键
    在浏览器访问Tomcat的时候报错java.lang.IllegalArgumentException: Control character in cookie value or attribute.
    Tomcat报错,内存溢出的错误Exception in thread "http-bio-8080-exec-13" java.lang.OutOfMemoryError: PermGen space
    java.io.Serializable的作用
    idea快捷键总结
    MYSQL分页limit速度太慢优化方法
  • 原文地址:https://www.cnblogs.com/alex101/p/14022379.html
Copyright © 2020-2023  润新知