策略模式
策略模式
直接上概念可能有些不太好理解,先来看个现成的此模式的应用吧,java中的Collections.sort(List<T>,Comparator<T>).
举个列子,医院的护士每年都会进行知识考试(四个科目),由考试的成绩决定每个科室内护士的能力排名,最终会影响年终奖的发放,医院现有四个科室,妇科,外科,内科,消化科,每个护士的总分由四个科目组成,不同的科室每个科目所占的比例不一样,比如说妇科的护士成绩:科目1*0.3+科目2*0.4+科目3*0.1+科目4*0.2。
首先会有一个Nurse(护士类),这个类包含了护士的基本信息,所属科室,四个科目的成绩等等,代码如下:
1 public class Nurse { 2 private String id; 3 private String name; 4 private int age; 5 private String departCode; 6 private double scorekm1; // 科目1 7 private double scorekm2; // 科目2 8 private double scorekm3; // 科目3 9 private double scorekm4; // 科目4 10 public String getId() { 11 return id; 12 } 13 public void setId(String id) { 14 this.id = id; 15 } 16 public String getName() { 17 return name; 18 } 19 public void setName(String name) { 20 this.name = name; 21 } 22 public int getAge() { 23 return age; 24 } 25 public void setAge(int age) { 26 this.age = age; 27 } 28 public String getDepartCode() { 29 return departCode; 30 } 31 public void setDepartCode(String departCode) { 32 this.departCode = departCode; 33 } 34 public double getScorekm1() { 35 return scorekm1; 36 } 37 public void setScorekm1(double scorekm1) { 38 this.scorekm1 = scorekm1; 39 } 40 public double getScorekm2() { 41 return scorekm2; 42 } 43 public void setScorekm2(double scorekm2) { 44 this.scorekm2 = scorekm2; 45 } 46 public double getScorekm3() { 47 return scorekm3; 48 } 49 public void setScorekm3(double scorekm3) { 50 this.scorekm3 = scorekm3; 51 } 52 public double getScorekm4() { 53 return scorekm4; 54 } 55 public void setScorekm4(double scorekm4) { 56 this.scorekm4 = scorekm4; 57 } 58 public Nurse(String id,String name,int age,String departCode,double scorekm1,double scorekm2,double scorekm3,double scorekm4) 59 { 60 this.id=id; 61 this.name=name; 62 this.departCode=departCode; 63 this.age=age; 64 this.scorekm1=scorekm1; 65 this.scorekm2=scorekm2; 66 this.scorekm3=scorekm3; 67 this.scorekm4=scorekm4; 68 } 69 }
有了bean后,要对同一科室的护士进行成绩排序,我们还需要一个比较器,针对不同科室的成绩计算方式不一样,我们需要有不同的算法,这部分的算法我们就封装进比较器里面,所以需要建立四个实现comparator接口的具体类,代码如下:
妇科比较器类:
1 public class CompareForfk implements Comparator<Nurse>{ 2 @Override 3 public int compare(Nurse o1, Nurse o2) { 4 double score01=o1.getScorekm1()*0.3+o1.getScorekm2()*0.2+o1.getScorekm3()*0.1+o1.getScorekm4()*0.4; 5 double score02=o2.getScorekm1()*0.3+o2.getScorekm2()*0.2+o2.getScorekm3()*0.1+o2.getScorekm4()*0.4; 6 if(score01<=score02){ 7 return -1; 8 }else{ 9 return 1; 10 } 11 } 12 }
内科比较器类:
public class CompareFornk implements Comparator<Nurse>{ @Override public int compare(Nurse o1, Nurse o2) { double score01=o1.getScorekm1()*0.3+o1.getScorekm2()*0.2+o1.getScorekm3()*0.4+o1.getScorekm4()*0.1; double score02=o2.getScorekm1()*0.3+o2.getScorekm2()*0.2+o2.getScorekm3()*0.4+o2.getScorekm4()*0.1; if(score01<=score02){ return -1; }else{ return 1; } } }
外科比较器类:
1 public class CompareForwk implements Comparator<Nurse>{ 2 @Override 3 public int compare(Nurse o1, Nurse o2) { 4 double score01=o1.getScorekm1()*0.3+o1.getScorekm2()*0.4+o1.getScorekm3()*0.2+o1.getScorekm4()*0.1; 5 double score02=o2.getScorekm1()*0.3+o2.getScorekm2()*0.4+o2.getScorekm3()*0.2+o2.getScorekm4()*0.1; 6 if(score01<=score02){ 7 return -1; 8 }else{ 9 return 1; 10 } 11 } 12 }
消化科比较器类:
1 public class CompareForxhk implements Comparator<Nurse> { 2 3 @Override 4 public int compare(Nurse o1, Nurse o2) { 5 double score01=o1.getScorekm1()*0.4+o1.getScorekm2()*0.3+o1.getScorekm3()*0.2+o1.getScorekm4()*0.1; 6 double score02=o2.getScorekm1()*0.4+o2.getScorekm2()*0.3+o2.getScorekm3()*0.2+o2.getScorekm4()*0.1; 7 if(score01<=score02){ 8 return -1; 9 }else{ 10 return 1; 11 } 12 } 13 }
这些基础类都建好了之后,剩下的就是客户端的事啦,比如说对妇科的护士按成绩排序。
Collections.sort(listForFk,new CompareForfk());
看到这里,应该有一个大概的了解了,那么开始了解下策略模式的概念吧。
概念:将一组算法分别封装起来,在使用这些算法时可以让他们相互替换,算法的相互替换独立于客户端的使用。
模式中有三种角色:
一 抽象策略类:所有算法实现的接口
二 具体策略类:具体的算法封装的类
三 环境角色:持有对抽象策略类的引用,由客户端程序调用
大家可以看下Collections.sort方法源代码,可以看到其实最终仍然是listForFk.sort(Comparator<T> c),所以说上面列子中的环境角色就是listForFk。
可能我们对环境角色持有对抽象策略类的引用这一点有一点小小的疑问,因为环境角色既然持有对抽象策略类的引用,那么抽象策略类应该是环境角色的一个属性,即两者之间应该是组合的关系,而上面的例子中,Comparator只是listForFk的一个方法的参数,实际上没有必要这么严格的对比,环境角色持有抽象策略类的引用这只是个抽象的说法,宽松一点来讲,在调用sort方法的时候,listForFk其实也已经持有了比较器的引用,如果没有持有比较器的引用,那么也就没有排序的依据了。
其实,仔细想想,多态的概念也是适用于策略模式的,比如说一个接受父类为参数的方法,当我们每次调用方法的时候,传入一个实际指向子类的父类引用,这样也可以实现子类方法的相互替换,同样独立于客户端的使用。
对于客户端而言,不同的情况下,程序员会选择不同的具体策略类,但是从使用策略类上来讲,客户端的使用方式是一样的,这算是策略模式的优点之一了。但是这有个前提,即程序员要知道所有的具体策略类,知道什么时候用哪个,从这方面来讲,这算是策略模式的一个缺点了,那么能不能改进下这个不好的地方呢,答案就是工厂模式,继续拿之前的护士成绩排序为例。
我们知道,护士成绩的排序是根据科室来的,不同的科室有不同的计算方法,那么我们可以根据科室作为工厂生产对象(比较器类)的依据,代码如下:
1 public class ComparatorFactory { 2 private Map<String, Comparator<Nurse>> map=new HashMap<String,Comparator<Nurse>>(){ 3 { 4 put("NK", new CompareFornk()); 5 put("WK", new CompareForwk()); 6 put("FK", new CompareForfk()); 7 put("XHK", new CompareForxhk()); 8 } 9 }; 10 11 public Comparator<Nurse> getComparatorForDepart(Nurse nurse) { 12 String departCode=nurse.getDepartCode().toUpperCase(); 13 return map.get(departCode); 14 } 15 }
工厂类实现后,我们再看下客户端的调用,代码如下:
1 public class comparator { 2 3 public static void main(String[] args) { 4 5 List<Nurse> listForFk=new ArrayList<Nurse>(){ 6 { 7 add(new Nurse("1","eileen",20,"FK",80,76,83,56)); 8 add(new Nurse("2","shelly",21,"FK",90,76,83,56)); 9 add(new Nurse("3","bailing",23,"FK",75,76,83,56)); 10 } 11 }; 12 Collections.sort(listForFk,new ComparatorFactory().getComparatorForDepart(listForFk.get(0))); 13 Iterator<Nurse> iterator=listForFk.iterator(); 14 while(iterator.hasNext()){ 15 Nurse nurse=iterator.next(); 16 System.out.println(nurse.getName()); 17 } 18 } 19 }
其实加深对模式的理解,还是需要多找些例子来学习,懂了并不代表会了,多思考,多学习。