【趣味设计模式系列】之【策略模式】
1. 简介
策略模式(strategy):定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。
2. 图解
商城搞多种优惠活动,顾客只能参与其中一种优惠算法。
3. 案例实现
类图
FullDistcount
满200减20元;FirstPurchaseDiscount
首次购买减20元;SecondPurchaseDiscount
第二件打9折;HolidayDiscount
节日一律减5元.
代码实现如下,环境类
package com.wzj.strategy;
/**
* @Author: wzj
* @Date: 2020/5/5 21:25
* @Desc: 优惠类:环境类
*/
public class Context {
private int price;
private Discount discount;
public Context(int price, Discount discount) {
this.price = price;
this.discount = discount;
}
public int getPrice() {
return this.discount.calculateBySourcePrice(this.price);
}
}
折扣接口类
package com.wzj.strategy;
/**
* @Author: wzj
* @Date: 2020/5/5 20:56
* @Desc: 折扣优惠接口
*/
public interface Discount {
public int calculateBySourcePrice(int price);
}
满减优惠
package com.wzj.strategy;
/**
* @Author: wzj
* @Date: 2020/5/5 20:57
* @Desc: 优惠满减20元
*/
public class FullDiscount implements Discount {
@Override
public int calculateBySourcePrice(int price) {
if (price > 200){
System.out.println("优惠满减20元");
price = price - 20;
}
return price;
}
}
首次优惠类
package com.wzj.strategy;
/**
* @Author: wzj
* @Date: 2020/5/5 21:11
* @Desc: 首次购买减20元
*/
public class FirstPurchaseDiscount implements Discount {
@Override
public int calculateBySourcePrice(int price) {
if (price > 100){
System.out.println("首次购买减20元");
price = price - 20;
}
return price;
}
}
第二件优惠类
package com.wzj.strategy;
/**
* @Author: wzj
* @Date: 2020/5/5 21:05
* @Desc: 第二件打9折
*/
public class SecondPurchaseDiscount implements Discount {
@Override
public int calculateBySourcePrice(int price) {
System.out.println("第二件打9折");
Double balance = price * 0.9;
return balance.intValue();
}
}
节假日优惠类
package com.wzj.strategy;
/**
* @Author: wzj
* @Date: 2020/5/5 21:09
* @Desc: 节日一律减5元
*/
public class HolidayDiscount implements Discount {
@Override
public int calculateBySourcePrice(int price) {
if (price > 20){
System.out.println("节日一律减5元");
price = price - 5;
}
return price;
}
}
测试类
package com.wzj.strategy;
/**
* @Author: wzj
* @Date: 2020/5/5 21:35
* @Desc: 测试类
*/
public class TestStrategy {
public static void main(String[] args) {
Discount discount = new FirstPurchaseDiscount();
Context context = new Context(110, discount);
int price = context.getPrice();
System.out.println(price);
}
}
结果
首次购买减20元
90
4. JDK中的策略模式-Comparable与Comparator接口
4.1 Comparable
Comparable,在jdk1.8中描述如下,实现该接口的对象的List和array,可以通过Collections.sort和Arrays.sort自动排序,该对象具备sorted map的key和sorted set的元素的特征。
源码解析
public interface Comparable<T> {
public int compareTo(T o);
}
该接口实现一个抽象方法compareTo,定义两个对象的比较方式,返回值大于0、等于0、小于0,分别表示当前对象与传入对象的关系为大于、相等、小于。
4.2 Comparator
Comparator为比较器,它可以作为一个参数传递到Collections.sort和Arrays.sort方法来指定某个类对象的排序方式。同时它也能为sorted set和sorted map指定排序方式。
源码解析
@FunctionalInterface
public interface Comparator<T> {
// 唯一的抽象方法,用于定义比较方式(即排序方式)
// o1>o2,返回1;o1=o2,返回0;o1<o2,返回-1
int compare(T o1, T o2);
boolean equals(Object obj);
// 1.8新增的默认方法:用于反序排列
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
// 1.8新增的默认方法:用于构建一个次级比较器,当前比较器比较结果为0,则使用次级比较器比较
default Comparator<T> thenComparing(Comparator<? super T> other) {
Objects.requireNonNull(other);
return (Comparator<T> & Serializable) (c1, c2) -> {
int res = compare(c1, c2);
return (res != 0) ? res : other.compare(c1, c2);
};
}
// 1.8新增默认方法:指定次级比较器的
// keyExtractor表示键提取器,定义提取方式
// keyComparator表示键比较器,定义比较方式
default <U> Comparator<T> thenComparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
return thenComparing(comparing(keyExtractor, keyComparator));
}
// 1.8新增默认方法:用于执行键的比较,采用的是由键对象内置的比较方式
default <U extends Comparable<? super U>> Comparator<T> thenComparing(
Function<? super T, ? extends U> keyExtractor)
{
return thenComparing(comparing(keyExtractor));
}
// 1.8新增默认方法:用于比较执行int类型的键的比较
default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) {
return thenComparing(comparingInt(keyExtractor));
}
// 1.8新增默认方法:用于比较执行long类型的键的比较
default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) {
return thenComparing(comparingLong(keyExtractor));
}
// 1.8新增默认方法:用于比较执行double类型的键的比较
default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) {
return thenComparing(comparingDouble(keyExtractor));
}
// 1.8新增静态方法:用于得到一个相反的排序的比较器,这里针对的是内置的排序方式(即继承Comparable)
public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
return Collections.reverseOrder();
}
// 1.8新增静态方法:用于得到一个实现了Comparable接口的类的比较方式的比较器
// 简言之就是将Comparable定义的比较方式使用Comparator实现
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
}
// 1.8新增静态方法:得到一个null亲和的比较器,null小于非null,两个null相等,如果全不是null,
// 则使用指定的比较器比较,若未指定比较器,则非null全部相等返回0
public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
return new Comparators.NullComparator<>(true, comparator);
}
// 1.8新增静态方法:得到一个null亲和的比较器,null大于非null,两个null相等,如果全不是null,
// 则使用指定的比较器比较,若未指定比较器,则非null全部相等返回0
public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) {
return new Comparators.NullComparator<>(false, comparator);
}
// 1.8新增静态方法:使用指定的键比较器用于执行键的比较
public static <T, U> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
Objects.requireNonNull(keyExtractor);
Objects.requireNonNull(keyComparator);
return (Comparator<T> & Serializable)
(c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
keyExtractor.apply(c2));
}
// 1.8新增静态方法:执行键比较,采用内置比较方式,key的类必须实现Comparable
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
// 1.8新增静态方法:用于int类型键的比较
public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
}
// 1.8新增静态方法:用于long类型键的比较
public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
}
// 1.8新增静态方法:用于double类型键的比较
public static<T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2));
}
}
JDK1.8之前,Comparator中只要两个方法,就是前两个方法,后面的所有默认方法均为1.8新增的方法,采用的是1.8新增的功能:接口可添加默认方法。即便拥有如此多方法,该接口还是函数式接口,compare用于定义比较方式
4.3 两者区别
- Comparable为可排序的,实现该接口的类的对象自动拥有可排序功能。
- Comparator为比较器,实现该接口可以定义一个针对某个类的排序方式。
- Comparator与Comparable同时存在的情况下,前者优先级高。
4.4 实例
首先定义个类,Student
package com.wzj.strategy;
/**
* @Author: wzj
* @Date: 2020/5/6 21:15
* @Desc:
*/
public class Student implements Comparable<Student>{
private int age;
private String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
定义年龄比较器
package com.wzj.strategy;
import java.util.Comparator;
/**
* @Author: wzj
* @Date: 2020/5/6 21:19
* @Desc: 年龄比较器
*/
public class AgeComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();
}
}
定义姓名比较器
package com.wzj.strategy;
import java.util.Comparator;
/**
* @Author: wzj
* @Date: 2020/5/6 21:19
* @Desc: 姓名比较器
*/
public class NameComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.getName().charAt(0) - o2.getName().charAt(0);
}
}
测试类
package com.wzj.strategy;
import java.util.Arrays;
/**
* @Author: wzj
* @Date: 2020/5/6 21:24
* @Desc:
*/
public class TestComparator {
public static void main(String[] args) {
Student s1 = new Student(18, "zhangsan");
Student s2 = new Student(15, "lisi");
Student s3 = new Student(10,"wangwu");
Student[] students = {s1, s2, s3};
System.out.print("数组排序前:");
printArray(students);
System.out.println();
Arrays.sort(students);
System.out.print("数组通过Comparable接口排序后:");
printArray(students);
System.out.println();
Arrays.sort(students, new AgeComparator());
System.out.print("数组通过年龄比较器AgeComparator排序后:");
printArray(students);
System.out.println();
Arrays.sort(students, new NameComparator());
System.out.print("数组通过姓名比较器NameComparator排序后:");
printArray(students);
}
public static void printArray (Student[] students) {
for (Student student : students) {
System.out.print(student.toString() + ",");
}
}
}
测试结果
数组排序前:Student{age=18, name='zhangsan'},Student{age=15, name='lisi'},Student{age=10, name='wangwu'},
数组通过Comparable接口排序后:Student{age=10, name='wangwu'},Student{age=15, name='lisi'},Student{age=18, name='zhangsan'},
数组通过年龄比较器AgeComparator排序后:Student{age=10, name='wangwu'},Student{age=15, name='lisi'},Student{age=18, name='zhangsan'},
数组通过姓名比较器NameComparator排序后:Student{age=15, name='lisi'},Student{age=10, name='wangwu'},Student{age=18, name='zhangsan'},
5. Spring源码中的策略模式
Spring Bean 实例化,是通过InstantiationStrategy接口实现的,根据创建对象情况的不同,提供了三种方法:无参构造方法、有参构造方法、工厂方法。如下
public interface InstantiationStrategy {
/**
* 默认构造方法
*/
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner)
throws BeansException;
/**
* 指定构造方法
*/
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
Constructor<?> ctor, @Nullable Object... args) throws BeansException;
/**
* 工厂方法
*/
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Object factoryBean, Method factoryMethod, @Nullable Object... args)
throws BeansException;
}
InstantiationStrategy 接口有两个实现类:SimpleInstantiationStrategy 和 CglibSubclassingInstantiationStrategy。SimpleInstantiationStrategy 对以上三个方法都做了简单的实现。
如果是工厂方法实例化,则直接使用反射创建对象,如下:
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Object factoryBean, final Method factoryMethod, @Nullable Object... args) {
try {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
ReflectionUtils.makeAccessible(factoryMethod);
return null;
});
}
else {
ReflectionUtils.makeAccessible(factoryMethod);
}
Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
try {
currentlyInvokedFactoryMethod.set(factoryMethod);
Object result = factoryMethod.invoke(factoryBean, args);
if (result == null) {
result = new NullBean();
}
return result;
}
finally {
if (priorInvokedFactoryMethod != null) {
currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
}
else {
currentlyInvokedFactoryMethod.remove();
}
}
}
// 省略 catch
}
如果是构造方法实例化,则是先判断是否有 MethodOverrides,如果没有则是直接使用反射,如果有则就需要 CGLIB 实例化对象。如下:
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(
(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
}
else {
constructorToUse = clazz.getDeclaredConstructor();
}
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Throwable ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
final Constructor<?> ctor, @Nullable Object... args) {
if (!bd.hasMethodOverrides()) {
if (System.getSecurityManager() != null) {
// use own privileged to change accessibility (when security is on)
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
ReflectionUtils.makeAccessible(ctor);
return null;
});
}
return (args != null ? BeanUtils.instantiateClass(ctor, args) : BeanUtils.instantiateClass(ctor));
}
else {
return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
}
}
SimpleInstantiationStrategy 对 instantiateWithMethodInjection() 的实现任务交给了子类 CglibSubclassingInstantiationStrategy。
类 CglibSubclassingInstantiationStrategy 为 Spring 实例化 bean 的默认实例化策略,其主要功能还是对父类功能进行补充:其父类将 CGLIB 的实例化策略委托其实现
//SimpleInstantiationStrategy
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");
}
//CglibSubclassingInstantiationStrategy
@Override
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
return instantiateWithMethodInjection(bd, beanName, owner, null);
}
CglibSubclassingInstantiationStrategy 实例化 bean 策略是通过其内部类 CglibSubclassCreator 来实现的。
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Constructor<?> ctor, @Nullable Object... args) {
return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
}
6. 总结
优点
- 算法可以自由切换
- 避免使用多重条件判断
- 扩展性良好
缺点
- 策略类数量增多
- 所有的策略类都需要对外暴露