• 策略(strategy)模式


    Head First一书中对于策略(strategy)模式的正式定义是:策略模式定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。

    为了介绍这个算法,书中讲了一个例子:

    在某个游戏中,有各种各样的鸭子,系统的内部设计使用了严格的OO技术,设计了一个鸭子(Duck)父类,所有的鸭子种类均继承于此父类。Joe设计的鸭子类如下:

    package com.first.strategy;
    
    public class Duck1 {
    
    	public Duck1()
    	{
    	}
    	//鸭子会游泳
    	public void swim()
    	{	
    	}
    	//鸭子会呱呱叫
    	public void quack()
    	{	
    	}
    	//鸭子会飞
    	public void fly()
    	{	
    	}
    	//描述不同的鸭子,子类继承时覆盖重写
    	public void display()
    	{	
    	}
    }
    

    然后让各种鸭子都继承此类,并且重写display()方法,例如:

    class DuckA extends Duck1
    {
    	public void display()
    	{	
    		System.out.println("I am DuckA!");
    	}
    }

    这样的设计缺点显而易见:新加入的鸭子种类“橡皮鸭子”也能飞,且也能呱呱叫(橡皮鸭子不能飞也不能呱呱叫),这是违背现实的。因为所有的子类均会继承来自父类的fly()方法和quack()方法,所有的子类均具备了fly()和quack(),使得不适合子类的行为也继承了父类的行为。

    这时,Joe又想到了继承,可以在子类橡皮鸭(RubberDuck)中覆盖父类的fly()和quack()方法,让其不能飞也不能呱呱叫。

    package com.first.strategy;
    
    public class RubberDuck extends Duck1{
    
    	@Override
    	public void quack() {
    		// TODO Auto-generated method stub
    		System.out.println("我不会呱呱叫,我会吱吱叫!");
    	}
    
    	@Override
    	public void fly() {
    		// TODO Auto-generated method stub
    		System.out.println("I can not fly!");
    	}
    }
    


    这时缺点同样清楚:代码重复太多,每个子类全部要覆盖,代码的可重用性太低。

    Joe想到了接口,即把Duck类中的fly和quack从父类中抽象出来,变成接口Flyable和Quackable,这样,只有会飞的鸭子子类才继承Flyable接口,只有会呱呱叫的鸭子继承Quackable接口。这也不是一个好主意,因为它的缺点同样显而易见:和上面重写覆盖类似,重复的代码太多,不易维护,要修改,子类全要修改。

    既然继承并不能很好的解决这个鸭子问题,那么我们便寻求变化。鸭子的行为在子类中不断变化,并且让所有的鸭子都具有这些行为是不恰当的。Flyable和Quackable接口的想法开始不错,但是,Java接口中的方法不具有实现,所有子类继承接口无法达到代码复用的效果。这就意味着,无论何时要修改某个行为,那么必须向下追踪所有定了这个行为的类(或子类)中去修改。这时一个设计原则应运而生,恰好解决此问题:

    设计原则1:找出应用中可能需要变化之处,把他们独立出来,不要把需要变化的代码和不变的代码混杂在一起。即:把会变化的的部分取出来封装起来,以便以后可以轻易的改动或扩充此部分,而不影响不会变化的部分。

    现在就把变化的部分从Duck类中分离,变化的部分就是fly()和quack(),因为这两者会随着鸭子的不同而改变。我们分离出来的两个方法建立两个类,一个是与fly相关,一个与quack相关,每一组类实现各自的动作或行为。例如一个类实现“呱呱叫”,另外一个类实现“吱吱叫”,还有一个类实现“安静不叫”(例如木头鸭子)。这是运用了第二条原则:

    设计原则2:针对接口编程,而不是实现。

    所有的和fly相关的行为,组装成一个接口,quack接口类似。并且分别定义各种与之相关的行为。

    package com.first.strategy;
    
    public interface FlyBehavior {
    
    	public void fly();
    	
    }
    
    package com.first.strategy;
    
    public interface QuackBehavior {
    
    	public void quack();
    }
    

    呱呱叫类:

    package com.first.strategy;
    
    public class Quack implements QuackBehavior{
    
    	@Override
    	public void quack() {
    		// TODO Auto-generated method stub
    		//呱呱
    		System.out.println("quack..quack");
    	}
    
    	
    }
    

    吱吱叫类:

    package com.first.strategy;
    
    public class Squeak implements QuackBehavior{
    
    	public void quack() {
    		// TODO Auto-generated method stub
    		//吱吱
    		System.out.println("squeak..squeak");
    	}
    }
    和fly相关的两个类:
    <pre class="java" name="code"><pre class="java" name="code">package com.first.strategy;
    
    public class FlyNoWay implements FlyBehavior{
    
    	@Override
    	public void fly() {
    		// TODO Auto-generated method stub
    		System.out.println("Can not fly!!");
    	}
    	
    
    }
    package com.first.strategy;
    
    public class FlyWithWings implements FlyBehavior{
    
    	@Override
    	public void fly() {
    		// TODO Auto-generated method stub
    		System.out.println("Flying with wings!");
    	}
    
    }
    

    
    
    
    

    这样一来,可以让飞行和呱呱叫的动作被其他的对象复用,因为这些行为被抽象出来,与鸭子类无关了。我们可以增加一些动作,既不会影响现有的行为类,也不会影响使用这些行为类的鸭子类。

    现在要整合鸭子的行为:

    1.在Duck中加入两个实例变量,分别是FlyBehavior的实例变量flybehavior和QuackBehavior的实例变量quackbehavior,为借口类型而不是具体的实现类型(为了使用多态)。同时将原Duck类中的fly()和quack()方法删除,加入两个相似的方法performFly()和performQuack()

    package com.first.strategy;
    
    public abstract class Duck {
    
    	FlyBehavior flybehavior;
    	QuackBehavior quackbehavior;
    	
    	public Duck()
    	{
    		
    	}
    	public abstract void display();
    	
    	public void performFly()
    	{
    		flybehavior.fly();
    	}
    	public void performQuack()
    	{
    		quackbehavior.quack();
    	}
    	public void swim()
    	{
    		System.out.println("All ducks can swim!");
    	}
    }
    

    2.Duck子类中对flybehavior和quackbehavior实例变量的设置:

    package com.first.strategy;
    
    public class MallardDuck extends Duck{
    	
    	public MallardDuck()
    	{
    		flybehavior = new FlyWithWings();
    		quackbehavior = new Squeak();
    	}
    	
    	public void display()
    	{
    		System.out.println("I am a mallardDuck!");
    	}
    }
    package com.first.strategy;
    
    public class RubberDuck extends Duck1{
    
    	@Override
    	public void quack() {
    		// TODO Auto-generated method stub
    		System.out.println("我不会呱呱叫,我会吱吱叫!");
    	}
    	@Override
    	public void fly() {
    		// TODO Auto-generated method stub
    		System.out.println("I can not fly!");
    	}
    }
    3.主测试用例
    package com.first.strategy;
    
    public class Client {
    
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    
    		Duck mallardduck = new MallardDuck();
    		
    		mallardduck.display();
    		
    		mallardduck.performFly();
    		
    		mallardduck.performQuack();
    	}
    
    }
    

    4.运行程序。

    动态设定行为

    1.在鸭子里很多动态的功能没有用到,很可惜,我们可以在Duck类中增加setter方法来设置鸭子的行为,而不是在构造器中实例化:

    package com.first.strategy;
    
    public abstract class Duck {
    
    	FlyBehavior flybehavior;
    	QuackBehavior quackbehavior;
    	
    	public Duck()
    	{
    		
    	}
    	public void setFlyBehavior(FlyBehavior flybehavior)
    	{
    		this.flybehavior = flybehavior;
    	}
    	
    	public void setQuackBehavior(QuackBehavior quackbehavior)
    	{
    		this.quackbehavior = quackbehavior;
    	}
    
    	public abstract void display();
    	
    	public void performFly()
    	{
    		flybehavior.fly();
    	}
    	public void performQuack()
    	{
    		quackbehavior.quack();
    	}
    	public void swim()
    	{
    		System.out.println("All ducks can swim!");
    	}
    }
    


    2.构造一个新的鸭子模型:模型鸭子(ModelDuck),这个鸭子开始不会飞。

    package com.first.strategy;
    
    public class ModelDuck extends Duck{
    	
    	public MallardDuck()
    	{
    		flybehavior = new FlyNoWay();//不会飞
    		quackbehavior = new Quack();
    	}
    	
    	public void display()
    	{
    		System.out.println("I am a ModelDuck!");
    	}
    }
    


    3.建立新的FlyBehavior类型

    package com.first.strategy;
    
    public class FlyRocketPowered implements FlyBehavior{
    
    	@Override
    	public void fly() {
    		// TODO Auto-generated method stub
    		System.out.println("I can fly with a rocket!");
    	}
    
    }
    


    4.主测试类:

    package com.first.strategy;
    
    public class Client {
    
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    
    		Duck modelduck = new ModelDuck();
    		modelduck.performFly();//开始不会飞
                      	modelduck.setFlyBehavior(new FlyRocketPowered());
    		modelduck.performFly();//现在会飞了
    	}
    
    }
    


     

    鸭子和FlyBehavior和QuackBehavior是“HAS—A”关系,两个类组合起来(composition),鸭子的行为不是继承来的,而是组合而来。这里是第三个设计原则:

    原则3:多用组合,少用继承。

    使用组合组建系统具有很大的弹性,不仅可将算法族封装成类,更可以在运行时动态的改变行为。


     

  • 相关阅读:
    div的显示和隐藏
    asp.net使用My97 Date Picker时设置默认起始时间为n年之前的今天
    2的次幂表示【递归算法训练】
    怎样用JS获取ASP.NET服务器控件的客户端ID
    九度oj 题目1034:寻找大富翁
    CSS + DIV 让页脚始终保持在页面底部
    简单的鼠标可拖动div 兼容IE/FF
    web的各种前端打印方法之CSS控制网页打印样式
    CSS控制print打印样式
    专为控制打印设计的CSS样式
  • 原文地址:https://www.cnblogs.com/sunp823/p/5601416.html
Copyright © 2020-2023  润新知