策略模式可以根据不同的行为使用不同的策略。
问题提出
假设我们要存储用户上传的图像。
类ImageStorage有一个store方法,在store方法里,我们先对图像进行压缩,再给图像应用某种滤镜,然后进行存储。
这个实现有一些问题。
首先,它违反了单一职责原则,store方法不仅负责存储图像,还负责了压缩图像,以及给图像应用滤镜。
其次,添加新的压缩算法或新的滤镜需要修改原来的实现,修改比较困难。
面向对象的四个原则:封装、抽象、继承、多态性,我们该选择哪个来解决这个问题呢?
解决方案
ImageStorage类里可能变化的是压缩算法和应用的滤镜。应用多态性原则我们将它们抽象成接口。
首先是压缩算法。
我们将它抽象成Compressor接口,ImageStorage只依赖于该接口。JpegCompressor、PngCompressor或其他更多的压缩算法实现Compressor接口,这样就达到了开放闭合原则。我们可以通过添加新的Compressor实现类来扩展算法,但同时不修改已有代码。
然后是滤镜。
将其抽象成Filter接口,BlackAndWhite和HighContrast实现该接口。ImageStorage只依赖Filter接口。
这就是策略模式(startegy pattern)。
我们发现,策略模式与状态模式(见下图)很像。
它们之间的区别是:
状态模式是对状态的多态化。
而策略模式并不是针对某种状态,它根据不同的行为使用不同的策略。
代码实现
创建接口Compressor,定义compress方法,实际场景中compress方法的定义类似这样:
byte[] compress(byte[] image);
这里为了简单,定义成:
void compress(String fileName);
再创建JpegCompressor、PngCompressor,实现Compressor接口。
类似地,我们也定义Filter接口,以及类BlackAndWhiteFilter和类HighContrastFilter。
回到ImageStorage类,属性compressor和filter类型分别变更为接口Compressor和接口Filter,store方法中调用compressor.compress和filter.apply即可。
到Main类进行测试。我们可以灵活选择压缩算法和应用滤镜对图像进行处理。
明天我们添加某种压缩算法,或添加某种滤镜,只需添加相应的实现类即可,而无需对现有的代码进行修改。
目前我们更改图像的压缩算法或更改应用的滤镜,都需要重新生成一个ImageStorage实例,稍有点笨重。可以将压缩算法和滤镜的选择移到store方法里作为参数,这样只需一个ImageStorage实例即可实现多文件不同格式的压缩滤镜处理,更加灵活。
小结
策略模式与状态模式有点类似,不过它不是状态的多态性,而是可以根据不同的行为使用不同的策略。