zoukankan      html  css  js  c++  java
  • 策略(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:多用组合,少用继承。

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


     

  • 相关阅读:
    Window 窗口类
    使用 Bolt 实现 GridView 表格控件
    lua的table库
    Windows编程总结之 DLL
    lua 打印 table 拷贝table
    使用 xlue 实现简单 listbox 控件
    使用 xlue 实现 tips
    extern “C”
    COleVariant如何转换为int double string cstring
    原来WIN32 API也有GetOpenFileName函数
  • 原文地址:https://www.cnblogs.com/sunp823/p/5601416.html
Copyright © 2011-2022 走看看