前言:
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性,继承性,多态性以及类的关联关系和组合关系的充分理解。
正确使用设计模式好处:
提高程序员的思维能力、编程能力和设计能力。
使程序设计更加标准化,代码编制更加工程化,使软件开发效率大大提高,缩短软件开发周期
使设计的代码可读性高,可维护性强,可靠性高,灵活性好等。
GOF23分类:
创建型模式(5):单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。
结构型模式(7):适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式。
行为型模式(11):模板方法模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,责任链模式,访问者模式。
OPP七大原则:
开闭原则:对扩展开放。对修改关闭。
里氏替换原则:继承必须确保超类所拥有的性质在子类中依然成立。
依赖倒置原则:面向接口编程,不要面向实现编程
单一职责原则:控制类的粒度大小,将对象解耦,提高其内聚性。
接口隔离原则:要为各个类建立她们需要的专门接口
迪米特法则:只与你的朋友交谈,不跟"陌生人"说话
合成复用原则:尽量先试用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
单例模式singleton
特点:
让类创建的对象,在内存中只有唯一一个实例
每一次实例化对象的内存地址都是相同的
应用场景:
只需要一种实例,eg:各种Mgr,Factory。
以Python为例,首先了解__new__方法:
- __new__方法是由object基类提供的内置静态方法
- 在内存中为对象分配空间
- 返回对象引用
- Python解释器获得对象引用之后,将引用作为第一个参数传递给__init__方法
那么构造单例,其实就是重写父类__new__方法
1、饿汉式(以下为两种常用的构造方法)
"""
* 饿汉式
* 类加载到内存后,就实例化一个单例
* 简单实用
* 缺点:不管用到与否。类加载时就会完成实例化
"""
class Singleton(object):
__instance = None
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super().__new__(cls)
return cls.__instance
def __init__(self):
print("init-----")
class Singleton1(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls, 'instance'):
cls.instance = super(Singleton1, cls).__new__(cls)
return cls.instance
if __name__ == '__main__':
s1 = Singleton()
s2 = Singleton()
print(s1, s2)
# 测试结果
# init-----
# init-----
# <__main__.Singleton object at 0x02D650B8> <__main__.Singleton object at 0x02D650B8>
2、懒汉式
"""
* 懒汉式:lazying loading
* 按需初始化
"""
class Singleton(object):
__instance = None
__Flag = False
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super().__new__(cls)
return cls.__instance
def __init__(self):
if not self.__Flag:
print("init-----")
self.__Flag = True
if __name__ == '__main__':
s1 = Singleton()
s2 = Singleton()
print(s1, s2)
# 测试结果
# init-----
# <__main__.Singleton object at 0x030A5130> <__main__.Singleton object at 0x030A5130>
简单工厂模式:
特点:
通过接口创建对象,且不会暴露对象创建逻辑,实现,用户可以指定自己想要的对象而不必关心对象的实例化过程。
通过固定的接口而不是直接去调用类的实例化方法来获得一个对象的实例,隐藏了实例创建过程的复杂度,解耦了生产实例和使用实例的代码,降低了维护的复杂性。
应用场景:
比如某一类公司能提供一种产品,但是这种产品有不同的型号。当客户需要一种产品,但是没有具体是哪一种型号,只是提供了一些产品参数,公司就根据这些参数来提供产品。
类图:
实现代码
class Product:
def __init__ (self):
self.productType = ''
def getProductType(self):
raise NotImplementError("Product")
def doUse(self):
raise NotImplementError("Product")
class ProductA(Product):
def __init__ (self):
self.productType = "ProductA"
self.productParameter = "A"
def getProductType (self):
return self.productType;
def getParameter (self):
return self.productParameter
def doUse (self):
print ("这是ProductA实现的功能")
class ProductB(Product):
def __init__ (self):
self.productType = "ProductB"
self.productParameter = "B"
def getProductType (self):
return self.productType;
def getParameter (self):
return self.productParameter
def doUse (self):
print ("这是ProductB实现的功能")
class Company:
def bulidProduct(self,Parameter):
raise NotImplementError("Company")
class CompanyA(Company):
def __init__ (self):
self.product = ''
def bulidProduct(self,Parameter):
if Parameter == "A":
self.product = ProductA()
return self.product
elif Parameter == "B":
self.product = ProductB()
return self.product
else:
return None;
从简单工厂到抽象工厂模式:
假设有两个“产品”分别是Mercedes和BMW的汽车,分别创建这两个类:
class Mercedes(object):
"""梅赛德斯
"""
def __repr__(self):
return "Mercedes-Benz"
class BMW(object):
"""宝马
"""
def __repr__(self):
return "BMW"
采用简单工厂模式,通过传递汽车参数就可实例化对应对象,实现如下:
class SimpleCarFactory(object):
"""简单工厂
"""
@staticmethod
def product_car(name):
if name == 'mb':
return Mercedes()
elif name == 'bmw':
return BMW()
当需要新增一个“产品”,例如:Audi的汽车,车,那么需要除了新增一个Audi类外,还要修改SimpleCarFactory内的product_car方法。这样就违背了软件设计中的开闭原则,即在扩展新的类时,尽量不要修改原有代码。所以我们在简单工厂的基础上把SimpleCarFactory抽象成不同的工厂,每个工厂对应生成自己的产品,这就是工厂方法。代码实现如下:
import abc
class AbstractFactory(object):
"""抽象工厂
"""
__metaclass__ = abc.ABCMeta @abc.abstractmethod
def product_car(self):
pass
class Mercedes(object):
"""梅赛德斯
"""
def __repr__(self):
return "Mercedes-Benz"
class BMW(object):
"""宝马
"""
def __repr__(self):
return "BMW"
class MercedesFactory(AbstractFactory):
"""梅赛德斯工厂
"""
def product_car(self):
return Mercedes()
class BMWFactory(AbstractFactory):
"""宝马工厂
"""
def product_car(self):
return BMW()
但当要生产很多产品,就会发现同样需要写很多对应的工厂类。比如如果MercedesFactory和BMWFactory不仅生产小汽车,还要生产SUV,那我们用工厂方法就要再多构造两个生产SUV的工厂类。所以为了解决这个问题,我们就要再更进一步的抽象工厂类,让一个工厂可以生产同一类的多个产品,这就是抽象工厂。代码实现如下:
import abc
# 两种小汽车
class Mercedes_C63(object):
"""梅赛德斯 C63
"""
def __repr__(self):
return "Mercedes-Benz: C63"
class BMW_M3(object):
"""宝马 M3
"""
def __repr__(self):
return "BMW: M3"
# 两种SUV
class Mercedes_G63(object):
"""梅赛德斯 G63
"""
def __repr__(self):
return "Mercedes-Benz: G63"
class BMW_X5(object):
"""宝马 X5
"""
def __repr__(self):
return "BMW: X5"
class AbstractFactory(object):
"""抽象工厂
可以生产小汽车外,还可以生产SUV
"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def product_car(self):
pass
@abc.abstractmethod
def product_suv(self):
pass
class MercedesFactory(AbstractFactory):
"""梅赛德斯工厂
"""
def product_car(self):
return Mercedes_C63()
def product_suv(self):
return Mercedes_G63()
class BMWFactory(AbstractFactory):
"""宝马工厂
"""
def product_car(self):
return BMW_M3()
def product_suv(self):
return BMW_X5()
三种工厂模式的对比:
-
简单工厂模式适用于需创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂的情况下,而且用户只关心那种类型的实例被创建,并不关心其初始化过程时,比如多种数据库(MySQL/MongoDB)的实例,多种格式文件的解析器(XML/JSON)等。
-
工厂方法模式继承了简单工厂模式的优点又有所改进,其不再通过一个工厂类来负责所有产品的创建,而是将具体创建工作交给相应的子类去做,这使得工厂方法模式可以允许系统能够更高效的扩展。实际应用中可以用来实现系统的日志系统等,比如具体的程序运行日志,网络日志,数据库日志等都可以用具体的工厂类来创建。
-
抽象工厂模式在工厂方法基础上扩展了工厂对多个产品创建的支持,更适合一些大型系统,比如系统中有多于一个的产品族,且这些产品族类的产品需实现同样的接口,像很多软件系统界面中不同主题下不同的按钮、文本框、字体等等。