策略模式原理及案例分析

原创 2016年08月30日 15:12:14

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

 

  这个概念毕竟是对这个模式的高度总结,我们可以先不必了解其含义,看完下面内容后,大家可以回头再揣摩一下定义的奥妙之处。

  

  我先来举一个生活中的实例:

  如果现在我需要写一个关于飞机行为的类,暂且描述飞机的三个行为动作:

  1.可以飞;

  2.有两个机翼;

  3.客运功能。

 

  我们会想到建一个飞机类Plane,之后建各种飞机子类来继承父类就好了。但是如果只是单纯的继承父类,飞机子类真的还能适用吗?对于可以飞和有两个机翼基本上飞机都可以满足,但是对于飞机的功能很明显是不可以的,比如说侦察机是没有客运功能的,战斗机也是没有客运功能的。

   

  解决方案:

  1.可以让子类来重写父类的功能方法(这种方法很明显是不可取的,如果说飞机种类很多,你不得不为每一个种类的飞机都要重写这样的一个方法,很重要的一点是,对于同样有战斗功能的战斗机和轰炸机,你却不得不重写两遍一模一样的功能函数,如果有10个、20个、100个相同功能的不同子类,你也要硬着头皮写下去吗?)

 

  2.可能大家会觉得,或许一开始父类Plane的类结构或许就已经存在问题,但是如果去掉这一个行为,那么每个子类还是要去添加这样一个方法,这样就回到了解决方案1的弊端上去了。更何况,飞机的功能原本就是每个飞机要有的基本属性,抽象到父类从逻辑上讲完全行得通。

 

  3.策略模式登场!解决方案2其实已经有点接近了,不过不是去掉这一个方法,而是要将此方法抽象出来,不表示具体某一功能。

 

  我们结合代码一起探讨。首先创建一个父类Plane。

public class Plane {  
    Function function;  
    public void fly(){  
        System.out.println("can fly!");  
    }  
    public void appearance(){  
        System.out.println("has two wings");  
    }  
    public void performFunction(){  
        function.whatFunction();  
    }  
}


      注意:在这个父类里,我们将功能Function抽象成了一个接口类,来表示功能这一行为,把接口类作为Plane类的一个变量。(通常我更喜欢称之为行为类)


     Function类如下:     


public interface Function {  
    void whatFunction();  
}


    这个就是一个简单的行为类,具体的行为需要来实现这个接口,比如客运功能和侦查功能等等。


public class AircraftFunction implements Function {  
    public void whatFunction(){  
        System.out.println("can carry guest");  
    }  
}

public class InvestigationFunction implements Function{  
    public void whatFunction(){  
        System.out.println("can investigation enemy");  
    }  
}


     我们将所有的准备工作做好,现在就可以创建子类了。这里创建客运机和侦察机两个子类。在初始化时就赋予它们Function。


public class AircraftPlane extends Plane{  
    public AircraftPlane() {  
        this.function = new AircraftFunction();  
    }  
}


public class InvestigationPlane extends Plane{  
    public InvestigationPlane(){  
        this.function = new InvestigationFunction();  
    }  
}


     现在我们就只需要一个执行类,来查看执行结果就可以了。


public class PlaneSimulator {  
    public static void main(String[] args) {  
        Plane aircraft = new AircraftPlane();  
        System.out.println("======aircraft======");  
        aircraft.fly();  
        aircraft.appearance();  
        aircraft.performFunction();  
  
        Plane investigation = new InvestigationPlane();  
        System.out.println("======investigation======");  
        investigation.fly();  
        investigation.appearance();  
        investigation.performFunction();  
    }  
}


     最后结果输出为:


======aircraft======  
can fly!  
has two wings  
can carry guest  
======investigation======  
can fly!  
has two wings  
can investigation enemy

    为了使大家更清晰的了解整个结构,这里给出它们的关系类图




   (PS:源码在https://github.com/chnwangzhenjiang/designPattern/tree/master/strategyPattern)
 


      在这个类图中大家可以看到,飞机的功能已经完全作为一个接口类被抽象了出来,并重新组合进了Plane类中,同时,继承父类Plane的子类中,也各自组合了功能的接口类的具体实现类。


      从宏观上看,如果某一行为存在着共性的属性(只要是飞机都有其存在的功能),却又因“类“而异,对不同的具体子类会表现出不同的行为,那么这种情况下应该采用接口的形式来抽象出类的这一行为。这种方法可称之为:对接口的编程。


        而对于上述的第1种解决方案,则是要繁琐得在每一个子类都去具体实现功能,我们可以称此种方案:依靠实现编程。

 

      由此我们引申出:针对接口编程,不针对实现编程。这是设计模式里一个非常重要的原则。希望大家可以借助这个例子有所体会。

 

      在策略模式的解决方案中,对于行为类是采用了组合的形式添加到了父类中,而不是盲目的采用解决方案1中的子类对父类的一味继承、通过不断地重复操作来实现功能。由此引出设计模式的另一条重要原则:少用继承,多用组合。

 

      现在我们再看首段对策略模式的规范性定义:此处的算法族就是以接口Function为中心的飞机功能的实现类的集合。通过Function接口可以让Plane的子类来随意使用这个行为类的各种实现类。而对于这种变化,在Plane父类及它的子类中,却完全不知情,它们只需要引用这个接口类就可以。

 

      看到这里想必大家对策略模式已经有了一个比较深刻的认知。我们下面来闲看一点spring源码方面的知识点。

 

      在SpringMVC中,Validation接口常被用作校验实体类。而我们可以建立起UserValidation、ProductValidation来实现Validation接口,并由ValidationUtils来作为运行类。那么此时这整个的流程就可以看作是策略模式的应用。类图如下:




     这里先贴出Spring的源码:

 

     org.springframework.validation.ValidationUtils(节选)


public static void invokeValidator(Validator validator, Object obj, Errors errors, Object... validationHints) {  
        Assert.notNull(validator, "Validator must not be null");  
        Assert.notNull(errors, "Errors object must not be null");  
        if(logger.isDebugEnabled()) {  
            logger.debug("Invoking validator [" + validator + "]");  
        }  
  
        if(obj != null && !validator.supports(obj.getClass())) {  
            throw new IllegalArgumentException("Validator [" + validator.getClass() + "] does not support [" + obj.getClass() + "]");  
        } else {  
            if(!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {  
                ((SmartValidator)validator).validate(obj, errors, validationHints);  
            } else {  
                validator.validate(obj, errors);  
            }  
  
            if(logger.isDebugEnabled()) {  
                if(errors.hasErrors()) {  
                    logger.debug("Validator found " + errors.getErrorCount() + " errors");  
                } else {  
                    logger.debug("Validator found no errors");  
                }  
            }  
  
        }  
    }


     org.springframework.validation.Validator

        

public interface Validator {  
    boolean supports(Class<?> var1);  
  
    void validate(Object var1, Errors var2);  
}

   下面为我们的实现类UserValidator和ProductValidator


public class UserValidator implements Validator {    
    
    @Override    
    public boolean supports(Class clazz) {    
        return User.class.equals(clazz);    
    }    
    
    @Override    
    public void validate(User user, Errors errors) {    
        if (!StringUtils.hasLength(user.getUsername())) {    
            errors.rejectValue("username", "", "用户名不能为空");    
        }    
        if (!StringUtils.hasLength(user.getPassword())) {    
            errors.rejectValue("password", "", "登录密码不能为空");    
        }    
    }    
    
}


public class ProductValidator implements Validator {    
    
    @Override    
    public boolean supports(Class clazz) {    
        return Product.class.equals(clazz);    
    }    
    
    @Override    
    public void validate(Product product, Errors errors) {    
        if (!StringUtils.hasLength(product.getName())) {    
            errors.rejectValue("name", "", "无效产品");    
        }    
    }    
    
}

    现在我们可以调用各自的策略模式了:


ValidationUtils.invokeValidator(new UserValidator(), user, errors);    
  
ValidationUtils.invokeValidator(new ProductValidator(), Product, errors);


    有的读者可能会产生疑问,这个策略模式怎么没有所谓的子父类的关联呢?策略模式难道不是建立在子父类的关系上吗?我以前完全地认为策略模式是建立在子父类的基础上的,包括《Head First之设计模式》最经典的例子Duck,也都是建立在子父类的关系上的。但是也请大家仔细读读策略模式的规范性定义,自始至终没有出现子父类或继承的字眼。策略模式一定是强调以算法族,来实现某算法游离于使用算法的客户之外。这也是我要举出这个Spring源码作为例子的缘由,希望大家可以发现策略模式的本质所在。

 

       好了,以上就是我对策略模式的理解。希望能够对大家有所帮助。




版权声明:本文为博主原创文章,未经博主允许不得转载。

策略模式原理及Java代码实例

一、策略模式的定义         —— 定义了一组算法,将每个算法包装起来,并且使它们之间可以互换         —— 策略模式使这些算法在客户端调用它们的时候能够相互不影响的变化,改变不同算法的...

Java设计模式——策略模式实现及原理

简介 定义 策略模式:将可变的部分从程序中抽象分离成算法接口,在该接口下分别封装一系列算法实现。并使他们可以相互替换,从而导致客户端程序独立于算法的改变。 策略模式中的设计原则:变化的抽象成接口;面向...

策略模式原理及例子代码

策略模式将行为和环境隔离开来,环境角色类负责维持和查询行为的类,各种类似的逻辑算法都在各个具体的策略类中进行实现,由于环境和行为隔离开来,所以行为的逻辑变更不会影响到环境和客户端。 如果要使用策略类...
  • eddle
  • eddle
  • 2011年11月03日 21:04
  • 5364

Java策略模式+案例

  • 2010年10月13日 16:16
  • 263KB
  • 下载

工程案例,设计模式——策略模式

策略模式定义         策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。 下面我将从xiang'mu...
  • hherima
  • hherima
  • 2014年06月26日 17:10
  • 959

简单工厂设计模式实现商店买牙膏收费案例过渡到结合策略模式的理由全解

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T...

策略模式demo

  • 2017年11月24日 09:54
  • 7KB
  • 下载

delphi xe7 下实现的策略模式例子

  • 2017年06月26日 14:36
  • 3.01MB
  • 下载

粗糙分析设计模式-策略模式

在软件开发的过程中,经常会碰见一种情况:实现一个功能可以有多种算法或者策略,我们根据实际情况来选择不同的算法或者是策略。比如说我们上班途中计算路费,如果乘公交是一种算法,乘出租车是一种算法。如果我们按...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:策略模式原理及案例分析
举报原因:
原因补充:

(最多只允许输入30个字)