在之前文章(Python从菜鸟到高手,这些功能强大的技巧要掌握)中,我们已介绍过Python作为一门编程类语言,会有一些功能强大但不易理解、不易掌握的用法。本篇文章将介绍两种在学Python过程有点懵逼、却必须理解和掌握的,同时也是在面试过程中经常被问及的设计模式,在阅读过程中如果感觉不易理解,建议先收藏,慢慢体会,有疑问请留言区留言。
很多初学者喜欢用全局变量,因为这比函数的参数传来传去更容易让人理解。确实在很多场景下用全局变量很方便。不过如果代码规模增大,并且有多个文件的时候,全局变量就会变得比较混乱,你可能不知道在哪个文件中定义了相同类型甚至重名的全局变量,对于这种情况,有种更好的实现方式:单例(Singleton)
单例模式
单例是一种设计模式,应用该模式的类只会生成一个实例。单例模式保证了在程序的不同位置都可以且仅可以取到同一个对象实例:如果实例不存在,会创建一个实例;如果已存在就会返回这个实例。
我们在使用class创建类的时候, 只会创建一个类对象, 但是, 当我们实例化这个类对象的时候, 一个类对象, 可以实例化出很多不同的对象, 而我们每次实例化出来一个对象, 就会在内存中重新分配一块空间, 而今天介绍的单例模式, 就是为了解决上述问题, 使得由一个类对象所实例化出来的全部对象都指向同一块内存空间。那么这有什么好处呢?
单例模式的优点:
- 由于单例模式要求在全局内只有一个实例,因而可以节省比较多的内存空间;
- 全局只有一个接入点,可以更好地进行数据同步控制,避免多重占用;
- 单例可长驻内存,减少系统开销。
单例模式的应用场景:
- 生成全局惟一的序列号;
- 访问全局复用的惟一资源,如磁盘、总线等;
- 单个对象占用的资源过多,如数据库等;
- 系统全局统一管理,如Windows下的Task Manager;
- 网站计数器。
代码示例
class python_learning(object):
instant = None
flag = True
def __new__(cls, *args, **kwargs):
if cls.instant is None:
cls.instant = super().__new__(cls)
return cls.instant
def __init__(self):
if not B.flag:
return
self.name = 'Python学习与数据挖掘'
B.flag = False
print('已经被初始化了')
d = python_learning()
print('d对象所在的内存地址是 %d, B类所在的内存地址是 %d' % (id(d), id(python_learning)))
e = python_learning()
print('e对象所在的内存地址是 %d, B类所在的内存地址是 %d' % (id(e), id(python_learning)))
f = python_learning()
print('f对象所在的内存地址是 %d, B类所在的内存地址是 %d' % (id(f), id(python_learning)))
结果输出
d对象所在的内存地址是 4434814456, python_learning类所在的内存地址是 140483145268616
e对象所在的内存地址是 4434814456, python_learning类所在的内存地址是 140483145268616
f对象所在的内存地址是 4434814456, python_learning类所在的内存地址是 140483145268616
上述是基于__new__方法实现的单例模式,一个对象的实例化过程是先执行类的__new__方法,如果我们没有写,默认会调用object的__new__方法,返回一个实例化对象,然后再调用__init__方法,对这个对象进行初始化。
工厂模式
想必大家一定见过类似于麦当劳自助点餐台一类的点餐系统吧。在一个大的触摸显示屏上,有三类可以选择的上餐品:汉堡等主餐、小食、饮料。当我们选择好自己需要的食物,支付完成后,订单就生成了。下面我们用今天的主角--工厂模式--来生成这些食物的逻辑主体。
首先,来看主餐的生成(仅以两种汉堡为例)。
class Burger():
name=""
price=0.0
def getPrice(self):
return self.price
def setPrice(self,price):
self.price=price
def getName(self):
return self.name
class cheeseBurger(Burger):
def __init__(self):
self.name="cheese burger"
self.price=10.0
class spicyChickenBurger(Burger):
def __init__(self):
self.name="spicy chicken burger"
self.price=15.0
其次,是小食
class Snack():
name = ""
price = 0.0
type = "SNACK"
def getPrice(self):
return self.price
def setPrice(self, price):
self.price = price
def getName(self):
return self.name
class chips(Snack):
def __init__(self):
self.name = "chips"
self.price = 6.0
class chickenWings(Snack):
def __init__(self):
self.name = "chicken wings"
self.price = 12.0
最后,是饮料
class Beverage():
name = ""
price = 0.0
type = "BEVERAGE"
def getPrice(self):
return self.price
def setPrice(self, price):
self.price = price
def getName(self):
return self.name
class coke(Beverage):
def __init__(self):
self.name = "coke"
self.price = 4.0
class milk(Beverage):
def __init__(self):
self.name = "milk"
self.price = 5.0
以上的Burger,Snack,Beverage,都可以认为是该快餐店的产品,由于只提供了抽象方法,我们把它们叫抽象产品类,而cheese burger等6个由抽象产品类衍生出的子类,叫作具体产品类。接下来,“工厂”就要出现了。
class foodFactory():
type=""
def createFood(self,foodClass):
print(self.type," factory produce a instance.")
foodIns=foodClass()
return foodIns
class burgerFactory(foodFactory):
def __init__(self):
self.type="BURGER"
class snackFactory(foodFactory):
def __init__(self):
self.type="SNACK"
class beverageFactory(foodFactory):
def __init__(self):
self.type="BEVERAGE"
同样,foodFactory为抽象的工厂类,而burgerFactory,snackFactory,beverageFactory为具体的工厂类。在业务场景中,工厂模式是如何“生产”产品的呢?
if __name__=="__main__":
burger_factory=burgerFactory()
snack_factorry=snackFactory()
beverage_factory=beverageFactory()
cheese_burger=burger_factory.createFood(cheeseBurger)
print(cheese_burger.getName(),cheese_burger.getPrice())
chicken_wings=snack_factorry.createFood(chickenWings)
print(chicken_wings.getName(),chicken_wings.getPrice())
coke_drink=beverage_factory.createFood(coke)
print(coke_drink.getName(),coke_drink.getPrice())
可见,业务中先生成了工厂,然后用工厂中的createFood方法和对应的参数直接生成产品实例。打印结果如下:
BURGER factory produce a instance.
cheese burger 10.0
SNACK factory produce a instance.
chicken wings 12.0
BEVERAGE factory produce a instance.
coke 4.0
工厂模式模式的优点:
- 工厂模式巨有非常好的封装性,代码结构清晰;在抽象工厂模式中,其结构还可以随着需要进行更深或者更浅的抽象层级调整,非常灵活;
- 屏蔽产品类,使产品的被使用业务场景和产品的功能细节可以分而开发进行,是比较典型的解耦框架。
工厂模式的使用场景:
当系统实例要求比较灵活和可扩展时,可以考虑工厂模式或者抽象工厂模式实现。比如,
在通信系统中,高层通信协议会很多样化,同时,上层协议依赖于下层协议,那么就可以对应建立对应层级的抽象工厂,根据不同的“产品需求”去生产定制的实例。