策略模式是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端。
策略模式的主要角色有
Context(上下文): 负责维护一个具体的策略类实例,并根据它来协调算法的执行。
Strategy(抽象策略): 定义了一个算法族,声明了一个算法的操作,它有一个或多个具体的策略类实现。
ConcreteStrategy(具体策略): 具体实现了 Strategy 接口定义的算法。
策略模式的优点
策略模式的缺点
// 支付策略接口
interface PaymentStrategy {
boolean pay(double amount);
}
// 具体的支付策略实现
class CashPaymentStrategy implements PaymentStrategy {
@Override
public boolean pay(double amount) {
System.out.println("现金支付了 " + amount + " 元");
return true;
}
}
class CreditCardPaymentStrategy implements PaymentStrategy {
@Override
public boolean pay(double amount) {
System.out.println("信用卡支付了 " + amount + " 元");
return true;
}
}
// 上下文类
class PaymentContext {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public boolean executePayment(double amount) {
return strategy.pay(amount);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
PaymentContext context = new PaymentContext();
context.setStrategy(new CashPaymentStrategy());
context.executePayment(100.0); // 现金支付了 100.0 元
context.setStrategy(new CreditCardPaymentStrategy());
context.executePayment(200.0); // 信用卡支付了 200.0 元
}
}
PaymentStrategy
接口定义了支付操作的抽象策略。CashPaymentStrategy
和 CreditCardPaymentStrategy
分别实现了现金支付和信用卡支付的具体策略。PaymentContext
类是上下文,它维护了一个策略实例,并提供了执行支付操作的方法。在一个电商项目中,我们可以根据不同的营销策略对商品进行不同的定价。比如,对于普通用户可以按照原价定价,而对于VIP用户则可以打折。我们可以使用策略模式来实现这个需求。
// 定价策略接口
interface PricingStrategy {
double calculatePrice(double originalPrice);
}
// 原价定价策略
class OriginalPricingStrategy implements PricingStrategy {
@Override
public double calculatePrice(double originalPrice) {
return originalPrice;
}
}
// VIP折扣策略
class VipDiscountPricingStrategy implements PricingStrategy {
private final double discountRate;
public VipDiscountPricingStrategy(double discountRate) {
this.discountRate = discountRate;
}
@Override
public double calculatePrice(double originalPrice) {
return originalPrice * (1 - discountRate);
}
}
// 定价上下文
class PricingContext {
private PricingStrategy strategy;
public void setStrategy(PricingStrategy strategy) {
this.strategy = strategy;
}
public double calculatePrice(double originalPrice) {
return strategy.calculatePrice(originalPrice);
}
}
在一个需要记录日志的系统中,我们可能需要根据不同的日志级别采用不同的日志记录策略。比如,对于DEBUG级别的日志,我们可以将其记录到控制台,而对于ERROR级别的日志,我们则需要将其记录到文件中。这种情况下,就可以使用策略模式。
// 日志记录策略接口
interface LoggingStrategy {
void log(String message, LogLevel level);
}
// 控制台日志记录策略
class ConsoleLoggingStrategy implements LoggingStrategy {
@Override
public void log(String message, LogLevel level) {
System.out.println("[" + level + "] " + message);
}
}
// 文件日志记录策略
class FileLoggingStrategy implements LoggingStrategy {
@Override
public void log(String message, LogLevel level) {
// 将日志记录到文件中
}
}
// 日志记录上下文
class LoggingContext {
private LoggingStrategy strategy;
public void setStrategy(LoggingStrategy strategy) {
this.strategy = strategy;
}
public void log(String message, LogLevel level) {
strategy.log(message, level);
}
}
在一个需要进行数据压缩的系统中,我们可能需要根据不同的场景采用不同的压缩算法。比如,对于一般的文本数据,我们可以使用Gzip算法进行压缩,而对于多媒体数据,我们则需要使用更高效的算法。这种情况下,我们可以使用策略模式来实现不同的压缩算法。
// 压缩策略接口
interface CompressionStrategy {
byte[] compress(byte[] data);
byte[] decompress(byte[] compressedData);
}
// Gzip压缩策略
class GzipCompressionStrategy implements CompressionStrategy {
@Override
public byte[] compress(byte[] data) {
// 使用Gzip算法进行压缩
}
@Override
public byte[] decompress(byte[] compressedData) {
// 使用Gzip算法进行解压缩
}
}
// ZLIB压缩策略
class ZlibCompressionStrategy implements CompressionStrategy {
@Override
public byte[] compress(byte[] data) {
// 使用ZLIB算法进行压缩
}
@Override
public byte[] decompress(byte[] compressedData) {
// 使用ZLIB算法进行解压缩
}
}
// 压缩上下文
class CompressionContext {
private CompressionStrategy strategy;
public void setStrategy(CompressionStrategy strategy) {
this.strategy = strategy;
}
public byte[] compress(byte[] data) {
return strategy.compress(data);
}
public byte[] decompress(byte[] compressedData) {
return strategy.decompress(compressedData);
}
}
Arrays.sort()
方法,不同的排序策略进行排序Java 的 Arrays.sort()
方法在内部确实使用了策略模式来实现不同的排序算法。下面我们来详细分析一下它的底层实现源码。
Arrays.sort()
方法的实现位于 java.util.Arrays
类中,它根据待排序数组的类型和大小,选择合适的排序算法进行排序。在这个过程中,它使用了策略模式来封装不同的排序算法。
首先,让我们看看 Arrays.sort()
方法的签名:
public static void sort(Object[] a) {
// 判断数组类型和长度
if (LegacyMergeSort.userRequested)
legacyMergeSort(a);
else
ComputeMaxs.parallelSort(a, null);
}
可以看到,对于对象数组,sort()
方法会根据一个名为 LegacyMergeSort.userRequested
的标志位,选择使用旧的归并排序算法(legacyMergeSort
)或者新的并行排序算法(ComputeMaxs.parallelSort
)。这里就体现了策略模式的思想,不同的排序算法被封装在不同的策略类中。
接下来,我们看看新的并行排序算法 ComputeMaxs.parallelSort()
的实现:
static <T extends Comparable<? super T>> void parallelSort(T[] a, Comparator<? super T> cmp) {
// 根据数组长度选择合适的排序算法
int len = a.length;
if (len > MIN_ARRAY_SORT_GRAN) {
rangeSort(a, 0, len - 1, cmp);
} else if (len != 0) {
Binarysort.sort(a, 0, len, null, cmp);
}
}
private static <T extends Comparable<? super T>>
void rangeSort(T[] a, int from, int to, Comparator<? super T> cmp) {
// 使用TimSort或归并排序算法进行排序
if (from == 0 && to == a.length - 1) {
// 使用TimSort算法
new TimSort(a, cmp).sort(a, from, to);
} else {
// 使用归并排序算法
new MergeSort(a, cmp, from, to).sort();
}
}
在上面的代码中,我们可以看到
parallelSort()
方法根据数组长度选择使用 rangeSort()
或 Binarysort.sort()
。rangeSort()
用于较大的数组,而 Binarysort.sort()
用于较小的数组。rangeSort()
方法根据数组的范围,选择使用 TimSort
算法或归并排序(MergeSort
)算法。这里,TimSort
、MergeSort
和 Binarysort
就是不同的排序策略类。它们都实现了相应的排序算法,而 parallelSort()
和 rangeSort()
充当了策略模式中的上下文(Context)角色,根据具体情况选择合适的策略类。
让我们继续看看 TimSort
的实现
static final class TimSort<T extends Comparable<? super T>> extends MergeSort<T> {
// TimSort算法的实现代码...
}
TimSort
类继承自 MergeSort
类,它是一种改进的归并排序算法,对于部分有序的数组有更好的性能表现。
而 MergeSort
类则实现了传统的归并排序算法
static final class MergeSort<T extends Comparable<? super T>> extends Sorter<T> {
// 归并排序算法的实现代码...
}
MergeSort
继承自 Sorter
抽象类,Sorter
定义了一些公共的排序方法和字段,同时也包含了 Binarysort
的实现。
abstract static class Sorter<T extends Comparable<? super T>> {
// 一些公共方法和字段...
// Binarysort算法的实现
static <T extends Comparable<? super T>> void sort(T[] a, int from, int to, ...) {
// Binarysort算法实现代码...
}
}
通过上面的源码分析,我们可以看到 Java Arrays.sort()
方法是如何使用策略模式来选择合适的排序算法的。不同的排序算法被封装在不同的策略类中,如 TimSort
、MergeSort
和 Binarysort
。而 parallelSort()
和 rangeSort()
方法则根据具体情况选择合适的策略类进行排序。
这种设计使得 Arrays.sort()
方法可以灵活地切换不同的排序算法,也便于后续添加新的排序算法。同时,由于每种算法都被封装在单独的类中,代码的可读性和维护性也得到了提高。
总的来说,Java 的 Arrays.sort()
方法是一个很好的策略模式的应用实例,它展示了如何使用策略模式来封装和选择不同的算法,提高代码的灵活性和可扩展性。
ResourceLoader
类,不同的资源位置采用不同的加载策略在 Spring 5 中,ResourceLoader
接口使用了策略模式来加载不同类型的资源。它定义了一个统一的接口,而具体的资源加载策略则由不同的实现类来处理。下面我们来分析一下它的底层源码:
ResourceLoader
接口的定义如下:
public interface ResourceLoader {
Resource getResource(String location);
ClassLoader getClassLoader();
}
它定义了两个方法:
getResource(String location)
用于根据给定的资源位置返回对应的 Resource
对象。getClassLoader()
返回相关的 ClassLoader
。Spring 提供了一个默认的 ResourceLoader
实现类 DefaultResourceLoader
。它实现了多种资源加载策略,包括从类路径、文件系统、URL 等不同位置加载资源。
public class DefaultResourceLoader implements ResourceLoader {
// ...
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
// 通过 ClassPathContextResource 策略加载类路径资源
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathContextResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
// ... 其他策略
}
}
在 getResource
方法中,根据资源位置的不同前缀,采用不同的资源加载策略。比如,对于以 classpath:
开头的资源位置,它会使用 ClassPathContextResource
策略进行加载。
ClassPathContextResource
是 ClassPathResource
的子类,它实现了从类路径中加载资源的具体策略:
public class ClassPathResource extends AbstractFileResolvingResource {
// 获取资源的具体实现...
}
Spring 还提供了其他一些 ResourceLoader
的实现,如:
FileSystemResourceLoader
: 从文件系统中加载资源ServletContextResourceLoader
: 从 Servlet 上下文中加载资源...
通过组合不同的资源加载策略,Spring 可以非常灵活地从各种不同的位置加载资源。
总的来说,ResourceLoader
接口充当了策略模式中的抽象策略角色,而具体的资源加载策略则由其不同的实现类(如 ClassPathResource
、FileSystemResource
等)扮演了具体策略的角色。当需要加载资源时,Spring 会根据资源位置信息选择合适的策略实现,从而完成资源的加载过程。