head first设计模式第一章读书笔记--策略模式

策略模式思想
核心思想,分离变与不变。
例如原先我们设计了一个动物园系统,这个是个动物园是个不正经的动物园,只有鸭子,比如绿头鸭,塑料玩具鸭,木头鸭子等等。原先的系统已经设计好了,系统中有各种鸭子继承了Duck,并有一个方法swim(假设所有鸭子都会游泳)
类图如下:

head first设计模式第一章读书笔记--策略模式

 

 

要知道,作为程序员,肯定知道需求的变动是很频繁的,现在发生了需求变动。要给原来的鸭子添加飞行的方法。有Java基础的会立马想到在Duck类中添加一个Fly方法。但是,请听我说完需求。现在所需要的飞行方法不是单一的,比如绿头鸭和野鸭等“真正的”鸭子是用翅膀飞的;木头鸭,塑料玩具鸭是不能飞的。马达鸭(一种玩具鸭,可以装电池,利用马达飞行)可以利用马达飞行。这样一来,继承的方式就不太好了。如果硬要使用继承,那么需要在每个子类鸭子重写父类的fly方法。
那么,使用接口呢?比如写出接口基类FlyBehavior。
然后用FlyNoWay接口表示不会飞的鸭子,FlyNoWay继承了FlyBehavior,不会飞的鸭子实现FlyNoWay接口;
用FlyWithWings接口表示会用翅膀飞的鸭子,FlyWithWings继承了FlyBehavior,会用翅膀飞的鸭子实现FlyWithWings接口;
以此类推。这样乍看很好啊,没有什么问题。但是很快我们会发现如果是木头鸭和塑料鸭,它们都实现了FlyNoWay接口,并且都在自己的类中将该方法重写一遍。因此,出现了重复代码。
那么,还有其他什么更好的方式来添加飞行方法吗?有,就是委托。委托这个词看起来很难懂,但是我们可以把委托者和被委托者想成has-a(有一个)的关系。比如上面这个例子,鸭子(被委托者)有一个飞行行为(委托者)
把飞行想成Duck的一个功能,Duck具有该功能,即Duck本身含有飞行行为的实例,可以调用飞行行为的飞行方法。

Duck实现

head first设计模式第一章读书笔记--策略模式

 

Duck.java

package duck;


import java.awt.DisplayMode;

import behavioroffly.FlyBehavior;
import behaviorofquack.QuackBeahvior;

public abstract class Duck {
   FlyBehavior flyBehavior;
   QuackBeahvior quackBeahvior;
	public Duck() {
		// TODO Auto-generated constructor stub
	}
	
    public abstract void display();
    
    public void performFly(){
    	flyBehavior.fly();
    }
    
    public void performQuack(){
    	quackBeahvior.quack();
    }
    
}

  

 MallardDuck.java

package duck;

import behavioroffly.FlyBehavior;
import behavioroffly.FlyWithWings;
import behaviorofquack.Quack;
import behaviorofquack.QuackBeahvior;

public class MallardDuck extends Duck{
    
     // FlyBehavior flyBehavior;
    //   QuackBeahvior quackBeahvior;
    
    public MallardDuck() {
        // TODO Auto-generated constructor stub
        quackBeahvior = new Quack();
        flyBehavior = new FlyWithWings();
    }
    

    public void display() {
        System.out.println("i am really duck!");    
    }
}

FlyBehavior.java

package behavioroffly;

public interface FlyBehavior {
   public void fly();
}

 

FlyNoWay.Java

package behavioroffly;

public class FlyNoWay implements FlyBehavior{
   @Override
public void fly() {
    // TODO Auto-generated method stub
   System.out.println("cant fly!");    
}
}

FlyWithWings.Java

package behavioroffly;

public class FlyWithWings implements FlyBehavior{
   public void fly() {
       System.out.println("really fly!");
   }
}

QuackBehavior.Java

package behaviorofquack;

public interface QuackBeahvior {
    public void quack();
}

 

Quack.Java

package behaviorofquack;

public class Quack implements QuackBeahvior{
   @Override
public void quack() {
    // TODO Auto-generated method stub
    System.out.println("Quack!");    
}
}

 

Test.Java

package test;

import duck.Duck;
import duck.MallardDuck;

public class TestDuck {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Duck mallardDuck = new MallardDuck();
        mallardDuck.performFly();
        mallardDuck.performQuack();
    }

}

 

测试结果

head first设计模式第一章读书笔记--策略模式

总结

使用策略模式时的三个重点

1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

 2.针对接口编程,而不是针对实现编程。

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

分离变与不变。回忆之前的需求,可以发现,不变的是swim,变的是fly。虽然swim和fly都是鸭子的行为,但是swim不怎么变化,它可以利用继承来实现代码复用,而飞行方式却有很多种,无法继承,因此需要将飞行行为抽出来,各自实现,让Duck实现类拥有飞行行为实例,达到操作飞行行为的目的。这种思想就像零件的组装,主体是不变的,零件有各种相同功能不同性能的各种款式,这样可以做出不同的产品。
另外策略模式还有一个好处是可以动态变化行为,比如木头鸭子原来是不会飞的,它用setFlyBehavior设置不会飞行,后来设计师给他装上了引擎翅膀,它可以再次通过setFlyBehavior在运行时动态变更飞行行为。

 

适用场景

1.当许多相关的类里面的内容仅仅是行为差异

2.当运行时选取不同的算法变体

3.当发现通过多个判断语句来选取不同行为的时候

策略模式在Spring源码(validator)中的使用

Spring框架的 validator 组件,是个辅助组件,在进行数据的完整性和有效性非常有用,通过定义一个某个验证器,即可在其它需要的地方,使用即可,非常通用。

UML图

head first设计模式第一章读书笔记--策略模式

 

 

 Class: org.springframework.validation.ValidationUtils 验证工具类

public static void invokeValidator(Validator validator, Object obj, Errors errors) {  
        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() + "]");  
        }  
        validator.validate(obj, errors);  
        if (logger.isDebugEnabled()) {  
            if (errors.hasErrors()) {  
                logger.debug("Validator found " + errors.getErrorCount() + " errors");  
            }  
            else {  
                logger.debug("Validator found no errors");  
            }  
        }  
    }  

 

Interface:org.springframework.validation.Validator

public interface Validator {  
    boolean supports(Class clazz);  
    void validate(Object target, Errors errors);  
  
}  

 

Class:UserValidator

public class UserValidator implements Validator {  
  
    @Override  
    public boolean supports(Class clazz) {  
        return User.class.equals(clazz);  
    }  
  
    @Override  
    public void validate(Object target, Errors errors) {  
        User user = (User) target;  
        if (!StringUtils.hasLength(user.getUsername())) {  
            errors.rejectValue("username", "", "登录编码必须填写!");  
        }  
        if (!StringUtils.hasLength(user.getPassword())) {  
            errors.rejectValue("password", "", "登录密码必须填写!");  
        }  
        if (!StringUtils.hasLength(user.getName())) {  
            errors.rejectValue("name", "", "用户姓名必须填写!");  
        }  
    }  
  
}  

 

Class:HarmlessHandleValidator

public class HarmlessHandleValidator implements Validator {  
  
    @Override  
    public boolean supports(Class clazz) {  
        return HarmlessHandle.class.equals(clazz);  
    }  
  
    @Override  
    public void validate(Object target, Errors errors) {  
        HarmlessHandle harmlessHandle = (HarmlessHandle) target;  
        if (!StringUtils.hasLength(harmlessHandle.getHandleNo())) {  
            errors.rejectValue("handleNo", "", "编码必须填写!");  
        }  
        if (!StringUtils.hasLength(harmlessHandle.getHandleName())) {  
            errors.rejectValue("handleName", "", "名称必须填写!");  
        }  
    }  
  
}  

 

上一篇:springboot错误消息message和exception | Validation response is missing errors details


下一篇:JavaScript 的原生错误类型