【设计模式学习笔记】 之 策略模式
简介:
经常网购的可能发现京东、淘宝等电商平台每到什么节日都会进行打折,这种打折就是一种策略,策略模式的意思呢,就是把不变的和易变的策略分离开,需要什么策略时候,把需要的策略传给执行体,而不是执行体内置这些策略。
举例1:
我们有一个CD播放器,cd播放器中如果内置了一些歌曲的话,那么我们只能听这些歌曲。但是cd本身并没有内置播放资源(播放策略),而是通过插入cd进行播放cd碟片中的资源信息。
有了这个思路,我们先创建一个CD接口,让每个CD实现类实现CD接口,cd接口中有一个sing方法
1 package com.mi.statege.statege1; 2 3 /** 4 * CD接口 5 * @author hellxz 6 */ 7 public interface CD { 8 9 //cd的播放方法,为了不与CDPlayer方法重名,使用sing 10 public void sing(); 11 12 }
1 package com.mi.statege.statege1; 2 3 /** 4 * 周杰伦的专辑 5 * @author hellxz 6 */ 7 public class JayCD implements CD { 8 9 @Override 10 public void sing() { 11 System.out.println("稻香"); 12 System.out.println("听妈妈的话"); 13 System.out.println("最长的电影"); 14 System.out.println("等你下课"); 15 System.out.println("红尘客栈"); 16 System.out.println("发如雪"); 17 } 18 19 }
1 package com.mi.statege.statege1; 2 3 /** 4 * 五月天的专辑 5 * @author hellxz 6 */ 7 public class WuyuetianCD implements CD { 8 9 @Override 10 public void sing() { 11 System.out.println("倔强"); 12 System.out.println("你不是真正的快乐"); 13 } 14 15 }
接下来 创建一个CDPlayer 播放器类,这个类中持有CD接口引用,通过set方法为cd赋值,在play方法中调用cd的sing方法播放唱片内容
1 package com.mi.statege.statege1; 2 3 /** 4 * CD播放器 5 * @author hellxz 6 */ 7 public class CDPlayer { 8 9 private String brand; //CD播放器的品牌 10 11 private CD cd; //持有cd的引用 12 13 public CDPlayer(String brand) { //构造方法初始化品牌 14 this.brand = brand; 15 } 16 17 //播放 18 public void play() { 19 System.out.println(brand+"牌CD播放器启动了……"); 20 cd.sing(); 21 } 22 23 //getters & setters 24 public CD getCd() { 25 return cd; 26 } 27 28 public void setCd(CD cd) { 29 this.cd = cd; 30 } 31 32 }
测试类
1 package com.mi.statege.statege1; 2 3 public class Test { 4 5 public static void main(String[] args) { 6 CDPlayer player = new CDPlayer("步步高"); 7 player.setCd(new WuyuetianCD()); 8 // player.setCd(new JayCD()); 9 player.play(); 10 } 11 }
输出:
步步高牌CD播放器启动了……
倔强
你不是真正的快乐
打开第8行,关掉第七行
步步高牌CD播放器启动了……
稻香
听妈妈的话
最长的电影
等你下课
红尘客栈
发如雪
观察输出,播放器中播放的歌曲的确切换了
举例2:
这个是柳大对于hibernate组合查询语句做的一个通用方法,使用了策略模式,试想:如果我们不用策略模式,而是通过传参的判断,通过传的参数进行拼接sql的话,它同时需要反射生成POJO类,这样一来POJO类名必须要和表名相同,而且如果我们需要传入的参数是like查询而不是=的时候,这样会更加麻烦,对客户端程序员的限制更大。
下面开始实现一下柳大的CommonQuery中如何使用的策略模式吧!
首先创建一个实体POJO的接口,并实现该接口,实现类为Student
1 package com.mi.statege.statege2; 2 3 /** 4 * 定义实体POJO接口 5 * @author hellxz 6 */ 7 public interface Entity { 8 9 //获取sql的方法,返回一个拼接好的query sql 10 public String getSql(); 11 }
Student类中有两个参数,学号以及姓名,学号使用“=”查询,姓名使用“like %str%”进行模糊查询,不为空则将不为空的参数拼接进sql
1 package com.mi.statege.statege2; 2 3 /** 4 * 学生POJO 5 * @author hellxz 6 */ 7 public class Student implements Entity { 8 9 private int stuNo; //学号 10 private String stuName; //姓名 11 12 @Override 13 public String getSql() { //重写获取query sql方法 14 StringBuffer sb = new StringBuffer(); 15 sb.append("select * from student where 1=1 "); 16 if(stuNo != 0) {//非空则学号使用"="进行查询 17 sb.append(" and stuNo = "+stuNo+" "); 18 } 19 if(stuName != null && !stuName.trim().equals("")) {//非空则学生姓名使用"like %str%"进行查询 20 sb.append(" and stuName like '%"+stuName+"%'; "); 21 } 22 return sb.toString(); 23 } 24 25 //getters & setters 26 public int getStuNo() { 27 return stuNo; 28 } 29 30 public void setStuNo(int stuNo) { 31 this.stuNo = stuNo; 32 } 33 34 public String getStuName() { 35 return stuName; 36 } 37 38 public void setStuName(String stuName) { 39 this.stuName = stuName; 40 } 41 42 }
通用查询类CommonQuery,运用多态动态指定POJO类型,获取对应的sql
1 package com.mi.statege.statege2; 2 3 /** 4 * 通用查询类 5 * @author hellxz 6 */ 7 public class CommonQuery { 8 9 private Entity entity; //持有一个Entity接口的引用 10 11 //查询方法 12 public void query() { 13 //调用entity引用,运行期间绑定到实际类型中,调用getSql方法获取拼接好的sql语句 14 String sql = entity.getSql(); 15 System.out.println(sql); //打印sql语句 16 } 17 18 //getters & setters 19 public Entity getEntity() { 20 return entity; 21 } 22 23 public void setEntity(Entity entity) { 24 this.entity = entity; 25 } 26 27 }
测试类
1 package com.mi.statege.statege2; 2 3 public class Test { 4 5 public static void main(String[] args) { 6 Student student = new Student(); 7 student.setStuNo(1); 8 student.setStuName("Hellxz"); 9 CommonQuery query = new CommonQuery(); 10 query.setEntity(student); 11 query.query(); 12 } 13 }
查看输出
select * from student where 1=1 and stuNo = 1 and stuName like '%Hellxz%';
这样一来,我们就可以通过实现Entity接口的对象让CommonQuery对象set进去就可以返回正确的sql了,而且不会有之前那些烦人的问题了
总结:
将执行体与策略分离开,这样执行体中无需内置策略,只需要在执行特定策略的时候将策略set到执行体中执行策略内容即可。
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2、一个系统需要动态地在几种算法中选择一种。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。