刘刚刚的个人博客

HeadFirst 设计模式入门--策略模式(Strategy Pattern)

创建时间:2021-10-14 23:38:40
更新时间:2021-10-14 23:38:40

从一次代码的改进来体验设计模式的魅力,了解面向对象的基础和原则,开始跨入设计模式之路。

场景说明

有一个需求,需要有一个鸭子类,然后可以生成不同类型的鸭子.利用面向对象继承的思想,代码如下.

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中一般直接对属性进行赋值即可

上述代码,便是使用策略模式编写出来的代码.

策略模式定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立与使用算法的客户。

使用设计模式沟通时,对团队的好处:

  1. 设计模式可以在沟通中使用很少的词汇表达出更丰富的意思
  2. 除名称之外,设计模式背后还包含着代码的特性、约束等
  3. 让谈话保持在设计层面,而非类与对象层面
  4. 对设计模式了解充分的团队,彼此间对设计的看法不容易产生误解
  5. 帮助初级开发人员成长

 学设计模式要知道的

面向对象基础:

  • 抽象
  • 封装
  • 多态
  • 继承

面向对象原则:

  • 封装变化
  • 多用组合,少用继承
  • 针对接口编程,不针对实现编程

个人思考

在以前使用oo思想开发时,总是想着使用继承,以后需要多多思考。

我的名片

昵称:shuta

职业:后台开发(python、php)

邮箱:648949076@qq.com

站点信息

建站时间: 2020/2/19
网站程序: ANTD PRO VUE + TP6.0
晋ICP备18007778号