如何真正理解好一个「设计模式」?

Python与算法社区

共 3616字,需浏览 8分钟

 ·

2020-08-21 14:34


我的施工之路

1我的施工计划

2数字专题

3字符串专题

4列表专题

5流程控制专题

6编程风格专题

7函数使用

8面向对象编程(上篇)

9面向对象编程(下篇)

10十大数据结构

11包和模块使用总结

12Python 正则使用专题总结

真正理解设计模式

设计模式是无数开发者前辈,经过大量编码实践,总结下来的一套能提高程序扩展性、可复用性的哲学。它就像建筑大师多年经验沉淀下来的楼宇设计方法,又像武侠小说中的武林高手击败对手的武林秘籍。

1 设计模式的由来

实话讲,很多开发者初次接触设计模式时,觉得它太玄学,明明封装为一个对象就能解决问题,为啥非要创建多个对象,各个对象还有引用关系,既不简约,也不可读。学完几个设计模式,最后真心觉得设计模式没用!

直到接手一个大项目时,对设计模式的认识才渐渐有所改变。客户的需求总会变,几天一个样。于是,开发者总要去改动原来的类或方法。好不容易上线,客户需求还在变,于是开发者再回去修改原来的方法。客户新需求确实实现了,但不要求改动的某些功能却意外出现bug,这令开发者非常挠头。

于是,这些前辈们,痛定思定,要设计出一套开发模式,既能保证住原功能的稳定性,同时也能实现客户需求变化。

这才有了设计模式。

2 面向特定场景

前辈们发现,为了同时实现原功能和新功能,一种设计模式很难做到。不同的需求场景,对应开发出不同的设计模式,久而久之,沉淀下十几种经典常用的设计模式。

这些设计模式大概可分类为:创建对象的设计方法,定义行为的设计方法。至于创建对象的设计模式,前辈们根据具体的场景不同,又制定出几种方法;定义行为的方法,也根据场景不同定义出不同的设计方法。

3 对象工厂

这是一种创建对象的设计模式。诞生它的初衷之一,是因为设计出了多个子类,导致这些类的使用者调用起来不是很便捷,于是他们对开发这些类的作者提出需求,需要增加一个对象工厂类来管理子类,由对象工厂组装出不同的子类对象。

这样,使用者只需找到对象工厂类,调用它创建出工厂里的任意一个对象。

大家注意:设计模式与具体的实现语言无关,它是一种提高面向对象可复用性、可扩展性的设计思想。一般来讲,设计模式普遍使用的语言包括:Java、C#、Python等

此处是讲设计模式,简化语言实现,重点帮助大家理解设计模式,因此不要纠结语法,你可以理解为下面是伪代码

首先定义一个接口:

class Interface(object):
  def createCar():
    pass

如下定义 3 个实现接口的类:

class A(Interface):
  def createCar():
    print('A-method')

class B(Interface):
  def createCar():
    print('B-method')
  
class C(Interface):
  def createCar():
    print('C-method')

创建一个对象工厂,专门用于创建A或B或C:

class CarFactory(object):
  def getObject(methodStr):
    if methodStr == 'A':
      return A() # 返回A对象
    if methodStr == 'B':
      return B()
    if methodStr == 'C':
      return C()

使用时,通过 CarFactory().getObject('C') 得到C对象,调用C对象的方法createCar就能根据此方法造车。

4 思考一下

学习设计模式的最终目标是要用到实际开发中,要灵活运用,要养成一种使用直觉。上版对象工厂实现,大家对其有何预期?

首先来看,如果将来生成Car又增加一种D方法,于是乎,需要增加下面的代码:

新增一个类D,这是没有问题的,符合面向对象的可扩展性:

class D(Interface):
  def createCar():
    print('D-method')

但是对象工厂CarFactory这个模块就要修改内部的方法getObject,增加一条生成D对象的分支。但这确实破坏了类的封装!

为解决此问题,实际上还可以进一步抽象,进一步扩展出几个类。比如增加一个抽象工厂类:

class CarFactoryInterface(object):
  pass

重新创建一个实现接口的工厂类:CarFactoryExtend,从而不用修改用来的类文件。

class CarFactoryExtend(CarFactoryInterface):
    def getObject(methodStr):
    if methodStr == 'A':
      return A() # 返回A对象
    if methodStr == 'B':
      return B()
    if methodStr == 'C':
      return C()
    if methodStr == 'D':
       return D()

以上设计模式就是所谓的抽象工厂模式。你看,这些设计模式的形成都是由需求背景的。因此,不是先有设计模式后,开发者们循着设计模式去解决实际需求;而恰恰相反,是有了源源不断的开发需求后,日积月累沉淀下这十几种实际模式。并被后来的开发者们争相模仿学习,更是被领悟其思想精髓者,大呼其好用。

5 设计禁忌

设计模式的几个禁忌,大概总结为以下几点:

  • 不是越抽象越好,也不是不抽象,而是要把握好一个度;
  • 继承链条的根不要是具体的实现类,因为具体不等于抽象,根最好是接口或抽象类;
  • 不要生搬硬套各种设计模式,虽然每个模式都有一个标准版本,但日常使用一般不是死板的模仿,一个角色都不能少;
  • 设计模式不是无用的,如果喜欢总结,再工作几年后,脑子里会有几个常用设计模式;
  • 没有一个通用的设计模式,一个设计模式往往只针对某个特定场景。

6 练习一个设计模式

有一种设计模式常被用于算法开发,先不说它的名字,我们根据实际的需求场景,倒推出这个设计模式。

解决某个特定问题可以使用策略A类里的方法solve:

class A(object):
  def solver():
    print('A method')

后来又发明方法B类:

class B(object):
  def solver():
    print('B method')

设计模式最重要一条:继承链条的根要是抽象类或接口,因此提取出接口StrategyInterface:

class StrategyInterface(object):
    def solver(): # 这是接口的方法
      pass

所以,A类和B类稍作修改:

class A(StrategyInterface):
  def solver():
    print('A method')

class B(StrategyInterface):
  def solver():
    print('B method')

使用方在使用这些策略时,到底该使用哪个策略呢?为了方便策略管理,又多出一个策略管理类:

class StrategyContext(object)
  def setStrategy(StrategyInterface):

    self.strategy = StrategyInterface
  def callMethod():
    print('context of strategy')
    self.strategy.solver()
    print('done')# other things    

实际使用时的方法:

context = StrategyContext()
context.setStrategy(A()) 
context.callMethod()

以上就是策略模式,是一种关于行为控制的设计模式。

如果不想要StrategyContext类,实际使用时的方法如下:

StrategyInterface strategy = A()
print('before strategy')
strategy.solver()
print('done')# other things    

这样又未尝不可呢,继承链收敛于接口,只不过使用者需要多写一些可能不是"太标准"的代码。依赖于设计模式,又能独立思考某些设计模式,这样更有可能灵活使用设计模式。

Python与算法社区

一个写了400+篇原创的技术号

浏览 41
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报