策略模式在面向对象编程中是一种非常常见的抽象层展示模式,该模式方便提供一种问题的多种解决方案,每个方案对应一个解法。客户端可以在运行时动态选择最合适的解法。在不同场景下,不同的算法各有利弊,有些时间更短,有些需要更多内存,或者有些适合在多CPU以及分布式环境下运行。用户代码只需要连接到抽象层,以完成相同接口的任务,但以不同的方式实现。
策略模式实例
最经典的策略模式实现的例子是排序;many many years, 无数的算法被学者提出来,比如快速排序,归并排序以及堆排序是典型的快速排序算法,每种都有其优缺点,依赖于数据规模以及输入数据的特点以及所运行的系统等。策略模式提供一个抽象层供调用代码使用,使得它更模块化以及可替代。
我们来考虑另外一种场景:一个桌面壁纸管理器。当一个图像被用于壁纸的时候,他可以按照不同的方式呈现。比如图像小于桌面的时候,他可以重复出现,居中出现或者放大出现。当然有其他的更负责的策略可以使用,例如单方面扩展长度或者宽度,绑定一个固态的,半透明的或者阶梯变化颜色,或者其他操作。然而我们想要之后增加这些策略,一开始我们仅仅提供最基本的几个。
from PIL import Image
class TiledStrategy:
def make_background(self, img_file, desktop_size):
in_img = Image.open(img_file)
out_img = Image.new('RGB', desktop_size)
num_tiles = [
o // i + 1 for o, i in
zip(out_img.size, in_img.size)
]
for x in range(num_tiles[0]):
for y in range(num_tiles[1]):
out_img.paste(
in_img,
(
in_img.size[0] * x,
in_img.size[1] * y,
in_img.size[0] * (x + 1),
in_img.size[1] * (y + 1)
)
)
return out_img
class CenteredStrategy:
def make_background(self, img_file, desktop_size):
in_img = Image.open(img_file)
out_img = Image.new('RGB', desktop_size)
left = (out_img.size[0] - in_img.size[0]) // 2
top = (out_img.size[1] - in_img.size[1]) // 2
out_img.paste(
in_img,
(
left,
top,
left +in_img.size[0],
top + in_img.size[1]
)
)
return out_img
class ScaledStrategy:
def make_background(self, img_file, desktop_size):
in_img = Image.open(img_file)
out_img = in_img.resize(desktop_size)
return out_img
这里我们有三个策略,每个使用PIL来执行他们的任务,每个的make_background方法是一样的,接受相同的参数。下面考虑一下,如果没有策略模式,该如何在这些选项之间进行切换呢?我们必须把它们放到一个很大的方法当中,并且使用一堆if语句来选择合适的操作。每次我们想要添加一个新的策略,我们必须笨重的进行的添加。
Python中的策略实现
前面讲过的策略模式的经典实现,并且在大多数面向的程序中,策略模式非常普通,但却在Python中很少出现。你发现策略模式实现的对象仅仅是提供了一个函数,在Python中我们可以定义__call__函数从而使得对象直接可以被调用。因为没有跟对象关联的其他数据,我们仅仅需要创建一组顶层的函数,将他们作为参数传递即可,而不需要策略模式。
由于Python的第一类函数(First-Class Functions)允许我们按照更加直接的方式实现策略。不过了解这种模式也会帮助我我们选择一种正确的设计方式来编写代码,当我们需要客户需要选择相同接口的不同实现时,需要选择策略模式。