首页 > 代码库 > 利用策略模式消除分支
利用策略模式消除分支
在实际的开发过程中,我们经常会遇到对于不同的对象采用不同的算法或者策略的场景。
一个真实的例子是这样的:
假设现在要将一个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 }
策略模式的优点是可以动态地改变对应的算法,并且在算法扩展时,可以不修改原有代码,而扩展出新的算法(即符合开闭原则)。同时可以有效地减少多条分支的判断,增加代码可读性。
缺点是会产生比较多的策略类,并且客户端需要知道都有哪些策略。
利用策略模式消除分支