一、设计模式简介
模式设计代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人人员在软件开发过程中面临的一般问题的解决方案。
设计模式在软件开发过程中的两个主要用途:
- 开发人员的共同平台:设计模式提供了一个标准的术语系统,且具体到特定的情境。
- 最佳的实践
1.1设计模式的类型
序号 | 模式&描述 | 包括 |
1 | 创建者模式:这些设计模式创建了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用new运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活 |
|
2 | 结构型模式:这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。 |
|
3 | 行为型模式:这些设计模式特别关注对象之间的通信 |
|
1.2设计模式的六大原则
- 开闭原则(open close principle)
对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。 - 里氏代换原则(Liskov Substitution Principle)
里式代换原则是面向对象设计的基本原则之一。里氏代换原则中说,任何基类出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。 - 依赖倒转原则(Dependence Inversion Principle)
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。 - 接口隔离原则(Interface Segregation Principle)
使用多个隔离的接口,比使用单个接口要好。另外一个意思:降低类之间的耦合度。 - 迪米特法则,最少知道原则(Demeter Principle)
一个实体应当尽量少的和其他实体之间发生相互作用,使得系统功能模块相对独立。 - 合成复用原则(Composite Reuse Principle)
尽量使用合成/聚合的方式,而不是使用继承。
二、创建者模式设计模式
一句话概括工厂模式:
- 简单工厂:一个工厂类,一个产品抽象类
- 工厂方法:多个工厂类,一个产品抽象类
- 抽象工厂:多个工厂类,多个产品抽象类
生活中的工厂模式举例: - 简单工厂:一个麦当劳店,可以生产多种汉堡
- 工厂方法类:一个麦当劳可以生产多种汉堡;一个肯德基也可以生产多种汉堡。
- 抽象工厂类:百胜餐饮集团下有肯德基和百事公司,肯德基生产汉堡,百事公司生产百事可乐。
2.1 工厂模式
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,而是通过使用一个共同的接口来指向新创建的对象。
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决 | 主要解决接口选择的问题 |
何时使用 | 我们明确的计划不同条件下创建不同实例时 |
如何解决 | 让其子类实现工厂接口,返回的也是一个抽象的产品。 |
关键代码 | 创建过程在其子类执行。 |
应用实例 | 1.你需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 |
优点 | 1.一个调用者想创建一个对象,只要知道其名称就可以了2.扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。3.屏蔽产品的具体实现,调用者只关心产品的接口。 |
缺点 | 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂性,同时也增加了系统具体类的依赖,这并不是什么好事。 |
实用场景:
- 日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
- 数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
- 设计一个连接服务器的框架,需要有三个协议"POP3", “IMAP”,“HTTP”,可以把三个作为产品类,共同实现一个接口。
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方,复杂对象适合使用工厂模式,而简单对象,特别是只要通过new就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂性。
实现
我们将创建一个Shape接口和实现Shape接口的实体类。下一步是定义工厂类ShapeFactory。FactoryPatternDemo类使用ShapeFactory来获取Shape对象。它将向ShapeFactory传递信息(Circle/Square/Rectangle),以便获取它所需对象的类型。
实例:
# Python原生默认不支持接口,默认多继承,所有的方法必须不能实现
from abc import abstractmethod,ABCMeta
# 声明一个抽象接口
class Shape(metaclass=ABCMeta):
@abstractmethod
def draw(self):
pass
# 三个形状继承实现Shape接口
class Rectangle(Shape):
def draw(self):
print("Inside Rectangle.draw.method")
class Circle(Shape):
def draw(self):
print("Inside Circle.draw.method")
class Square(Shape):
def draw(self):
print("Inside Square.draw.method")
# 创建一个工厂
class ShapeFactory():
def getShape(self,shapeType):
if shapeType==None:
return None
elif shapeType.upper()=="CIRCLE":
return Circle()
elif shapeType.upper()=="RECTANGLE":
return Rectangle()
elif shapeType.upper()=="SQUARE":
return Square()
return None
# 调用
if __name__=="__main__":
shapeFactory=ShapeFactory()
shape1=shapeFactory.getShape("CIRCLE")
shape1.draw()
shape2=shapeFactory.getShape("RECTANGLE")
shape2.draw()
shape3=shapeFactory.getShape("Square")
shape3.draw()
2.2 抽象工厂模式
抽象工厂模式是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建类型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是一个负责创建相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
当产品只有一个的时候,抽象工厂模式随即变成工厂模式;当工厂模式的产品变成多个时,工厂模式变为抽象工厂模式。
意图 | 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类 |
主要解决 | 主要解决接口选择的问题 |
何时使用 | 系统的产品有多于一个的产品族,而系统只消费其中某一族的产品 |
如何解决 | 在一个产品族里面,定义多个产品 |
关键代码 | 在一个工厂里聚合多个同类产品 |
应用实例 | 一个人有两套或多套衣服,比如说有商务装(成套,一系列具体产品) |
优点 | 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象 |
缺点 | 产品族扩展非常困难,要增加一个系列的某一产品,既要在一个抽象的Creator里加代码,又要在具体的里面加代码 |
使用场景 | 1.QQ换皮肤,一整套一起换 2. 生成不同操作系统的程序 |
注意事项 | 产品族难扩展,产品等级容易扩展 |
举例:我们将创建Shape 和Color接口实现这些接口的实体类。下一步是创建抽象工厂AbstractFactory。接着定义工厂类ShapeFactory和ColorFactory,这两个工厂类都是扩展了AbstractFactory。然后创建工厂创造器/生成器类FactoryProducter。
AbstractFactoryPatternDemo类使用FactoryProducter来获取AbstracttFactory对象。它将向AbstractFactory传递形状信息Shape(CIRCLE/RECTANGL/SQUARE),以便获取它所需对象的类型。同时它还向AbstractFactory传递颜色信息Color(RED/GREEN/BLUE),以便获取它所需对象的类型。
# Python原生默认不支持接口,默认多继承,所有的方法默认都不能实现
from abc import abstractmethod,ABCMeta
# 创建一个接口Shape
class Shape(metaclass=ABCMeta):
@abstractmethod
def draw(self):
pass
# 创建Shape的实体类
class Rectangle(Shape):
def draw(self):
print("Inside Rectagle:draw.method")
class Square(Shape):
def draw(self):
print("Inside Square:draw.method")
class Circle(Shape):
def draw(self):
print("Inside Circle:draw.method")
# 创建一个Color接口
class Color(metaclass=ABCMeta):
def fill(self):
pass
# 创建颜色的实体类
class Red(Color):
def fill(self):
print("Inside Red.fill.method")
class Green(Color):
def fill(self):
print("Inside Green.fill.method")
class Blue(Color):
def fill(self):
print("Inside Blue.fill.method")
# 创建抽象工厂
class AbstractFactory(metaclass=ABCMeta):
@abstractmethod
def getColor(self,color):
pass
@abstractmethod
def getShape(self,shape):
pass
# 创建抽象工厂实例ShapeFactory,ColorFactory
class ShapeFactory(AbstractFactory):
def getShape(self,shapeType):
if shapeType==None:
return None
elif shapeType.upper()=="CIRCLE":
return Circle()
elif shapeType.upper()=="RECTANGLE":
return Rectangle()
elif shapeType.upper()=="SQUARE":
return Square()
return None
def getColor(self,colorType):
pass
class ColorFactory(AbstractFactory):
def getShape(self,shapeType):
pass
def getColor(self,colorType):
if colorType==None:
return None
elif colorType.upper()=="RED":
return Red()
elif colorType.upper()=="GREEN":
return Green()
elif colorType.upper()=="BLUE":
return Blue()
return None
# 创建工厂创造器/生产器类
class FactoryProducter():
# 这里不能写成def getFactory(self,choiceType):否则会报错
# 因为是静态方法,被直接调用,所以不能带self参数
# 如果不是静态方法,必须加self参数,且需要先实例化对象,再用实例化的对象调用方法
@abstractmethod
def getFactory(choiceType):
if choiceType.upper()=="SHAPE":
return ShapeFactory()
elif choiceType.upper()=="COLOR":
return ColorFactory()
return None
# 调用输出
if __name__=="__main__":
shapeFactory=FactoryProducter.getFactory("SHAPE")
shape1= shapeFactory.getShape("CIRCLE")
shape1.draw()
shape2=shapeFactory.getShape("RECTANGLE")
shape2.draw()
shape3=shapeFactory.getShape("SQUARE")
shape3.draw()
colorFactory= FactoryProducter.getFactory("Color")
color1=colorFactory.getColor("RED")
color1.fill()
color2 = colorFactory.getColor("GREEN")
color2.fill()
color3 = colorFactory.getColor("BLUE")
color3.fill()
2.3 单例模式
2.4 建造者模式
使用多个简单对象,一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一个创建对象的最佳方式。
一个builder类会一步一步构造最终的对象。该builder类是独立于其他对象的。
意图 | 将一个复杂的构建与其表示相分离,这使得同样的构建过程可以创建不同的表示 |
主要解决 | 主要解决在软件系统中,有时候面临着一个复杂对象的创建工作,其通常由各部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将他们组合在一起的算法却相对稳定 |
何时使用 | 一些组件基本不会变,而其组合经常发生变化的时候 |
如何解决 | 将变与不变分离开 |
关键代码 | 建造者:创建和提供实例;导演:管理建造出来的实例的依赖关系 |
应用实例 | 1.去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成处所谓的套餐 |
优点 | 1.建造者独立,易扩展。2.便于控制细节风险 |
缺点:1.产品必须有共同点,范围有限制 2 如果内部变化复杂,会有很多的建造类 | |
使用场景 | 1.需要生成的对象具有复杂的内部结构2.需要生成的对象内部属性本身相互依赖 |
注意事项 | 与工厂模式的区别:建造者模式更加关注与零件装配的顺序 |
实例:
假设一个快餐的商业案例,其中,一个典型的套餐可以是一个汉堡(burger)和一杯冷饮(Cold drink) 。汉堡(Burger)可以是素食汉堡(Veg Burger)或者鸡肉汉堡(Chicken Burger),他们是包在纸盒中。冷饮(Cold drink)可以是可口可乐(coke)或者百事可乐(pepsi),他们是装在瓶子中。
我们将建造一个表示事物条目(比如汉堡和冷饮)的Item接口和实现Item接口的实体类,以及一个表示实物包装的Packing接口和实现Packing接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。然后我们创建一个Meal类和带有Item的ArrayList和一个通过结合Item来创建不同类型的Meal对象的MealBuilder。
from abc import abstractmethod,ABCMeta
# 创建一个表示食物条目和食物包装的接口
class Item(metaclass=ABCMeta):
@abstractmethod
def myName(self):
pass
@abstractmethod
def packing(self):
pass
@abstractmethod
def price(self):
pass
class Packing(metaclass=ABCMeta):
@abstractmethod
def pack(self):
pass
# 实现Packing接口的实体类
class Wrapper(Packing):
def pack(self):
return "Wrapper"
class Bottle(Packing):
def pack(self):
return "Bottle"
# 实现Item接口的抽象类
class Burger(Item):
def packing(self):
return Wrapper()
@abstractmethod
def price(self):
pass
class ColdDrink(Item):
def packing(self):
return Bottle()
@abstractmethod
def price(self):
pass
# 创建扩展了Burger和ColdDrink的实体类
class VegBurger(Burger):
def price(self):
return 25.0
def myName(self):
return "Veg Burger"
class ChickenBurger(Burger):
def price(self):
return 50.5
def myName(self):
return "Chicken Burger"
class Coke(ColdDrink):
def price(self):
return 30.0
def myName(self):
return "Coke"
class Pepsi(ColdDrink):
def price(self):
return 35.0
def myName(self):
return "Pepsi"
# 设计一个Meal类,带有上面定义的对象
class Meal():
__items=[]
# 注意初始化,否则变量类会被重用
def __init__(self):
self.__items=[]
def addItem(self,aItem):
self.__items.append(aItem)
def getCost(self):
mySum=0
for myCost in self.__items:
mySum = mySum+myCost.price()
return mySum
def showItems(self):
for myItem in self.__items:
print("Item : %s , Packing : %s , Price : %5.2f" %(myItem.myName(), myItem.packing().pack(), myItem.price()))
# 设计一个MealBuilder类,负责创建Meal对象
class MealBuilder():
def prepareVegmeal(self):
meal=Meal()
meal.addItem(VegBurger())
meal.addItem(Coke())
return meal
def prepareNonVegMeal(self):
meal = Meal()
meal.addItem(ChickenBurger())
meal.addItem(Pepsi())
return meal
# 输出
if __name__=="__main__":
mealBuilder=MealBuilder()
vegMeal = mealBuilder.prepareVegmeal()
print("Veg Meal")
vegMeal.showItems()
print("Total Cost:%5.2f"%vegMeal.getCost())
noVegMeal=mealBuilder.prepareNonVegMeal()
print("\n\nNon-Veg Meal")
noVegMeal.showItems()
print("Total Cost:%5.2f" % noVegMeal.getCost())
2.5 原型模式
三、结构型模式
3.1 适配器模式
3.2 桥接模式
3.3 过滤器模式
3.4 组合模式
3.5 装饰器模式
装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有类的一个包装。
这种模式创建了一个装饰类,用来包装现有的类,并在保证类方法签名完整性的前提下,提供了额外的功能。例如:我们将把一个形状装饰上不同的颜色,同时又不改变 形状类型。
意图 | 动态地给一个对象添加额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活 |
主要解决 | 一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。 |
何时使用 | 在不想增加很多字类的情况下扩展类 |
如何解决 | 将具体的功能职责划分,同时继承装饰者模式 |
关键代码 | 1.Component类充当抽象角色,不应该具体实现 2.装饰类引用和继承Component类,具体扩展类重写父类方法。 |
应用实例 | 1.孙悟空有72变,当他变成庙宇后,他的根本还是一只猴子,但是他又有了庙宇的功能。2. |
优点 | 装饰类和被装饰类都可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个类的功能。 |
缺点 | 多层装饰比较复杂 |
使用场景 | 1.扩展一个类的功能 2.动态增加功能,动态撤销 |
注意事项 | 可代替继承 |
# Decorator Pattern
from abc import abstractmethod,ABCMeta
# 创建Shape接口
class Shape(metaclass=ABCMeta):
@abstractmethod
def draw(self):
pass
# 实现Shape的实体类:Rectangle、Circle
class Rectangle(Shape):
def draw(self):
print("Shape: Rectangle")
class Circle(Shape):
def draw(self):
print("Shape: Circle")
# 创建实现了Shape接口的抽象装饰类ShapeDecorator类
class ShapeDecorator(Shape):
_decoratedShape = None
def __init__(self,inDecoratedShape):
self._decoratedShape = inDecoratedShape
def draw(self):
self._decoratedShape.draw()
# 创建扩展了ShapeDecorator类的实体装饰类对象
class RedShapeDecorator(ShapeDecorator):
def __init__(self,inDecoratedShape):
ShapeDecorator.__init__(self,inDecoratedShape)
def draw(self):
self._decoratedShape.draw()
self.setRedBorder(self._decoratedShape)
def setRedBorder(self,inDecoratedShape):
print("Border Color: Red")
# 调用输出
if __name__ == '__main__':
aCircle = Circle()
aRedCircle = RedShapeDecorator(Circle())
aRedRectangle = RedShapeDecorator(Rectangle())
print("Circle with normal border")
aCircle.draw()
print("\nCircle of red border")
aRedCircle.draw()
print("\nRectangle of red border")
aRedRectangle.draw()
补充:
1.abc模块的作用
Python本身不提供抽象类和接口机制,要实现抽象类,可以借助abc模块。abc是Abstract Base Class的缩写。
2.模块中的类和函数
abc.ABCMeta:这是用来生成抽象基础类的元类,由它生成的类可以被直接继承
abc.abstractmethod(function):表明抽象方法的生成器
abc.abstractproperty([fget[,fset[,fdel[,doc]]]]):表明一个抽象属性
3.6 享元模式
3.7 代理模式
四、行为型模式
4.1 责任链模式
4.2 命令模式
4.3 解释器模式
!!!!!
意图 | |
主要解决 | |
何时使用 | |
如何解决 | |
关键代码 | |
应用实例 | |
优点 | |
缺点 | |
使用场景 | |
**** | |
**** |
4.4 迭代器模式
迭代器模式是Java和.Net编程环境中非常常用的设计模式。这种设计模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
迭代器模式属于行为型模式。
意图 | 提供一种方法顺序访问一个聚合对象中各个元素,而又无需暴露该对象的内部表示。 |
主要解决 | 不同的方式来遍历整个整合对象 |
何时使用 | 遍历一个聚合对象 |
如何解决 | 把在元素之间游走的责任交给迭代器,而不是聚合对象 |
关键代码 | 定义接口:hasNext,next |
应用实例 | Java中的iterator |
优点 | 1.它支持以不同的方式遍历一个聚合对象。2.迭代器简化了聚合类。3.在同一个聚合上可以有多个遍历。4.在迭代器模式中,增加新的聚合类和迭代器类都很方便,无需修改原有代码 |
缺点 | 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性 |
使用场景 | 1.访问一个聚合对象的内容而无需暴露它的内部表示。2.需要为聚合对象提供多种遍历方式3.为遍历不同的聚合结构提供统一的一个接口 |
注意事项 | 迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到 不暴露集合的内部结构,又可以让外部代码透明地访问集合内部的数据 |
例子:
创建一个叙述导航方法的Iterator接口和一个返回迭代器的Container接口。实现了Container接口的实体类将负责实现Iterator接口。
IteratorPatternDemo,我们演示使用实体类NamesRepository来打印NamesRepository中存储为集合的Names。
# 迭代器模式
from abc import abstractmethod,ABCMeta
# 创建Iterator
class Iterator(metaclass=ABCMeta):
@abstractmethod
def hasNext(self):
pass
@abstractmethod
def next(self):
pass
# 创建Container接口
class Container(metaclass=ABCMeta):
@abstractmethod
def getIterator(self):
pass
# 创建实现了Iterator接口的类NameIterator
class NameIterator(Iterator):
index = 0
aNameRepository= None
def __init__(self,inNameRepository):
self.aNameRepository=inNameRepository
def hasNext(self):
if self.index< len(self.aNameRepository.names):
return True
return False
def next(self):
if self.hasNext():
theName = self.aNameRepository.names[self.index]
self.index += 1
return theName
return None
# 创建实现了Container接口的实体类
class NameRepository(Container):
names= ["Robert","John","Julie","Amy"]
def getIterator(self):
return NameIterator(self)
# 调用输出
if __name__=="__main__":
nameRepository = NameRepository()
iter=nameRepository.getIterator()
while iter.hasNext():
strName= iter.next()
print("Name "+strName)