Python 设计模式:工厂模式、单例模式
1. 工厂模式
2. 单例模式
1. 工厂模式
1.1 初步设计
设计一个卖车的4S店,该怎样做呢?
1 # 定义车类 2 class Car: 3 4 # 定义车的方法 5 def move(self): 6 print("车启动") 7 8 def stop(self): 9 print("停车") 10 11 12 # 定义一个销售车的店类 13 class CarStore: 14 15 def order(self): 16 self.car = Car() 17 self.car.move() 18 self.car.stop() 19 20 21 # 进行购车 22 my_car = CarStore() 23 my_car.order()
上面的 4s 店,只能销售定义了的一种类型的车。如果这是个销售北京现代品牌的车,比如伊兰特、索纳塔等,该怎样做呢?
设计一个卖北京现代车的4S店
1 # 定义伊兰特车类 2 class YilanteCar(object): 3 4 # 定义车的方法 5 def move(self): 6 print("---车在移动---") 7 8 def stop(self): 9 print("---停车---") 10 11 # 定义索纳塔车类 12 class SuonataCar(object): 13 14 # 定义车的方法 15 def move(self): 16 print("---车在移动---") 17 18 def stop(self): 19 print("---停车---") 20 21 # 定义一个销售北京现代车的店类 22 class CarStore(object): 23 24 def order(self, typeName): 25 #根据客户的不同要求,生成不同的类型的车 26 if typeName == "伊兰特": 27 car = YilanteCar() 28 elif typeName == "索纳塔": 29 car = SuonataCar() 30 return car
这样做不太好,因为当北京现代又生产一种新类型的车时,又得在 CarStore 类中修改,有没有好的解决办法呢?
1.2 简单工厂模式
1)使用函数实现:
1 # 定义伊兰特车类 2 class YilanteCar(object): 3 4 # 定义车的方法 5 def move(self): 6 print("---车在移动---") 7 8 def stop(self): 9 print("---停车---") 10 11 # 定义索纳塔车类 12 class SuonataCar(object): 13 14 # 定义车的方法 15 def move(self): 16 print("---车在移动---") 17 18 def stop(self): 19 print("---停车---") 20 21 # 用来生成具体的对象 22 def createCar(typeName): 23 if typeName == "伊兰特": 24 car = YilanteCar() 25 elif typeName == "索纳塔": 26 car = SuonataCar() 27 return car 28 29 # 定义一个销售北京现代车的店类 30 class CarStore(object): 31 32 def order(self, typeName): 33 # 让工厂根据类型,生产一辆汽车 34 car = createCar(typeName) 35 return car
2)使用类来实现:
1 # 定义伊兰特车类 2 class YilanteCar(object): 3 4 # 定义车的方法 5 def move(self): 6 print("---车在移动---") 7 8 def stop(self): 9 print("---停车---") 10 11 # 定义索纳塔车类 12 class SuonataCar(object): 13 14 # 定义车的方法 15 def move(self): 16 print("---车在移动---") 17 18 def stop(self): 19 print("---停车---") 20 21 # 定义一个生产汽车的工厂,让其根据具体的订单生产车 22 class CarFactory(object): 23 24 def createCar(self, typeName): 25 if typeName == "伊兰特": 26 car = YilanteCar() 27 elif typeName == "索纳塔": 28 car = SuonataCar() 29 30 return car 31 32 # 定义一个销售北京现代车的店类 33 class CarStore(object): 34 35 def __init__(self): 36 #设置4s店的指定生产汽车的工厂 37 self.carFactory = CarFactory() 38 39 def order(self, typeName): 40 # 让工厂根据类型,生产一辆汽车 41 car = self.carFactory.createCar(typeName) 42 return car
咋一看来,好像只是把生产环节重新创建了一个类,这确实比较像是一种编程习惯,此种解决方式被称作简单工厂模式。
工厂函数、工厂类对具体的生成环节进行了封装,这样有利于代码的后需扩展,即把功能划分的更具体,4s店只负责销售,汽车厂只负责制造。
1.3 工厂模式
多种品牌的汽车 4S 店
当买车时,有很多种品牌可以选择,比如北京现代、别克、凯迪拉克、特斯拉等,那么此时该怎样进行设计呢?
把以上代码复制一份显然会有冗余的重复代码,那么可以将汽车店共有的方法放在基类中,而子类负责实现各自不同的部分。
1 # 所有车都有的基本功能 2 class Car: 3 4 def __init__(self, name): 5 self.name = name 6 7 def start(self): 8 print("%s启动了" % self.name) 9 10 def stop(self): 11 print("%s停车了" % self.name) 12 13 14 class Suonata(Car): 15 pass 16 class Mingtu(Car): 17 pass 18 class Mini(Car): 19 pass 20 class Smart(Car): 21 pass 22 23 24 # 生产现代汽车的工厂 25 class ModernFactory: 26 27 def createCarByType(self, type): 28 if type == '索纳塔': 29 return Suonata('索纳塔') 30 if type == '明图': 31 return Mingtu('明图') 32 33 34 # 生产宝马汽车的工厂 35 class BMWFactory: 36 37 def createCarByType(self, type): 38 if type == 'Mini': 39 return Mini('Mini') 40 if type == 'Smart': 41 return Smart('Smart') 42 43 44 # 4S店 基类 45 class CarStore: 46 47 # 提供接口,让子类自己根据需要实现 48 def select_car(self): 49 pass 50 51 def order(self, type): 52 return self.select_car(type) 53 54 # 4S店更多的公有功能 55 # def ... 56 57 58 # 现代汽车4S店 59 class ModernStore(CarStore): 60 61 def select_car(self, type): 62 return ModernFactory().createCarByType(type) 63 64 65 # 宝马汽车4S店 66 class BmwStore(CarStore): 67 68 def select_car(self, type): 69 return BMWFactory().createCarByType(type) 70 71 72 73 modern_store = ModernStore() 74 modern_car = model_store.order("索纳塔") 75 modern_car.start() 76 modern_car.stop() 77 78 bmw_store = BmwStore() 79 bmw = bmw_store.order("Smart") 80 bmw.start() 81 bmw.stop()
- 最后来看看工厂方法模式的定义:定义了一个创建对象的接口(可以理解为函数),由子类决定要实例化的类是哪一个。工厂方法模式让类的实例化推迟到子类,抽象的 CarStore 提供了一个创建对象的方法 select_car,也叫作工厂方法。
- 子类真正实现这个 select_car 方法创建出具体产品。创建者类选择了使用了哪个子类,就决定了实际创建的产品是什么。
2. 单例模式
什么是单例模式?确保某一个类只有一个实例,在实例化时自行向整个系统提供这个实例,这个类被称为单例类,单例模式是一种对象创建型模式。
举个常见的单例模式例子,我们日常使用的电脑上都有一个回收站,在整个操作系统中,回收站只能有一个实例,整个系统都使用这个唯一的实例,而且回收站自行提供自己的实例。因此回收站是单例模式的应用。
创建单例,保证只有 1 个对象
1 class Singleton: 2 3 __instance = None 4 5 # 如果__instance没有被赋值过(即为None), 6 # 那么就创建一个对象,并且赋值为这个对象的引用,保证下次调用这个方法时 7 # 能够知道之前已经创建过对象了,这样就保证了只有1个对象 8 # 注意,此处__new__()方法中的age与name既不是类属性也不是实例属性,虽然在创建对象时需要传参,但无实际作用 9 def __new__(cls, age, name): 10 if not cls.__instance: 11 cls.__instance = object.__new__(cls) 12 return cls.__instance 13 14 15 xiaoming = Singleton(18, "xiaoming") 16 xiaodan = Singleton(18, "xiaodan") 17 # xiaoming与xiaodan实际为同一个对象 18 print(id(xiaoming)) # 46105504 19 print(id(xiaodan)) # 46105504 20 21 xiaoming.age = 19 # 给对象xiaoming添加属性 22 print(xiaodan.age) # 19
创建单例,只执行 1 次 __init__() 方法
1 class Singleton: 2 3 __instance = None 4 __first_init = False 5 6 def __new__(cls, age, name): 7 if not cls.__instance: 8 cls.__instance = object.__new__(cls) 9 return cls.__instance 10 11 def __init__(self, age, name): 12 # 判断是否第一次执行 init 方法 13 if not Singleton.__first_init: 14 self.age = age 15 self.name = name 16 Singleton.__first_init = True 17 18 19 xiaoming = Singleton(18, "xiaoming") 20 xiaodan = Singleton(18, "xiaodan") 21 22 print(id(xiaoming)) # 46105504 23 print(id(xiaodan)) # 46105504 24 25 print(xiaoming.age) # 18 26 print(xiaodan.age) # 18 27 28 xiaoming.age = 19 # 修改xiaoming对象的实例属性 29 print(xiaodan.age) # 19
单例模式的线程安全问题
未加锁版的单例模式,在多线程下,可能会存在线程安全问题,即创建了多个实例。
1 class Singleton: 2 3 __instance = None 4 # 如果__instance没有被赋值过(即为None), 5 # 那么就创建一个对象,并且赋值为这个对象的引用,保证下次调用这个方法时 6 # 能够知道之前已经创建过对象了,这样就保证了只有1个对象 7 8 def __new__(cls): 9 if not cls.__instance: # 此处有可能发生多个线程同时判断了False,从而各自创建实例对象 10 cls.__instance = object.__new__(cls) 11 return cls.__instance
改进:DCL(Double Check Lock,双重检查锁)单例模式
此加锁版的单例模式,可以避免线程安全问题。
1 class Singleton: 2 3 __instance = None 4 # 如果__instance没有被赋值过(即为None), 5 # 那么就创建一个对象,并且赋值为这个对象的引用,保证下次调用这个方法时 6 # 能够知道之前已经创建过对象了,这样就保证了只有1个对象 7 8 def __new__(cls): 9 if not cls.__instance: # 此处的判断,多线程间可各自执行判断,无需争夺锁资源,因此耗时纳秒级别 10 lock.acquire(): 11 if not cls.__instance: # 如果没有外层的if判断,则每次判断都需要各个线程先争夺锁,耗时在微秒级别 12 cls.__instance = object.__new__(cls) 13 lock.release() 14 return cls.__instance