模式的秘密-策略模式

策略模式

策略模式将可变的部分从程序中抽象分离成算法接口,在该接口下分别封装一系列算法实现,

并使得他们可以相互替换,从而导致客户端程序独立于算法的改变

 

实例:

假如有一个鸭子类:鸭子有叫的功能,有展示功能,另外需要给鸭子加一个飞行能力,让鸭子飞起来,但是对于不同的鸭子有不同的飞行能力,

比如有的展翅高飞,有的不会飞,有的飞向太空。

方案一:继承:

在父类中提供实现方法,子类通过继承获得父类中的飞行行为。

优点:简单易用,已有应用可以快速添加飞行能力。

缺点:不具有灵活性,对未来变更支持性差。假如子类鸭子有跟父类不一样的飞行方法,需要在子类中复写飞行的方法以提供新的

飞行行为,这很容易出差造成错误(粗心的程序员忘记复写),假如一只大黄鸭,是不具备飞行能力的,但是因为程序员忘记复写飞行,导致该大黄鸭能飞。

方案二:抽象方法

在父类中提供抽象方法,强迫子类实现自己的飞行行为。

优点:足够灵活,小伙伴再也不会忘记复写代码了,大黄鸭也不会飞到天上吓你了。

缺点:累死小伙伴,每个子类都要实现一遍代码,即使是相同的行为也不例外。

  代码重复,却没有复用代码。万一再有一个bug,哎妈呀,不敢想

继承是重用代码的利器,但是继承并不总是最好的工具。

 

解决思想:复合优先于继承,多用组合,少用继承。

组合:在类中增加一个私有域,引用另一个已有的类的实例,通过调用引用实例的方法从而获得新的功能,

这种设计被称为组合(复用)。

就像赛车通过换轮胎,获得超凡奔跑能力。

 

方案三:组合

将飞行行为抽象为接口,在父类中持有该接口,并由该接口代理飞行行为。

public interface  FlyingStragety
{
     void performFly();      
}

父类:

private FlyingStragety  flyingStragety;
public void fly()
{
     flyingStragety.performFly();
}

优点:足够灵活,复用代码,更易于维护。

 

策略模式的设计原则:

1,找出应用中需要变化的部分,把他们独立出来,不要和那些不需要的代码混在一起(可复用的功能独立出来,供不同的子类重用,这也是为什么不采用抽象的原因

2,面向接口编程,而不是面向实现编程。

3,多用组合,少用继承。

策略模式的实现:

1,通过分离变化得出的策略接口Stratege。(策略接口,实现鸭子的飞行行为)。

2,继承策略模式接口,实现Stratege的实现类(不同的飞行行为,分别实现实现类)。

3,策略模式的实现:客户程序有一个"Stratege接口对象"。(在父类抽象类中实现即可)

4,在客户程序中选择/组装正确的Stratege实现。(在stratege对象的set方法里面,传入要选择的具体策略类实例)。

 

具体代码:

1,先定义一个飞行策略接口,该接口提供一个飞行方法。

package com.Stratege;

/*
 * 策略接口,实现鸭子的飞行行为
 * */
public interface FlyingStragety {

    void performFly();
}

2,对于飞行接口,分别提供几种飞行实现类,来供客户程序选择使用哪个策略Stratege实现。

如:不会飞实现类:

package com.Stratege.impl;

import com.Stratege.FlyingStragety;
//不能飞行
public class FlyNoWay implements FlyingStragety {

    @Override
    public void performFly() {
        System.out.println("我不会飞行,有点羞涩");
    }
}

展翅高飞实现类:

package com.Stratege.impl;

import com.Stratege.FlyingStragety;
//第一种飞行策略:展翅高飞
public class FlyWithMe implements FlyingStragety {

    @Override
    public void performFly() {
        System.out.println("振翅高飞");
    }
}

3,不同的飞行策略的接口和实现类已经实现了。

现在开始实现客户端类,假如有三种鸟:绿头鸭,红头鸭,橡胶鸭,小黄鸭。其中绿头鸟和红头鸭会飞,

橡胶鸭和小黄鸭不会飞,橡皮鸭子叫声有点沙哑:嘎~嘎~嘎~。

4,定义一个会叫,能展示的鸭子,不同的鸭子展示方式不一样,这里通过一个抽象方法实现,并且采用策略模式提供飞行功能。

策略模式飞行:1,提供策略模式飞行接口对象,2,提供该接口对象的set方法。

       3,提供飞行方法,方法里面由策略模式接口对象调用自身的performFly。这样实现了,不同的子类,调用飞行方法,不需要修改飞行方法,

       只需要为给策略模式飞行接口对象实例化不同的策略模式实现类。

package com.Stratege;

/*
 * 超类,所有的鸭子都要继承此类
 * 抽象了鸭子的行为:显示和叫
 * */
public abstract class Duck {
    
    //鸭子发出叫声
    //通用行为,由超类实现
    public void quack()
    {
        System.out.println("嘎嘎嘎");
    }
    
    //显示鸭子的外观,鸭子外观各不相同,声明为abstract,由子类实现
    public abstract void display();
    
    private FlyingStragety flyingStragety;
    
    public void setFlyingStragety(FlyingStragety flyingStragety) {
        this.flyingStragety = flyingStragety;
    }
    //调用策略接口实现飞行
    public void fly()
    {
        this.flyingStragety.performFly();
    }

}

5,不同的鸭子子类分别实例化:在构造方法里面,调用父类的setStragety方法,方法的参数传递对应的实现子类实例,从而实现往子类中注入不同的策略实例,

绿鸭:

package com.Stratege;

import com.Stratege.impl.FlyWithMe;

public class MallardDuck extends Duck {

    public MallardDuck()
    {
        super();
        //给红头鸭提供了一个翅膀飞行的能力
        super.setFlyingStragety(new FlyWithMe());
    }

    @Override
    public void display() {
        System.out.println("我的头是绿色的");
    }
}

红头鸭:

package com.Stratege;

import com.Stratege.impl.FlyWithMe;

public class RedheadDuck extends Duck {
    
    public RedheadDuck()
    {
        super();
        //给红头鸭提供了一个翅膀飞行的能力
        super.setFlyingStragety(new FlyWithMe());
    }

    @Override
    public void display() {
        System.out.println("我的头是红色的");
    }

}

橡胶鸭:因为叫声不一样,重写了叫声方法。

package com.Stratege;

import com.Stratege.impl.FlyNoWay;

public class RubberDuck extends Duck{

    public RubberDuck()
    {
        super();
        //给红头鸭提供了一个翅膀飞行的能力
        super.setFlyingStragety(new FlyNoWay());
    }

    @Override
    public void display() {
        System.out.println("我全身发黄,嘴巴很红");
    }
    
    public void quack()
    {
        System.out.println("嘎~嘎~嘎~");
    }
}

大黄鸭:不会飞。

package com.Stratege;

import com.Stratege.impl.FlyNoWay;

public class BigYellow extends Duck {

    public BigYellow()
    {
        super();
        //给红头鸭提供了一个翅膀飞行的能力
        super.setFlyingStragety(new FlyNoWay());
    }

    @Override
    public void display() {
        System.out.println("我身体很大,全身黄黄");
    }
}

 

测试:

package com.Stratege;

public class DuckTest {

    public static void main(String[] args) {

        System.out.println("测试鸭子程序");
        System.out.println("-----------------");
        Duck duck=new MallardDuck();
        
        duck.display();
        duck.quack();
        duck.fly();
        
        System.out.println("-----------------");
        
        Duck duck2=new RedheadDuck();
        duck2.display();
        duck2.quack();
        duck2.fly();
        
        System.out.println("-----------------");
        
        Duck duck3=new RubberDuck();
        duck3.display();
        duck3.quack();
        duck3.fly();
        
        System.out.println("-----------------");
        
        Duck duck4=new BigYellow();
        duck4.display();
        duck4.quack();
        duck4.fly();        
        
    }

}

运行结果:

测试鸭子程序
-----------------
我的头是绿色的
嘎嘎嘎
振翅高飞
-----------------
我的头是红色的
嘎嘎嘎
振翅高飞
-----------------
我全身发黄,嘴巴很红
嘎~嘎~嘎~
我不会飞行,有点羞涩
-----------------
我身体很大,全身黄黄
嘎嘎嘎
我不会飞行,有点羞涩

 

总结:

策略模式的优点:

1,使用了组合模式,使得架构更加灵活。

2,富有弹性,可以较好的应对变化(开-闭原则)。

3,更好的代码复用性。(相对于继承。而且如果相同的策略,只需要实例化对应的策略模式类就行了)

4,消除大量的条件语句。(不同的子类,当对于不同的策略,只需要为策略接口对象,实例化对应的策略模式类就行了)

 

适用场景:

1,许多相关的类,仅仅是行为差异。

2,运行时选取不同的算法变体。

3,通过条件语句在多个分支中选取一。

 

posted @ 2018-04-27 00:51  美好的明天  阅读(432)  评论(0)    收藏  举报