HeadFirst 设计模式入门--策略模式(Strategy Pattern)
从一次代码的改进来体验设计模式的魅力,了解面向对象的基础和原则,开始跨入设计模式之路。
场景说明
有一个需求,需要有一个鸭子类,然后可以生成不同类型的鸭子.利用面向对象继承的思想,代码如下.
import abc
class Duck(metaclass=abc.ABCMeta):
def quack(slef):
print('鸭子叫')
def swim(self):
print('鸭子游泳')
@abc.abstractmethod
def display(self):
pass
class BlackDuck(Duck):
def display(self):
print('这是一只黑色的鸭子')
class RedDuck(Duck):
def display(self):
print('这是一只红色鸭子')
black_duck = BlackDuck()
black_duck.display()
black_duck.quack()
red_duck = RedDuck()
red_duck.display()
red_duck.quack()
>>>
这是一只黑色的鸭子
鸭子叫
这是一只红色鸭子
鸭子叫
如果我们要实例化一个木头鸭子,可以增加以下代码:
class WoodDuck(Duck):
def quack():
pass
def display(self):
print('这是一只红色鸭子')
因为木头鸭并不会叫,我们给木头鸭,重写了叫的方式。如果我们想给鸭子增加飞的功能,那么需要修改Duck的代码为下边的代码:
class Duck(metaclass=abc.ABCMeta):
def fly(self):
print('鸭子飞起来了')
def quack(self):
print('鸭子叫')
def swim(self):
print('鸭子游泳')
@abc.abstractmethod
def display(self):
pass
这时,因为木头鸭不会飞,我们还需要去修改木头鸭的代码:
class WoodDuck(Duck):
def fly():
pass
def quack():
pass
def display(self):
print('这是一只红色鸭子')
如果我们再生成一个橡皮鸭,会叫不会飞,且叫声不同,这时我们需要对其方法重写:
class RubberDuck():
def fly(self):
pass
def quack(self):
print('橡皮鸭子的声音')
def display(self):
print('这是一只红色鸭子')
通过继承,导致复写的代码在多个子类中重复,同时,牵一发而动全身。当修改了父类时,子类可能也需要改变。
tip:
为了复用而继承时,如果设计到了【维护】那么可能因为复用而带来麻烦
改进
在《head first 设计模式》书中,介绍的是使用接口进行改进,python官方并没有提供接口的实现方案。python可以使用第三方包实现接口,接口的实现方式介绍可以查看 https://fsdafdas 。java中使用接口可以实现类似多继承的功能,但是在接口中的方法必须在子类中重写。而python 无需接口即可实现多继承,以下的代码使用的是python中使用接口改写的书中原java代码.
import abc
from zope.interface import Interface
from zope.interface.declarations import implementer
class Duck(metaclass=abc.ABCMeta):
def swim(self):
print('鸭子游泳')
@abc.abstractmethod
def display(self):
pass
class Flyable(Interface):
def fly(self):
pass
@implementer(Flyable)
class BlackDuck(Duck):
def fly(self):
print('鸭子飞')
def display(self):
print('这是一只黑色的鸭子')
class RubberDuck(Duck):
def display(self):
print('这是一只橡皮鸭')
black_duck = BlackDuck()
black_duck.fly()
black_duck = RubberDuck()
black_duck.display()
使用接口会导致代码在每个实现的类中都需要重写。增加了代码量,对开发并不友好。而在python中可以使用多继承(java不支持),使用python多继承来进行改写时,可以减少需要重写的代码。
以下是使用python的多继承实现的代码:
import abc
class Duck(metaclass=abc.ABCMeta):
def swim(self):
print('鸭子游泳')
@abc.abstractmethod
def display(self):
pass
class Flyable():
def fly(self):
print('鸭子飞')
class BlackDuck(Duck,Flyable):
def display(self):
print('这是一只黑色的鸭子')
class RubberDuck(Duck):
def display(self):
print('这是一只橡皮鸭')
black_duck = BlackDuck()
black_duck.fly()
black_duck = RubberDuck()
black_duck.display()
相对于上边java的代码来说好一点,但是缺少对属性调整的灵活性,比如:刚开始初始化的时候木头鸭没有飞行功能,但在对象生成后要给他加一个飞行装置。
import abc
class FlyBehavior(Interface):
def fly(self):
pass
@implementer(FlyBehavior)
class FlyNoWithWings():
def fly(self):
print("会飞")
@implementer(FlyBehavior)
class FlyNoWay():
def fly(self):
print("不会飞")
class QuackBehavior(Interface):
def quack(self):
pass
@implementer(QuackBehavior)
class Quack():
def quack(self):
print('呱呱')
@implementer(QuackBehavior)
class MuteQuack():
def quack(self):
print("不会叫")
class Duck(metaclass=abc.ABCMeta):
fly_behavior = None
quack_behavior = None
def performFly(self):
self.fly_behavior.fly()
def performQuack(self):
self.quack_behavior.quack()
def swim(self):
print('鸭子游泳')
@abc.abstractmethod
def display(self):
pass
class BlackDuck(Duck):
def __init__(self):
self.fly_behavior = FlyNoWithWings()
self.quack_behavior = MuteQuack()
def display(self):
print('这是一只黑色的鸭子')
class WoodDuck(Duck):
def __init__(self):
self.fly_behavior = FlyNoWay()
self.quack_behavior = Quack()
def display(self):
print('这是一只木头鸭子')
black_duck = BlackDuck()
black_duck.display()
black_duck.performFly()
wood_duck = WoodDuck()
wood_duck.display()
wood_duck.performFly()
我们还可以调整为可动态调整行为的代码
class Duck(metaclass=abc.ABCMeta):
fly_behavior = None
quack_behavior = None
def setFlyBehavior(self,b):
self.fly_behavior = b
def setQuackBehavior(self,q):
self.quack_behavior = q
def performFly(self):
self.fly_behavior.fly()
def performQuack(self):
self.quack_behavior.quack()
def swim(self):
print('鸭子游泳')
@abc.abstractmethod
def display(self):
pass
class FlyRocketPowered():
def fly(self):
print("火箭")
wood_duck = WoodDuck()
wood_duck.setFlyBehavior(FlyRocketPowered())
wood_duck.display()
wood_duck.performFly()
tip:
通过 set 来设置属性是java常用的方式。python中一般直接对属性进行赋值即可
上述代码,便是使用策略模式编写出来的代码.
策略模式定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立与使用算法的客户。
使用设计模式沟通时,对团队的好处:
设计模式可以在沟通中使用很少的词汇表达出更丰富的意思
除名称之外,设计模式背后还包含着代码的特性、约束等
让谈话保持在设计层面,而非类与对象层面
对设计模式了解充分的团队,彼此间对设计的看法不容易产生误解
帮助初级开发人员成长
学设计模式要知道的
面向对象基础:
抽象
封装
多态
继承
面向对象原则:
封装变化
多用组合,少用继承
针对接口编程,不针对实现编程
个人思考
在以前使用oo思想开发时,总是想着使用继承,以后需要多多思考。