Fork me on GitHub

【趣味设计模式系列】之【策略模式】

 


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. 总结

优点

  • 算法可以自由切换
  • 避免使用多重条件判断
  • 扩展性良好

缺点

  • 策略类数量增多
  • 所有的策略类都需要对外暴露

__EOF__

本文作者小猪爸爸
本文链接https://www.cnblogs.com/father-of-little-pig/p/12832665.html
关于博主:不要为了技术而技术,总结分享技术,感恩点滴生活!
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   小猪爸爸  阅读(559)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示