首页 > 代码库 > 利用策略模式消除分支

利用策略模式消除分支

在实际的开发过程中,我们经常会遇到对于不同的对象采用不同的算法或者策略的场景。

一个真实的例子是这样的:

假设现在要将一个Student对象存入数据库。在逻辑层,需要对对象的字段进行合法性判断,比如ID是否超过某个阈值,名字长度是否超长。

 1 public class Student { 2      3     private Integer id; 4      5     private String name; 6      7     public Integer getId() { 8         return id; 9     }10 11     public void setId(Integer id) {12         this.id = id;13     }14 15     public String getFirstName() {16         return name;17     }18 19     public void setFirstName(String firstName) {20         this.name = firstName;21     }22 }

1. if else分支方法

最常见的做法,是将参数的类型进行分类。提供一个方法,对传入的参数,根据不同的参数类型进行校验。

package com.huawei.khlin.strategy;public class App {    public static void validateField(Object param, FieldType type)            throws Exception {        if (type == FieldType.INTEGER) {            // do something....        } else if (type == FieldType.DOUBLE) {            // do something....        } else if (type == FieldType.CHAR) {            // do something....        } else if (type == FieldType.STRING) {            // do something...        }    }    public static enum FieldType {        INTEGER, DOUBLE, CHAR, STRING;    }        public static void main(String[] args) throws Exception {        Student student = new Student();        student.setId(Integer.valueOf(1));        student.setName("kingsley");                validateField(student.getId(), FieldType.INTEGER);        validateField(student.getName(), FieldType.STRING);                //dao层操作    }}

上面的if else分支换成switch效果是一样的。

可以预见的是,当字段的类型越来越多(包括同样类型但阈值不同,例如String 32长度和64长度),validateField方法将会越来越臃肿,分支数量分分钟超过最大圈复杂度15.

2. 策略模式拯救烂代码

策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。

个人理解,策略模式实际上就是利用了类的多态性,通过不同的实现类,在不同的场景下使用不同的算法处理事务。

那么,什么时候就要使用策略模式呢?我认为,当某个算法比较复杂,并且你头脑里第一个闪现的想法是用if else来做分支处理,那么就是使用策略模式的时候。

策略模式的结构图如下:

技术分享

主要有三个组件

Context:持有Strategy对象

Stragety: 抽象的策略类,提供统一的操作方法

ConcretStrategy:具体的策略类,封装了一些行为或算法

具体到上述的场景中,Context就是调用的客户端,即main方法。而根据不同的类型进行参数校验,则是不同的ConcreteStrategy.

这样,我们的代码就可以改成如下:

抽象的策略类

1 public interface Validator {2     3     public void validate(Object value) throws Exception;4 5 }

具体的策略类

 1 public class IntegerValidator implements Validator { 2  3     private int maxCount = Integer.MAX_VALUE; 4  5     private int minCount = 0; 6  7     public IntegerValidator() { 8  9     }10 11     public IntegerValidator(int minCount, int maxCount) {12         this.minCount = minCount;13         this.maxCount = maxCount;14     }15 16     @Override17     public void validate(Object value) throws Exception {18 19         int realValue = http://www.mamicode.com/0;20 21         if (null == value) {22 23             realValue = http://www.mamicode.com/0;24 25         } else {26             if (!(value instanceof Integer)) {27                 throw new Exception("field is NOT String Type.");28             }29 30             Integer valueInteger = Integer.valueOf(value.toString());31 32             realValue =http://www.mamicode.com/ valueInteger.intValue();33 34         }35 36         if (!(realValue >= minCount && realValue <= maxCount)) {37             throw new Exception("integer value out of range.");38         }39     }40 41 }
 1 public class StringValidator implements Validator { 2      3     private int maxCount = 1024; 4  5     private int minCount = 0; 6  7     public StringValidator() { 8  9     }10 11     public StringValidator(int minCount, int maxCount) {12         this.minCount = minCount;13         this.maxCount = maxCount;14     }15 16     @Override17     public void validate(Object value) throws Exception {18         int charNumber = 0;19 20         if (null == value) {21             22             charNumber = 0;23             24         } else {25             if (!(value instanceof String)) {26                 throw new Exception("field is NOT String Type.");27             }28 29             String valueStr = (String) value;30 31             charNumber = valueStr.length();32 33         }34         35         if(!(charNumber >= minCount && charNumber <= maxCount)) {36             throw new Exception("value out of range.");37         }38 39     }40 }

Context环境

 1 public class ValidatorContext { 2      3     private Validator validator; 4      5     public ValidatorContext(Validator validator) { 6         this.validator = validator; 7     } 8      9     public void operate(Object param) throws Exception {10         validator.validate(param);11     }12 }

main方法调用

 1 public class App { 2  3     private static final Validator DEFAULT_STRING_VALIDATOR = new StringValidator(0, 1024); 4      5     private static final Validator DEFAULT_INTEGER_VALIDATOR = new IntegerValidator(0, 100); 6      7     public static void main(String[] args) throws Exception { 8         Student student = new Student(); 9         student.setId(Integer.valueOf(1));10         student.setName("kingsley");11         12         ValidatorContext idContext = new ValidatorContext(DEFAULT_INTEGER_VALIDATOR);13         ValidatorContext nameContext = new ValidatorContext(DEFAULT_STRING_VALIDATOR);14         15         idContext.validate(student.getId());16         nameContext.validate(student.getName());17         // dao层操作18     }19 }

策略模式的优点是可以动态地改变对应的算法,并且在算法扩展时,可以不修改原有代码,而扩展出新的算法(即符合开闭原则)。同时可以有效地减少多条分支的判断,增加代码可读性。

缺点是会产生比较多的策略类,并且客户端需要知道都有哪些策略。

 

利用策略模式消除分支