zoukankan      html  css  js  c++  java
  • 设计模式之策略模式(Strategy Pattern)

    一.什么是策略模式(Strategy Pattern)?

    从字面上理解,策略模式就是应用了某种“策略”的设计模式,而这个“策略”就是:把变化的部分封装起来。

    其实这个理解有误,也是本文被反对一次的原因,例子没错,但对此模式的理解有偏差,修改内容已经追加在本文尾部,点我跳转>>

    二.举个例子

    假定现在我们需要用类来描述Dog

    首先,所有的Dog都有外形(比如Color),有行为(比如Run)

    于是我们很自然地定义了这样一个基类Dog:

    public abstract class Dog {
    	public abstract void display();//显示Dog的外形
    	public abstract void run();//定义Dog的Run行为
    }
    

    其它的Dog具体类全部继承自Dog基类就好了,很快,需求来了

    有一只Red Dog,它是宠物犬,跑得很慢,叫声也很小

    于是我们又定义了RedDog类:

    public class RedDog {
    	@override
    	public void display(){
    		System.out.println("this is a red dog.");
    	}
    	@override
    	public void run(){
    		System.out.println("it's running slowly.");
    	}
    }
    

    接着,来了一大批需求,Black Dog、Gray Dog、Pink Dog、BlackGrayDog、PinkGrayDog。。。一共100只Dog,除Color不同外,Run的速度也不同

    于是我们定义了100个类来表示100种不同类型的Dog,每个类中都实现了Run方法与Display方法。

    嗯~,看看我们的类图,没错,这个BigBang就是我们的类图了,好像很合理啊,有100种Dog当然会有100个类啊,而且具体Dog类当然要继承自Dog基类吧?嗯,好像是这样的,而且这样好像也没什么不好,至少现在看不出来有什么不妥。。。

    -------

    又一个新的需求来了,我们发现Dog不仅可以Run,还可以Bark

    自然而然地,我们想到了给Dog基类定义一个新行为Bark,然后在100个具体类中逐一重写了Bark方法。。。

    虽然过程有些麻烦,不过好在我们还是解决了问题,现在所有的Dog都可以Bark了

    -------

    这天,来了一只新Dog,它叫ToyDog,是玩具狗,不会Run也不能Bark,只是个玩偶

    于是我们让ToyDog继承了Dog基类,重写了Run方法和Bark方法(方法体为空,因为Toy不会Run也不会Bark)

    问题好像也被完美解决了,至少现在看来不存在什么问题

    -------

    很多天后,ToyDog工厂技术革新了,新产品被装上了电池,可以Bark了

    没关系,我们修改了ToyDog类,实现了Bark

    -------

    我们初始化了一只ToyDog,它欢快的BarkBarkBark,过了一会儿,电池没电了,ToyDog不能Bark了,但是我们的ToyDog类显示它还可以Bark。。。

    *******

    现在,终于出问题了吧,我们发现Dog问题中最难处理的部分就是Run、Bark这两个行为,一旦发生变化我们就需要修改具体Dog类,甚至是Dog基类,不仅需要花费大量的时间,而且所有具体Dog类中都实现了Run与Bark方法,显得很臃肿。还有最重要的问题,我们无法动态地修改Dog的行为,比如小Dog跑得慢叫声小,长大后跑得快叫声大,也无法应对上面提到的电池没电导致的行为变化问题。。。。这一系列的问题想向我们说明一点:这从一开始就是一个糟糕的设计。

    三.如何应用策略模式?

    策略模式要求把变化的部分封装起来,首先,我们要找到代码中频频发生变化的部分

    在上一个例子中,变化的部分是什么?

    1.Run行为

    2.Bark行为

    3.其它可能存在的行为

    ...

    下面我们把这些行为封装起来(以Run为例):

    package StrategyPattern;
    
    /**
     * @author ayqy
     * 定义Run接口
     *
     */
    public interface IRun {
    	public void run();//定义run行为
    }
    
    package StrategyPattern;
    
    /**
     * @author ayqy
     * 实现RunQuickly行为
     *
     */
    public class RunQuickly implements IRun{
    
    	@Override
    	public void run() {
    		System.out.println("it's running quicky.");
    	}
    
    }
    
    package StrategyPattern;
    
    /**
     * @author ayqy
     * 实现RunSlowly行为
     *
     */
    public class RunSlowly implements IRun{
    
    	@Override
    	public void run() {
    		System.out.println("it's running slowly.");
    	}
    
    }
    
    package StrategyPattern;
    
    /**
     * @author ayqy
     * 实现RunNoWay行为
     *
     */
    public class RunNoWay implements IRun{
    
    	@Override
    	public void run() {
    		System.out.println("it isn't able to run.");
    	}
    
    }
    

    Bark行为的封装与之类似

    封装好“变化”之后,我们的Dog基类也要做相应改变:

    package StrategyPattern;
    
    /**
     * @author ayqy
     * 定义Dog基类
     *
     */
    public abstract class Dog {
    	IBark bark;
    	IRun run;
    	
    	public abstract void display();//显示Dog的外形
    	
    	public IBark getBark() {
    		return bark;
    	}
    
    	public void setBark(IBark bark) {
    		this.bark = bark;
    	}
    
    	public IRun getRun() {
    		return run;
    	}
    
    	public void setRun(IRun run) {
    		this.run = run;
    	}
    }
    

    注意,我们只封装了容易发生变化的部分(Bark与Run),而没有封装Display方法(Dog的外形应该比较fixed吧),什么才是“变化的部分”?这需要我们仔细考虑,视具体情景而定

    现在来看看我们新的具体Dog类吧

    package StrategyPattern;
    
    /**
     * @author ayqy
     * 实现RedDog
     *
     */
    public class RedDog extends Dog{
    	
    	public RedDog()
    	{
    		//红狗是宠物狗
    		//跑得慢,叫声小
    		super.setRun(new RunSlowly());
    		super.setBark(new BarkQuietly());
    	}
    
    	@Override
    	public void display() {
    		System.out.println("this is a red dog.");
    	}
    }
    

    类被明显瘦身了吧,而且我们现在还能在运行时动态地修改Dog的行为,看到策略模式的威力了吧。

    四.效果示例

    编写一个测试类:

    package StrategyPattern;
    
    /**
     * @author ayqy
     * 测试策略模式<br/>
     * 策略模式,简单的说就是要求把“变化”封装起来,以隔离“变化”对其它部分的影响
     *
     */
    public class Test {
    
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		System.out.println("------- 创造一只Red Dog -------");
    		Dog redDog = new RedDog();
    		showDogInfo(redDog);
    		
    		System.out.println("
    ------- 创造一只Black Dog -------");
    		Dog blackDog = new BlackDog();
    		showDogInfo(blackDog);
    		
    		System.out.println("
    ------- 创造一只Toy Dog -------");
    		Dog toyDog = new ToyDog();
    		showDogInfo(toyDog);
    		
    		System.out.println("
    ------- 技术革新,现在Toy Dog可以小声叫了 -------");
    		toyDog.setBark(new BarkQuietly());
    		showDogInfo(toyDog);
    		
    		//上面的代码并不是最优的,只是为了说明策略模式
    		//一个很明显的问题是Dog redDog = new RedDog();
    		//我们在面向具体对象编码,而不是设计模式所提倡的面向接口编码
    		//可以定义一个Dog工厂来生产Dog对象以求模块之间更松的耦合
    	}
    	
    	/**
    	 * @param dog
    	 * 显示Dog相关信息
    	 */
    	private static void showDogInfo(Dog dog)
    	{
    		dog.display();
    		dog.run.run();
    		dog.bark.bark();
    	}
    
    }
    

    运行结果示例:

    五.总结

    策略模式的核心是要把频繁发生变化的部分封装起来,作用是把变化部分的影响隔离开,避免局部的变化对其它fixed部分造成影响,设计时可能需要更多的时间,但便于维护、复用与扩展,在本例中,Run、Bark行为都可以在新的类(如Pig)中复用;一旦行为发生变化我们只需要修改各个行为接口,最多再对Dog基类做简单修改就可以从容地应对变化了。

    六.修正

    上面的内容有些偏差,但勉强可以看,只是不太准确,下面作以准确的描述:

    策略模式的思想确实是封装,但这里的”策略“并不是指”把变化封装起来“,这是在反复读了几遍原文后发现的(《Head First 设计模式》第一章感觉有点不太好,第一遍理解偏了)

    这里的”策略“近似于算法

    策略模式不太容易理解(虽然笔者极力避免照搬原文,但万一再理解偏了就误导别人了。。),书上的准确定义是这样的:

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

    个人反复读过几遍之后,觉得原文想表达的东西是这样的:

    • 1.策略模式核心是对算法的封装(还有一种设计模式叫”模版方法模式“,也是对算法的封装,区别与联系放在里面介绍,点我跳转>>
    • 2.专注于实现算法(策略)的选择,支持运行时动态改变策略
    • 3.具体实现是把变化的部分找出来,定义为接口,每个接口对应一组算法,每一个都是一种策略
    • 4.同一接口下的算法是可以相互替换的
    • 5.算法是独立于客户代码的,也就是对算法封装的具体体现
  • 相关阅读:
    网络爬虫之Cookies解决
    高性能异步爬虫
    Python csv存储
    pandas ExcelWriter用法及代码示例
    pandas to_excel 用法详解
    pandas read_csv 与 to_csv 方法各参数详解
    pandas read_excel操作
    pandas DataFrame.groupby()所见的各种用法详解
    Pandas 中 DataFrame 基本函数整理
    Python 解析二维码 输出文本
  • 原文地址:https://www.cnblogs.com/ayqy/p/3956704.html
Copyright © 2011-2022 走看看