策略模式原理及案例分析

原创 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策略模式+案例

  • 2010-10-13 16:16
  • 263KB
  • 下载

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

策略模式定义         策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。 下面我将从xiang'mu

策略模式原理及例子代码

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

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

一、策略模式的定义         —— 定义了一组算法,将每个算法包装起来,并且使它们之间可以互换         —— 策略模式使这些算法在客户端调用它们的...

C++ 策略模式實現

单例和策略模式示例

  • 2015-12-28 14:51
  • 12KB
  • 下载

JAVA设计模式之策略模式的使用分析

策略模式是面向对象行为模式中的一种简单常用的设计模式。 首先看下GOF对策略模式的定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

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