设计模式之策略模式

来源:互联网 发布:做淘宝客的步骤有哪些 编辑:程序博客网 时间:2024/06/06 06:30

一:背景

在软件开发的过程中,实现一种功能,可能存在几种算法,要根据不同的对象调用不同的算法。其中一种解决办法就是把不同的算法放到同一类里面,不同的对象通过根据情况调用不同的方法。这样做的话,可能新增一种算法,就要修改客户端代码,不灵活,后期的维护也相当麻烦。这个时候就需要用到策略模式。

二:策略模式定义

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

三:优缺点

优点

①:策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。

②:使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句里面,比使用继承的办法还要原始和落后。

缺点:

①:客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。

②:由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。

四:策略模式的结构


这个模式涉及到三个角色:

  ●  环境(Context)角色:持有一个Strategy的引用。

  ●  抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。

  ●  具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

五:使用场景举例

有一个鸭子游戏,要求出现各种鸭子,一边游泳,一边呱呱叫,有的会呱呱叫,有的会吱吱叫。

具体实现:

①:因为有的鸭子是呱呱叫,有的是吱吱叫,所以要把这部分行为拿出来,做一个单独的接口,来定义鸭子叫行为

public interface QuackBehavior {public abstract void quack() ;}
会呱呱叫的行为,继承该接口

public class Quack implements QuackBehavior {@Overridepublic void quack() {// TODO Auto-generated method stubSystem.out.println("我是呱呱叫");}}
会吱吱叫的行为,同样继承该接口

public class MuteQuack implements QuackBehavior{@Overridepublic void quack() {// TODO Auto-generated method stubSystem.out.println("我是吱吱叫");}}

②:我们一定一个鸭子的超类,存放鸭子的公共属性

public abstract class Duck {private QuackBehavior mQuackBehavior;/** * 用来设定鸭子的叫的行为 * @param quackBehavior */public void setmFlyBehavior(QuackBehavior quackBehavior) {this.mQuackBehavior = quackBehavior;}public void performquack(){mQuackBehavior.quack();}public abstract void display();public void swim(){System.out.println("我在游泳");}}


不同的鸭子有不同的外表,有的是绿头鸭,该绿头鸭继承自Duck

public class MallardDuck extends Duck{public MallardDuck() {// TODO Auto-generated constructor stub}@Overridepublic void display() {// TODO Auto-generated method stubSystem.out.println("我是绿头鸭!");}}
红头鸭也继承自Duck

public class RedlDuck extends Duck{ public RedlDuck() {// TODO Auto-generated constructor stub}@Overridepublic void display() {// TODO Auto-generated method stubSystem.out.println("我是红头鸭");}}
③:最后让我们来模拟鸭子的行为

public class DuckSimulator {public static void main(String[] args){Duck mallardDuck = new MallardDuck();QuackBehavior fly = new MuteQuack();mallardDuck.setmFlyBehavior(fly);mallardDuck.display();mallardDuck.swim();mallardDuck.performquack();Duck redDuck = new RedlDuck();QuackBehavior noFly = new Quack();redDuck.setmFlyBehavior(noFly);redDuck.display();redDuck.swim();redDuck.performquack();}}
输出的结果是:
我是绿头鸭!我在游泳我是吱吱叫我是红头鸭我在游泳我是呱呱叫
六:策略模式的设计原则

①:多组合,少继承

②:开闭原则(Open-Closed Principle,缩写为OCP)
一个软件实体应当对扩展(例如对抽象层的扩展)开放,对修改(例如对抽象层的修改)关闭。即在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展。
开闭原则的关键,在于抽象。策略模式,是开闭原则的一个极好的应用范例。

③:里氏替换原则(Liskov Substitution Principle,缩写为LSP)
一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且它根本不能察觉到基类对象和子类对象的区别。比如,假设有两个类,一个是Base类,一个是Derived类,并且Derived类是Base类的子类。那么一个方法如果可以接受一个基类对象b的话:method1(Base b),那么它必然可以接受一个子类对象d,也即可以有method1(d)。反之,则不一定成立
里氏替换原则讲的是基类与子类的关系。只有当这种关系存在时,里氏替换关系才存在,反之则不存在。
策略模式之所以可行的基础便是里氏替换原则:策略模式要求所有的策略对象都是可以互换的,因此它们都必须是一个抽象策略角色的子类。在客户端则仅知道抽象策略角色类型,虽然变量的真实类型可以是任何一个具体策略角色的实例






0 0
原创粉丝点击