设计模式之 ==> 策略设计模式
一、什么是策略设计模式
策略设计模式(Strategy Pattern)定义了一系列的算法,并将每一个算法封装起来,而且使它们之间可以互相替换。策略模式让算法的变化不会影响到使用算法的客户。
分析下定义,策略模式定义和封装了一系列的算法,它们是可以相互替换的,也就是说它们具有共性,而它们的共性就体现在策略接口的行为上,另外为了达到最后一句话的目的,也就是说让算法独立于使用它的客户而独立变化,我们需要让客户端依赖于策略接口。
二、策略设计模式结构图
这个类图并不复杂,右边是策略接口以及它的实现类,左边会有一个上下文,这个上下文会拥有一个策略,而具体这个策略是哪一种,我们是可以随意替换的
三、策略模式写法举例
我们需要实现这样一个功能:
- 我们需要对一批不同格式的文件进行处理
- 文件类型有 mp4,avi,rmvb,不同格式的文件使用不同的文件解析器来解析文件
- 文件类型可不一定只有这几种,随着业务的扩展会不断增加, 比如 : txt, jpg,png等等
下面,我们使用策略设计模式来实现这个功能:
首先,先来定义一个文件类型的枚举和自定义的一个文件类,用于描述我们处理的文件类型
public class MyFile { private FileType fileType; private String fileName; private String filePath; public MyFile(Builder builder) { this.fileType = builder.fileType; this.fileName = builder.fileName; this.filePath = builder.filePath; } public static Builder builder() { return new Builder(); } public static class Builder { private FileType fileType; private String fileName; private String filePath; public Builder fileType(FileType fileType) { this.fileType = fileType; return this; } public Builder fileName(String fileName) { this.fileName = fileName; return this; } public Builder filePath(String filePath) { this.filePath = filePath; return this; } public MyFile build() { return new MyFile(this); } } public FileType getFileType() { return fileType; } public String getFileName() { return fileName; } public String getFilePath() { return filePath; } @Override public String toString() { return "MyFile{" + "fileType=" + fileType + ", fileName='" + fileName + '\'' + ", filePath='" + filePath + '\'' + '}'; } }
public enum FileType { MP4, AVI, RMVB, PNG }
再来,定义策略接口,相当于结构图中的 Strategy 接口
public interface IStrategy<T> { void parse(T t); }
这里也可以不使用泛型,因为我们在前面定义了文件描述的类 MyFile
然后,是三个具体的策略类,分别是 mp4,avi,rmvb 三种格式文件的解析器,相当于结构图中的 ConcreteStrategy
public class Mp4ParserStrategy implements IStrategy<MyFile> { @Override public void parse(MyFile file) { System.out.println("Mp4ParserStrategy.parse file=" + file); } }
public class AviParserStrategy implements IStrategy<MyFile> { @Override public void parse(MyFile file) { System.out.println("AviParserStrategy.parse file=" + file); } }
public class RmvbParserStrategy implements IStrategy<MyFile> { @Override public void parse(MyFile file) { System.out.println("RmvbParserStrategy.parse file=" + file); } }
这里没有实现解析器的功能,只是进行了简单的描述
再下来是上下文,又或者叫策略管理的类,相对于结构图中的 Context
public final class ParserStrategyManager { private final Map<FileType, IStrategy<MyFile>> strategyMap; private ParserStrategyManager() { this.strategyMap = Maps.newConcurrentMap(); this.strategyMap.put(FileType.AVI, new AviParserStrategy()); this.strategyMap.put(FileType.MP4, new Mp4ParserStrategy()); this.strategyMap.put(FileType.RMVB, new RmvbParserStrategy()); } public void doParser(MyFile file) { if (!strategyMap.containsKey(file.getFileType())) { throw new IllegalStateException("无法处理这种格式的文件"); } this.strategyMap.get(file.getFileType()).parse(file); } private static class ClassHolder { private static final ParserStrategyManager INSTANCE = new ParserStrategyManager(); } public static ParserStrategyManager getInstance() { return ClassHolder.INSTANCE; } }
在这个类中,我们将文件类型和策略解析器以 Key,Value的形式存入一个Map当中,这样客户端在调用时只需传入文件的相关属性(包括文件类型),我们就根据文件类型找到文件对应的解析器进行解析,后续如果增加了其他的文件类型,只需要在 FileType 中增加类型并且增加具体的解析策略类,最后在 ParserStrategyManager 类中将文件类型和对应的解析策略类维护进 Map当中就可以了。
最后,来看一下客户端调用和结果
public class App { public static void main(String[] args) { MyFile file = MyFile.builder() .filePath("/home") .fileName("movie.avi") .fileType(FileType.AVI) .build(); ParserStrategyManager.getInstance().doParser(file); } }
运行结果:
根据运行结果可以看出,根据客户端传入的文件类型(AVI类型),找到了具体的解析策略 AviParserStrategy.parse 进行解析。