如何真正理解好一个「设计模式」?
共 3616字,需浏览 8分钟
·
2020-08-21 14:34
我的施工之路
2数字专题
4列表专题
7函数使用
10十大数据结构
11包和模块使用总结
真正理解设计模式
设计模式是无数开发者前辈,经过大量编码实践,总结下来的一套能提高程序扩展性、可复用性的哲学。它就像建筑大师多年经验沉淀下来的楼宇设计方法,又像武侠小说中的武林高手击败对手的武林秘籍。
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+篇原创的技术号