博客
关于我
【设计模式】策略模式
阅读量:427 次
发布时间:2019-03-06

本文共 8980 字,大约阅读时间需要 29 分钟。

一、前言

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

二、策略模式

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

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

三、示例说明

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

  说明: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();}
View Code

  MallardDuck类  

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

  RedheadDuck类 

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

  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() {            }}
View Code

  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();    }}
View Code

  运行结果:  

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...");    }}
View Code

  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() {            }}
View Code

  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();    }}
View Code

  运行结果:  

quack...swim...I am a MallardDuckfly...------------------quack...swim...I am a RedheadDuckfly...------------------squack...swim...I am a RubberDuck

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

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

  3.3 v3.0

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

  1. 抽出鸭子飞行行为

  2. 抽出鸭子呱呱叫行为

  设计的类图如下图所示

  

  

  说明:对于飞行行为和呱呱叫行为都已经从鸭子对象本身独立出来了。这样,可以很轻易让鸭子对象有其他的行为。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();}
View Code

  MallardDuck类 

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

  RedheadDuck类  

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

  RubberDuck类  

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

  FlyBehavior类  

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

  FlyWithWings类  

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

  FlyNoWay类 

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

  QuackBehavior类 

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

  Quack类  

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

  Squeak类  

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

  MuteQuack类

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

  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("------------------");            }}
View Code

  运行结果: 

quack...fly with wingsswim...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");    }    }
View Code

  FlyRocketPower类 

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

  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("------------------");                }}
View Code

  运行结果  

quack...fly with wingsswim...I am a MallardDuck------------------fly with a rocketswim...I am a ModelDuck------------------

四、总结

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

 

转载地址:http://rgcyz.baihongyu.com/

你可能感兴趣的文章
windows环境下安装zookeeper(仅本地使用)
查看>>
Pytest学习(二十)- allure之@allure.step()、allure.attach的详细使用
查看>>
Docker学习(十三)- docker rm 命令详解
查看>>
移动端Web开发调试之Chrome远程调试(Remote Debugging)
查看>>
解决Eclipse左键无法查看maven第三方包的源代码,多图亲测可用【转】
查看>>
selenium获取Cookie操作
查看>>
selnium远程机上传图片遇到的坑
查看>>
idea如何编译maven项目
查看>>
Kali安装Docker
查看>>
IDEA中Git更新合并代码后,本地修改丢失
查看>>
Jmeter之模拟文件上传、下载接口操作
查看>>
uni-app 商场样式
查看>>
ServerSuperIO Designer IDE 发布,打造物联网通讯大脑,随心而联。附:C#驱动源代码。
查看>>
Java 持久化操作之 --XML
查看>>
日历JS代码
查看>>
程序员如何提高工作效率
查看>>
ExtJs学习笔记
查看>>
(转)在ASP.NET 中实现单点登录(利用Cache, 将用户信息保存在服务器缓存中)
查看>>
《Spring Boot 实战纪实》之如何攥写需求文档
查看>>
形象革命——穿搭
查看>>