定义
策略模式就是定义一个算法族,分别封装起来,让他们之前可以互相替换。此模式让算法的变化独立于使用算法的客户。
我的个人理解
定义中的解释可能有些官方,用大白话说,就是为了解决一个问题,定义一组方法类,然后呢,具体要用哪一种方法解决问题,让用户自己去决定。举个现实中的例子,我们去超市买东西,我们就是客户,我们买好东西要付钱了,我们会有很多种方式:支付宝支付,现金支付,微信支付等。具体用哪一种方式来付钱,完全取决于我们自身(客户)。策略模式实际上关心的不是算法的实现,而且提供一种方式管理这些算法。下面以这个购物付款的例子进行编码实现一个简单的策略模式。
编码实现
首先,抽象出一个付款的接口。在设计模式中,很重要的一个设计原则就是面向接口编程(这里的面向接口,是广义的,即面向超类进行编程,也就是说也包含抽象类)。这个付款的接口就是我们客户要使用的付款对象的超类。
public interface PayMethod {
void pay();
}
其次,定义出我们付款的三种类型,都是该接口的实现类:
public class WechatPay implements PayMethod {
@Override
public void pay() {
System.out.println("我使用微信支付");
}
}
public class CashPay implements PayMethod {
@Override
public void pay() {
System.out.println("我使用现金支付");
}
}
public class AliPay implements PayMethod {
@Override
public void pay() {
System.out.println("我使用支付宝来支付");
}
}
现在我们可以定义客户对象了,就是要使用付款方式的对象。我们将付款方式作为客户对象的一个属性,并且以构造方法的形式初始化。
public class Customer {
private PayMethod payMethod;
public Customer(PayMethod payMethod) {
this.payMethod = payMethod;
}
public void buyGoods() {
this.payMethod.pay();
System.out.println("我东西买好了");
}
}
从代码中我们可以看出,购物的方法buyGoods()实际上是用传入的PayMethod的付款方式进行付款的,由于三种付款方式都实现了付款的接口,也就是都有一个付款的方法。所以这里利用了多态的优势。
现在可以写测试类来测试了,也很简单:
public class Main {
public static void main(String[] args) {
PayMethod payMethod = new WechatPay();
Customer customer = new Customer(payMethod);
customer.buyGoods();
}
}
我们可以看到,先定义了一个微信的付款方式,再传进实例化的客户对象中,接着我们调用购买buyGoods()方法的时候,实际上用的就是微信的付款方式。至此,一个策略模式的demo已经实现了。
思考策略模式的优缺点
优点:我们从上例中可以看出,策略模式的一个优点,就是将算法统一的管理起来了。这些算法之间相互平等,并且都实现一个接口,包含相同的方法。这使得客户使用的时候可以随意的根据自己的需要切换。
缺点:客户如果想要随意的切换策略,就要知道所有的策略类,不然无法根据自己的需要来确定最终的策略;其次,所有的策略类都是以类的形式存在的,所以如果某一个模式包含的策略比较多时候,会造成类的数量很多,不利于维护。
jdk中用到的策略模式举例
jdk中的比较器的实现,用的实际上就是策略模式,我们在针对一个集合排序的时候,可以传入一个比较器Comparator进行自定义的比较策略实现。如下例:
定义一个Student学生类(重写toString是为了方便打印结果观看):
public class Student {
private String name;
private Integer age;
private String info;
public Student() {
}
public Student(String name, Integer age, String info) {
this.name = name;
this.age = age;
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", info='" + info + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
先定义一个年龄比较器,根据学生的年龄进行升序排列:
public class AgeComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge()-o2.getAge();
}
}
再定义一个姓名比较器,根据学生的姓名进行排序:
public class NameComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
}
接着编写测试类测试,打印出根据年龄排序和根据姓名排序的集合的值:
public class TestStrategy {
public static void main(String[] args) {
List<Student> studentList = new ArrayList<Student>();
studentList.add(new Student("zhangsan", 12, "张三"));
studentList.add(new Student("lisi", 11, "李四"));
studentList.add(new Student("wangwu", 13, "王五"));
studentList.add(new Student("zhaoliu", 14, "赵六"));
studentList.add(new Student("tianqi", 15, "田七"));
Collections.sort(studentList, new AgeComparator());
System.out.println("根据年龄比较==>" + studentList.toString());
Collections.sort(studentList, new NameComparator());
System.out.println("根据姓名比较==>" + studentList.toString());
}
}
打印结果:
我们可以看到,根据年龄比较的结果,集合按照年龄的大小进行了升序排列;根据姓名比较的结果,集合按照姓名的首字母的码表顺序进行了排序。
上例中的两个比较器,都实现了Comparator的接口,都实现了compare的方法。跟之前的购物付款的例子很相似。