设计模式之策略模式(Strategy Pattern)引子设计原则1提取鸭子的的行为设计原则2实现鸭子的行为整合实现我们设计的鸭子类策略模式总结

策略模式,是我们接触到的第一个设计模式,也是较容易理解的一个模式。 我们可以给它下一个定义: ** 定义了算法族,分别封装起来,让它们之间可以互相转换,此模式让算法的独立于使用算法的客户。** 维基百科上的定义是:** a software design pattern that enables an algorithm's behavior to be selected at runtime. ** 维基百科上的强调了算法行为是在运行时决定的,这正是策略模式很关键的一点。

引子

假设我们现在要设计一个鸭子类Duck类,然后让不同的鸭子继承于它。我们把目光聚焦到鸭子的行为上。如果我们要给鸭子增加一个行为“fly”,第一个想法,在抽象类duck里添加一个fly方法就可,其余鸭子继承实现这个方法。 但是这就出现了一个问题,并不是所有鸭子都会飞,我们反而让一些本不具备这个fly行为的鸭子也具有该行为。那怎么办呢? 利用继承来提供鸭子的行为,会导致下面这些后果:

  • 代码在多个子类中重复,如果两类不同鸭子需要同一种fly行为,我们就要在两个类里分别覆盖两次,这样万一维护起来是非常困难的
  • 很难知道所有鸭子的全部行为
  • 运行时的行为不容易改变
  • 改变会一发动全身,造成其他鸭子不想要改变

设计原则1

软件开发中,我们常常需要遵守的设计原则是: ** 把可能需要变化的地方独立出来,不要和那些不需要变化的代码混在一起 ** 这样代码变化引起的不经意后果变少,系统变得更有弹性 实际就是尽量让系统中某部分的改变不影响其他部分的变化。

提取鸭子的的行为

根据设计原则,鸭子飞行的行为会发生变化,所以我们需要将fly行为单独提取出来。同理,我们提取出两个鸭子可能变化的行为fly和quack鸭叫。用两组类分别代表fly和quack行为。

设计原则2

那么我们如何那两组鸭子行为的类呢?这里引出第二个我们提出的设计原则: ** 面对接口编程,而不是面对实现编程 ** 这样就可以实现在运行时改变鸭子的行为。 我们不会直接指定特定的行为给鸭子。而是声明两个接口FlyBehavior和QuackBehavior。我们制造的其他一系列的类专门来实现FlyBehavior和QuackBehavior,这组就成为行为类,或者算法类。 用行为类来实现接口而不是利用duck类来实现。

Paste_Image.png

实现鸭子的行为

根据设计原则2,可以让飞行和鸭叫行为的动作被其他对象复用,因为这些为行为已经与鸭子类无关了。 而且当我们新增一些行为的时候,不会影响到既有的行为类,也不会影响鸭子类。太棒了!

Paste_Image.png

** 很多同学都觉得这里用类来代表行为是不是觉得很奇怪。在大家默认里,类应该是代表某种东西的,类应该拥有状态与行为。这里,我们需要纠正这个观点,一个行为也可以具有各种属性和函数。类不仅仅是用来代表东西事物的。 **

整合实现我们设计的鸭子类

首先,在duck类中加入两个实例变量,分别声明为两个接口的类型,每个鸭子对象都会动态的设置这些变量以便在运行时引用正确的行为类型

Paste_Image.png

duck类的实现

package strategyPattern;

//抽象的Duck类,所有鸭子都继承
public abstract class Duck {
    
    //为行为接口类型声明两个引用变量,所有鸭子类都继承它们
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

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

    public Duck() {
        
    }
    
    public abstract void display();
    
    public void performFly() {
        flyBehavior.fly();  //委托给fly行为类实现
    }
    
    public void performQuack() {
        quackBehavior.quack();  //委托给quack行为类实现
    }
    
    //swim是所有鸭子都共同拥有的方法,所以可以直接在在duck类中实现
    public void swim() {
        System.out.println("All ducks float, even decoys!");
    }
}

FlyBehavior接口的实现:

package strategyPattern;

//所有飞行行为类必须实现的接口
public interface FlyBehavior {
    public void fly();
}

两个fly行为实现类,继承至FlyBehavior接口

package strategyPattern;

public class FlyWithWings implements FlyBehavior {

    @Override
    public void fly() {
        // TODO Auto-generated method stub
        System.out.println("I'm flying with wings!");
    }

}
package strategyPattern;

public class FlyNoWay implements FlyBehavior {

    @Override
    public void fly() {
        // TODO Auto-generated method stub
        System.out.println("I can't fly!");
    }

}

Quack接口及其三个行为实现类:

package strategyPattern;

public class Quack implements QuackBehavior {

    @Override
    public void quack() {
        // TODO Auto-generated method stub
        System.out.println("Quack");
    }

}
package strategyPattern;

public class MuteQuack implements QuackBehavior {

    @Override
    public void quack() {
        // TODO Auto-generated method stub
        System.out.println("<<silence>>");
    }

}
package strategyPattern;

public class Squeak implements QuackBehavior {

    @Override
    public void quack() {
        // TODO Auto-generated method stub
        System.out.println("Squeak");
    }

}
package strategyPattern;

public interface QuackBehavior {
    public void quack();
}

一个MallardDuck类继承至Duck类:

package strategyPattern;

public class MallardDuck extends Duck {

    public MallardDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }
    
    @Override
    public void display() {
        // TODO Auto-generated method stub
        System.out.println("I'm a real Mallard duck!");
    }

}

测试类

package strategyPattern;

public class MiniDuckSimulator {
    public static void main(String[] args) {
        Duck mallard = new MallardDuck();
        //这里调用mallardduck继承来的performFly方法,进而委托给该对象的quackBehavior对象处理
        //也就是最后是调用了继承来的quackBehavior引用对象的quack()方法
        mallard.performFly();
        mallard.performQuack();

    }
}

运行结果:

Paste_Image.png

在这里为了实现动态的改变鸭子的行为,我们可以新建一个flyrocketPowered行为类,然后动态的改变其行为:

package strategyPattern;

public class FlyRocketPowered implements FlyBehavior {

    @Override
    public void fly() {
        // TODO Auto-generated method stub
        System.out.println("I'm flying with a rocket ");
    }

}
package strategyPattern;

public class MiniDuckSimulator {
    public static void main(String[] args) {
        Duck mallard = new MallardDuck();
        //这里调用mallardduck继承来的performFly方法,进而委托给该对象的quackBehavior对象处理
        //也就是最后是调用了继承来的quackBehavior引用对象的quack()方法
        mallard.performFly();
        mallard.performQuack();
        
        
        Duck model = new ModelDuck();
        model.performFly();
        model.setFlyBehavior(new FlyRocketPowered());
        model.performFly();
    }
}

运行结果:

Paste_Image.png

Paste_Image.png

每一个鸭子都有一个FlyBehavior和一个quackBehavior,好将飞行和鸭叫委托给他们代为处理。 当你将两个类结合起来使用时,如同本例,这就是组合composition。这种做法和继承不同的地方在于,鸭子的行为不是继承来的而是和适当行为对象那个组合来的。 设计原则3: ** 多用组合 少用继承 **

策略模式总结

三个设计原则:

  • 封装变化,分开变化与不变
  • 多用组合,少用继承
  • 面向接口编程,而不是面对实现编程

策略模式: ** 定义了算法族,分别封装起来,让它们之间可以互相转换,此模式让算法的独立于使用算法的客户。**

Paste_Image.png

实现策略模式,我们需要对行为或算法实现各自的接口,具体的实现交给继承自这些接口的行为类,不需要在我们的主类鸭子中实现。主类鸭子声明两个接口的引用的实例变量,并设计set方法,这样就能在运行时动态的改变行为。实现独立和复用。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏用户2442861的专栏

近一个月的面试总结 分类:JAVA

本文转载自:http://blog.csdn.net/pistolove/article/details/46753275

1252
来自专栏皮皮之路

【JDK1.8】Java 8源码阅读汇总

3947
来自专栏精讲JAVA

Gof设计模式之工厂模式(四)

定义 工厂模式是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 工厂模式...

1988
来自专栏算法channel

其他|c++几个容易混淆的点

希望时间的流逝不仅仅丰富我们的阅历,更重要的是通过提炼让我们得以升华,走向卓越。 1编译出错 一段时间没写c/c++的程序了,最近公司一个项目底层的核心算法是...

3395
来自专栏智能合约

PHP魔术方法之__set()和__get()

1693
来自专栏小詹同学

Python系列之零——从零说起!!!

2017年可谓是人工智能元年,要问哪个行业最火,詹小白不敢确定,但要问哪个编程语言最热门,好吧,詹小白还是不敢说太满。但是!至少从舆论Pytho...

37110
来自专栏lgp20151222

Java的常量接口思考,项目中的常量是放在接口里还是放在类里呢?

最近在看一本书 Java与模式,里面提了一句不建议使用常量接口,甚至举了个java源码的反例,

1371
来自专栏Android干货

自定义控件:数独游戏(二)

4047
来自专栏互联网技术栈

读《重构:改善既有代码的设计》

1044
来自专栏老马说编程

计算机程序的思维逻辑 (1)

程序大概是怎么回事 计算机就是个机器,这个机器主要由CPU、内存、硬盘和输入输出设备组成。计算机上跑着操作系统,如Windows或Linux,操作系统上运行着各...

21010

扫码关注云+社区

领取腾讯云代金券