【设计模式】策略模式

简介:  笔者在看JDK源码的同时也穿插着看设计模式,之前有涉猎设计模式,但是没有进行总结和提炼,现在再读一遍设计模式,感觉受益匪浅,也特此进行记录。下面设计模式系列是以《Head First 设计模式》书为参考。有兴趣的读者可以购买,讲解得浅显易懂。

一、前言


  笔者在看JDK源码的同时也穿插着看设计模式,之前有涉猎设计模式,但是没有进行总结和提炼,现在再读一遍设计模式,感觉受益匪浅,也特此进行记录。下面设计模式系列是以《Head First 设计模式》书为参考。有兴趣的读者可以购买,讲解得浅显易懂。


二、策略模式


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


  乍一看,概念比较难懂和生涩,可以用另外一种方式说明,对于不同的策略,对象会有不同的行为,这些策略是独立的,并且可以替换彼此,下面通过实际的示例来帮助理解这一模式。


三、示例说明


  设计一个鸭子游戏,游戏中有各种鸭子,鸭子有各种行为,如呱呱叫、游泳等,同时不同的鸭子的外观不相同,这时候,很容易设计如下的类图。

download.png

说明:Duck为抽象类,表示鸭子类,所有的鸭子都有呱呱叫和游泳的行为,所以在Duck抽象类中实现,而display则是抽象的,因为不同的鸭子有不同的外观,具体子类给出实现。MallardDuck、RedheadDuck是呱呱叫(与Duck实现的相同),两者的外观不相同。RubberDuck是吱吱叫(需要重写Duck的quack方法),外观也不相同。


  3.1 v1.0


  根据类图,代码如下


  Duck类  

package com.hust.grid.leesf.strategy;
public abstract class Duck {
    public void quack() {
        System.out.println("quack...");
    }
    public void swim() {
        System.out.println("swim...");
    }
    public abstract void display();
}

MallardDuck类  

package com.hust.grid.leesf.strategy;
public class MallardDuck extends Duck {
    @Override
    public void display() {
        System.out.println("I am a MallardDuck");
    }
}

 RedheadDuck类 

package com.hust.grid.leesf.strategy;
public class RedheadDuck extends Duck {
    @Override
    public void display() {
        System.out.println("I am a RedheadDuck");
    }
}

 RubberDuck类

package com.hust.grid.leesf.strategy;
public class RubberDuck extends Duck {
    @Override
    public void display() {
        System.out.println("I am a RubberDuck");
    }
    @Override
    public void quack() {
        System.out.println("squeak...");
    }
    @Override
    public void fly() {
    }
}

  Main类(用作测试)  

package com.hust.grid.leesf.strategy;
public class Main {
    public static void main(String[] args) {
        Duck duck = new MallardDuck();
        duck.quack();
        duck.swim();
        duck.display();
        System.out.println("------------------");
        duck = new RedheadDuck();
        duck.quack();
        duck.swim();
        duck.display();
        System.out.println("------------------");
        duck = new RubberDuck();
        duck.quack();
        duck.swim();
        duck.display();
    }
}

运行结果:  

quack...
swim...
I am a MallardDuck
------------------
quack...
swim...
I am a RedheadDuck
------------------
squeak...
swim...
I am a RubberDuck

可以看到,上面的编写的代码可以运行良好,Rubber类重写了quack,为吱吱叫,但是,若此时提出一个新的需求,给某些鸭子添加飞的行为,因为有些可以飞(会飞的行为),一种不太好的解决办法是在Duck超类中添加fly方法,这样,每个子类都将会有fly方法,达到了代码复用的目的,但是,对与RubberDuck而言,它不会飞。则可以将RubberDuck类的fly覆盖,什么都不做


  3.2 v2.0


  v2.0,有些鸭子会飞(有飞的行为),修改代码如下


  Duck类  

package com.hust.grid.leesf.strategy;
public abstract class Duck {
    public void quack() {
        System.out.println("quack...");
    }
    public void swim() {
        System.out.println("swim...");
    }
    public abstract void display();
    public void fly() {
        System.out.println("fly...");
    }
}

 RubberDuck类 

package com.hust.grid.leesf.strategy;
public class RubberDuck extends Duck {
    @Override
    public void display() {
        System.out.println("I am a RubberDuck");
    }
    @Override
    public void quack() {
        System.out.println("squack...");
    }
    @Override
    public void fly() {
    }
}

Main类(用于测试) 

package com.hust.grid.leesf.strategy;
public class Main {
    public static void main(String[] args) {
        Duck duck = new MallardDuck();
        duck.quack();
        duck.swim();
        duck.display();
        duck.fly();
        System.out.println("------------------");
        duck = new RedheadDuck();
        duck.quack();
        duck.swim();
        duck.display();
        duck.fly();
        System.out.println("------------------");
        duck = new RubberDuck();
        duck.quack();
        duck.swim();
        duck.display();
        duck.fly();
    }
}

 运行结果:  

quack...
swim...
I am a MallardDuck
fly...
------------------
quack...
swim...
I am a RedheadDuck
fly...
------------------
squack...
swim...
I am a RubberDuck

RubberDuck重写了Duck类fly方法,使其什么都不做,这样可以认为RubberDuck不具有飞的行为。


  不足:当新增子类时,若子类也不具有飞的行为,则又要像RubberDuck一样进行处理,即每当新增子类时,都需要检查是否需要覆盖quack方法和fly方法,这显然是很麻烦的。有没有一种更好的办法,可以很好的解决这个问题呢?

  3.3 v3.0


  分离鸭子行为中变化的与不变化的部分(设计原则:找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起),这样,每次有新的需求时,都会使某方面的代码发生变化,可以把这部分的代码抽离出来,和其他的稳定代码有所区别。方便进行改动或者扩充,而不影响不需要变化的部分。变化的部分包括飞行行为和呱呱叫行为。


  1. 抽出鸭子飞行行为


  2. 抽出鸭子呱呱叫行为


  设计的类图如下图所示

download.png

download.png


 说明:对于飞行行为和呱呱叫行为都已经从鸭子对象本身独立出来了。这样,可以很轻易让鸭子对象有其他的行为。Duck对象中有FlyBehavior和QuackBehavior两个实例变量。

  

再次修改后的代码如下


  Duck类 

package com.hust.grid.leesf.strategy;
public abstract class Duck {
    private FlyBehavior flyBehavior;
    private QuackBehavior quackBehavior;
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }
    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }
    public void performQuack() {
        quackBehavior.quack();
    }
    public void performFly() {
        flyBehavior.fly();
    }
    public void swim() {
        System.out.println("swim...");
    }
    public abstract void display();
}

 MallardDuck类 

package com.hust.grid.leesf.strategy;
public class MallardDuck extends Duck {
    public void display() {
        System.out.println("I am a MallardDuck");
    }
}

RedheadDuck类  

package com.hust.grid.leesf.strategy;
public class RedheadDuck extends Duck {
    @Override
    public void display() {
        System.out.println("I am a RedheadDuck");
    }
}

 RubberDuck类  

package com.hust.grid.leesf.strategy;
public class RubberDuck extends Duck {
    @Override
    public void display() {
        System.out.println("I am a RubberDuck");
    }
}

FlyBehavior类

package com.hust.grid.leesf.strategy;
public interface FlyBehavior {
    void fly();
}

 FlyWithWings类  

package com.hust.grid.leesf.strategy;
public class FlyWithWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("fly with wings");
    }
}

 FlyNoWay类 

package com.hust.grid.leesf.strategy;
public class FlyNoWay implements FlyBehavior {
    @Override
    public void fly() {
    }
}

QuackBehavior类 

package com.hust.grid.leesf.strategy;
public interface QuackBehavior {
    void quack();
}

 Quack类 

package com.hust.grid.leesf.strategy;
public class Quack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("quack...");
    } 
}

 Squeak类  

package com.hust.grid.leesf.strategy;
public class Squeak implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("squeak...");
    }

 MuteQuack类

package com.hust.grid.leesf.strategy;
public class MuteQuack implements QuackBehavior {
    @Override 
    public void quack() {
    }
}

 Main类 

package com.hust.grid.leesf.strategy;
public class Main {
    public static void main(String[] args) {
        Duck duck = new MallardDuck();
        FlyBehavior flyBehavior = new FlyWithWings();
        duck.setFlyBehavior(flyBehavior);
        QuackBehavior quackBehavior = new Quack();
        duck.setQuackBehavior(quackBehavior);
        duck.performQuack();
        duck.performFly();
        duck.swim();
        duck.display();
        System.out.println("------------------");        
    }
}

 运行结果: 

quack...
fly with wings
swim...
I am a MallardDuck
------------------

可以看到,这个版本的鸭子游戏已经比较理想,此时,可以很容易的添加子类,并且不用改变之前稳定的代码,在运行过程中可以动态改变对象的行为(通过setFlyBehavior和setQuackBehavior实现)。如添加一个ModelDuck类,继承自Duck类,FlyRocketPower,实现FlyBehavior接口,代码如下


  ModelDuck类  

package com.hust.grid.leesf.strategy;
public class ModelDuck extends Duck {
    @Override
    public void display() {
        System.out.println("I am a ModelDuck");
    }    
}

 FlyRocketPower类 

package com.hust.grid.leesf.strategy;
public class FlyRocketPower implements FlyBehavior {
    @Override 
    public void fly() {
        System.out.println("fly with a rocket");
    }
}

 Main类(用作测试) 

package com.hust.grid.leesf.strategy;
public class Main {
    public static void main(String[] args) {
        Duck duck = new MallardDuck();
        FlyBehavior flyBehavior = new FlyWithWings();
        duck.setFlyBehavior(flyBehavior);
        QuackBehavior quackBehavior = new Quack();
        duck.setQuackBehavior(quackBehavior);
        duck.performQuack();
        duck.performFly();
        duck.swim();
        duck.display();
        System.out.println("------------------");    
        duck = new ModelDuck();
        flyBehavior = new FlyRocketPower();
        quackBehavior = new MuteQuack();
        duck.setFlyBehavior(flyBehavior);
        duck.setQuackBehavior(quackBehavior);
        duck.performQuack();
        duck.performFly();
        duck.swim();
        duck.display();
        System.out.println("------------------");    
    }
}

运行结果  

quack...
fly with wings
swim...
I am a MallardDuck
------------------
fly with a rocket
swim...
I am a ModelDuck
------------------

四、总结


  策略模式的应用很广泛,在游戏中有应用,如角色可以更换不同的武器,不同的武器有不同的效果,就可以使用策略模式来完成。所有源代码已经上传至github,欢迎fork,谢谢各位园友的观看~

目录
相关文章
|
24天前
|
设计模式 算法 Kotlin
Kotlin - 改良设计模式 - 策略模式
Kotlin - 改良设计模式 - 策略模式
45 4
|
3月前
|
设计模式 算法 测试技术
PHP中的设计模式:策略模式的应用与实践
在软件开发的浩瀚海洋中,设计模式如同灯塔,指引着开发者们避开重复造轮子的暗礁,驶向高效、可维护的代码彼岸。今天,我们将聚焦于PHP领域中的一种重要设计模式——策略模式,探讨其原理、应用及最佳实践,揭示如何通过策略模式赋予PHP应用灵活多变的业务逻辑处理能力,让代码之美在策略的变换中熠熠生辉。
|
15天前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
34 1
|
18天前
|
设计模式 前端开发 JavaScript
JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式
本文深入探讨了JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式,结合电商网站案例,展示了设计模式如何提升代码的可维护性、扩展性和可读性,强调了其在前端开发中的重要性。
22 2
|
1月前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
43 2
|
2月前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
本教程详细讲解Kotlin语法,适合深入学习。快速入门可参考“简洁”系列教程。本文通过游泳运动员的案例,介绍策略模式及其在Kotlin中的改良应用,利用高阶函数简化代码结构,提高灵活性。
35 3
|
2月前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
本教程详细讲解Kotlin语法,适合深入学习。快速入门可参考“简洁”系列教程。本文介绍策略模式在Kotlin中的应用,通过游泳运动员的例子,展示如何使用接口和高阶函数实现策略模式,使代码更简洁、灵活。
31 2
|
2月前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
64 3
|
2月前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
28 3
|
2月前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入解析与实践
【10月更文挑战第9天】 策略模式是一种行为设计模式,它允许在运行时选择算法的行为。在PHP开发中,通过使用策略模式,我们可以轻松切换算法或逻辑处理方式而无需修改现有代码结构。本文将深入探讨策略模式的定义、结构以及如何在PHP中实现该模式,并通过实际案例展示其应用价值和优势。
32 1