在Java的集合框架中,经常需要通过构造方法传入一个比较器Comparator,或者创建比较器传入Collections的静态方法中作为方法参数,进行比较排序等,使用的是策略模式。
一、策略模式的定义
定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
二、策略模式体现了两个非常基本的面向对象设计原则:
封装变化的概念,找出应用中可能需要变化之处,把它独立出来,不要把那些不需要变化的代码混在一起,系统会变得更有弹性。
编程中使用接口,而不是对接口的实现。
三、策略模式的角色组成
抽象策略角色:策略类,通常由一个接口或者抽象类实现
具体策略角色:包装了相关的算法和行为
环境角色:持有一个策略类的引用,最终给客户端调用的
四、编写策略模式的一般步骤:
1. 对策略对象定义一个公共接口
2. 编写具体策略类,该类实现了上面的接口
3. 在使用策略对象的类(即:环境角色)中保存一个对策略对象的引用
4. 在使用策略对象的类中,实现对策略对象的set和get方法(注入)或者使用构造方法完成赋值
5. 客户端进行调用
五、示例代码
/** * 策略类 * */ public abstract class Strategy { public abstract void algorithmln() ; }
/** *具体策略类 */ public class ConcreateStrategyA extends Strategy{ @Override public void algorithmln() { System.out.println("A"); } } /** *具体策略类 */ public class ConcreateStrategyB extends Strategy{ @Override public void algorithmln() { System.out.println("B"); } } /** *具体策略类 */ public class ConcreateStrategyC extends Strategy{ @Override public void algorithmln() { System.out.println("C"); } }
/** *环境角色 */ public class Context { //定义一个对策略对象的引用 private Strategy strategy; public Context(Strategy strategy) { this.strategy=strategy; } public void contextStrategy() { strategy.algorithmln(); } }
测试代码
public class TestStrategy { public static void main(String[] args) { //测试 Context contextA=new Context(new ConcreateStrategyA()); contextA.contextStrategy(); Context contextB=new Context(new ConcreateStrategyB()); contextB.contextStrategy(); Context contextC=new Context(new ConcreateStrategyC()); contextC.contextStrategy(); } }
输出结果
A
B
C
策略模式在Java中处处可以体现,TreeSet和TreeMap中均存在这样的构造方法:TreeSet(Comparator<? super E> comparator)
和TreeMap(Comparator<? superK> comparator)
,对它的描述为:构造一个空的TreeSet,它根据指定比较器进行排序。这里的指定比较器就是我们根据需要自己写的“算法”,这就是策略模式最基本的使用方式。
Java实现代码
要求:有这样一个类Class Person,包含id、name、age属性,有若干了Person对象存储在List中,要求对他们进行排序,分别按照名字、年龄、id进行排序(要有正序与倒序两种方式)。如果年龄或者姓名重复,则按照id的正序进行排列。要求使用策略模式进行。
思路分析:可以将若干Person类存放在ArrayList中,然后利用Collections中的 sort(List<T> list,Comparator<? super T> c)方法根据指定比较器产生的顺序对指定列表进行排序,我们要实现具体的比较规则,由于Comparator是一个接口,正好可以作为步骤一中的抽象策略角色。
1. 对策略对象定义一个公共接口
省了一步,直接用接口Comparator即可
2. 编写具体策略类,该类实现了上面的接口
按name进行排序的具体策略类SortByName,其它SortById和SortByAge与之类似:
package com.StrategyJob; import java.util.Comparator; public class SortByName implements Comparator { private int sortType = 0 ; // 定义标记位,默认是采用升序排列 @Override public int compare(Object arg0, Object arg1) { Person p0 = (Person)arg0 ; Person p1 = (Person)arg1 ; String name0 = p0.name ; String name1 = p1.name ; int id0 = p0.id ; int id1 = p1.id ; if(name0.endsWith(name1)){ return id0 - id1 ; } if(this.sortType != 0 ){ return name1.compareTo(name0); }else{ return name0.compareTo(name1); } } // 设置排序类型 public void setSortType(int sign) { this.sortType = sign ; } }
3. 在使用策略对象的类(即:环境角色)中保存一个对策略对象的引用 ; 在使用策略对象的类中,实现对策略对象的set和get方法(注入)或者使用构造方法完成赋值
这里3和4的步骤是在一起
package com.StrategyJob; import java.util.Comparator; public class Environment { private Comparator comparator ; // 私有的抽象策略角色的引用 public Environment(Comparator comp) { this.comparator = comp ; } public void setComparator(Comparator comp){ this.comparator = comp ; } public Comparator getComparator(){ return this.comparator; }
客户端进行调用
public static void main(String[] args){ // 按姓名name进行排序 SortByName sortbyname = new SortByName() ; Environment environment = new Environment(sortbyname); Person p1 = new Person(); Person p2 = new Person(); Person p3 = new Person(); Person p4 = new Person(); ArrayList list = new ArrayList(); p1.setName("zhangsan"); p1.setId(1); p1.setAge(20); p2.setName("lisi"); p2.setId(5); p2.setAge(25); p3.setName("wangwu"); p3.setId(16); p3.setAge(5); p4.setName("zhangsan"); p4.setId(6); p4.setAge(5); list.add(p1); list.add(p2); list.add(p3); list.add(p4); System.out.println("--------------------- 按照name升序排列 -----------------------"); // 按照name进行排序 Collections.sort(list, environment.getComparator()); for(Iterator iter = list.iterator() ; iter.hasNext() ; ){ Person p = (Person)iter.next(); String name = p.name ; int id = p.id ; System.out.println(name + " "+ id); } sortbyname.setSortType(1); System.out.println("--------------------- 按照name降序排列start -----------------------"); // 按照name进行排序 Collections.sort(list, environment.getComparator()); for(Iterator iter = list.iterator() ; iter.hasNext() ; ){ Person p = (Person)iter.next(); String name = p.name ; int id = p.id ; System.out.println(name + " "+ id); } System.out.println("---------------------- 按照id升序排列 ----------------------"); SortById sortbyid = new SortById(); environment.setComparator(sortbyid); Collections.sort(list, environment.getComparator()); for(Iterator iter = list.iterator() ; iter.hasNext() ; ){ Person p = (Person)iter.next(); int id = p.id ; System.out.println(id + ""); } System.out.println("---------------------- 按照id降序排列 ----------------------"); sortbyid.setSortType(1); Collections.sort(list, environment.getComparator()); for(Iterator iter = list.iterator() ; iter.hasNext() ; ){ Person p = (Person)iter.next(); int id = p.id ; System.out.println(id + ""); } System.out.println("---------------------- 按照age升序排列 ----------------------"); SortByAge sortbyage = new SortByAge(); environment.setComparator(sortbyage); Collections.sort(list, environment.getComparator()); for(Iterator iter = list.iterator() ; iter.hasNext() ; ){ Person p = (Person)iter.next(); int age = p.age ; System.out.println(age + " " + p.id); } System.out.println("---------------------- 按照age降序排列 ----------------------"); sortbyage.setSortType(1); Collections.sort(list, environment.getComparator()); for(Iterator iter = list.iterator() ; iter.hasNext() ; ){ Person p = (Person)iter.next(); int age = p.age ; System.out.println(age + " " + p.id); } }
运行结果
应用场景和优缺点
对于Strategy模式来说,主要有这些应用场景:
1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为
2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现
3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。
对于Strategy模式来说,主要有如下优点:
1、 提供了一种替代继承的方法,而且既保持了继承的优点(代码重用)还比继承更灵活(算法独立,可以任意扩展)。
2、 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。
3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。
对于Strategy模式来说,主要有如下缺点:
1、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。
参考资料
《Head First Design Patterns》
http://blog.csdn.net/houqd2012/article/details/12585093?utm_source=tuicool