Python設計模式-創建型:單例模式和工廠模式家族 知識點: 單例模式概念及一般實現 單例模式的裝飾器實現 簡單工廠模式 抽象工廠模式 單例模式(singleton) 所謂單例模式,也就是說不管什麼時候我們要確保只有一個對象實例存在。 很多情況下,整個系統中只需要存在一個對象,所有的信息都從這個對 ...
Python設計模式-創建型:單例模式和工廠模式家族
知識點:
- 單例模式概念及一般實現
- 單例模式的裝飾器實現
- 簡單工廠模式
- 抽象工廠模式
單例模式(singleton)
- 所謂單例模式,也就是說不管什麼時候我們要確保只有一個對象實例存在。
- 很多情況下,整個系統中只需要存在一個對象,所有的信息都從這個對象獲取,比如系統的配置對象,或者是線程池。
- 這些場景下,就非常適合使用單例模式。總結起來,就是說不管我們初始化一個對象多少次,真正幹活的對象只會生成一次並且在首次生成。
Singleton._instance
# -*- coding: utf-8 -*-
class Singleton(object):
"""
單例模式
"""
class _A(object):
"""
真正幹活的類, 對外隱藏
"""
def __init__(self):
pass
def display(self):
""" 返回當前實例的 ID,是全局唯一的"""
return id(self)
# 類變數,用於存儲 _A 的實例
_instance = None
def __init__(self):
""" 先判斷類變數中是否已經保存了 _A 的實例,如果沒有則創建一個後返回"""
if Singleton._instance is None:
Singleton._instance = Singleton._A()
def __getattr__(self, attr):
""" 所有的屬性都應該直接從 Singleton._instance 獲取"""
return getattr(self._instance, attr)
if __name__ == '__main__':
# 創建兩個實例
s1 = Singleton()
s2 = Singleton()
print(id(s1), s1.display())
print(id(s2), s2.display())
-
使用類變數 Singleton._instance 來存儲創建的實例,並且保證只會創建一次實例。
-
由於 Python 是一門動態語言,我們可以在運行時改變類定義。
-
在首次初始化Singleton時,我們將首次生成類_A的實例,並將其存儲到 Singleton._instance 中,以後每次初始化 Singleton 時都從 Singleton._instance 獲取真正幹活的實例,這樣我們就實現了單例模式。
裝飾器
# -*- coding: utf-8 -*-
class Singleton:
"""
單例類裝飾器,可以用於想實現單例的任何類。註意,不能用於多線程環境。
"""
def __init__(self, cls):
""" 需要的參數是一個類 """
self._cls = cls
def Instance(self):
"""
返回真正的實例
"""
try:
return self._instance
except AttributeError:
self._instance = self._cls()
return self._instance
def __call__(self):
raise TypeError('Singletons must be accessed through `Instance()`.')
def __instancecheck__(self, inst):
return isinstance(inst, self._decorated)
# 裝飾器
@Singleton
class A:
"""一個需要單例模式的類"""
def __init__(self):
pass
def display(self):
return id(self)
if __name__ == '__main__':
s1 = A.Instance()
s2 = A.Instance()
print(s1, s1.display())
print(s2, s2.display())
print(s1 is s2)
- 用裝飾器實現了單例模式,任何想使用單例模式的類,只需要使用 Singleton 裝飾器裝飾一下就可以使用了。
- 可以看到其核心工作原理其實和第一種實現方式是一致的,也是使用內置的屬性 Singleton._instance 來存儲實例的。通過使用裝飾器的模式我們將代碼解耦了,使用更加靈活
案例-單例模式-連接sqlite3資料庫
# -*- coding: utf-8 -*-
import sqlite3
from flask import current_app
from flask import _app_ctx_stack as stack
class SQLite3(object):
def __init__(self, app=None):
self.app = app
if app is not None:
self.init_app(app)
def init_app(self, app):
"""
典型的 Flask 擴展的初始化方式
"""
app.config.setdefault('SQLITE3_DATABASE', ':memory:')
app.teardown_appcontext(self.teardown)
def connect(self):
"""
連接到 sqlite 資料庫
"""
return sqlite3.connect(current_app.config['SQLITE3_DATABASE'])
def teardown(self, exception):
"""
關閉 sqlite 鏈接
"""
ctx = stack.top
if hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db.close()
@property
def connection(self):
"""
單例模式在這裡:使用 flask._app_ctx_stack 存放 sqlite 鏈接,
每次獲取資料庫鏈接時都通過 connection 獲取
"""
ctx = stack.top
if ctx is not None:
if not hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db = self.connect()
return ctx.sqlite3_db
簡單工廠模式 Simple factory
一個函數,傳入需要創建的產品類型,然後返回相應的產品
# -*- coding: utf-8 -*-
import random
class BasicCourse(object):
"""
基礎課程
"""
def get_labs(self):
return "basic_course: labs"
def __str__(self):
return "BasciCourse"
class ProjectCourse(object):
"""
項目課
"""
def get_labs(self):
return "project_course: labs"
def __str__(self):
return "ProjectCourse"
class SimpleCourseFactory(object):
@staticmethod
def create_course(type):
""" 簡單工廠,用於創建課程"""
if type == 'bc':
return BasicCourse()
elif type == 'pc':
return ProjectCourse()
if __name__ == '__main__':
t = random.choice(['bc', 'pc'])
course = SimpleCourseFactory.create_course(t)
print(course.get_labs())
工廠方法模式 factory method
上面的簡單工廠模式中,我們遇到了問題:如果需要增加一種課程,那我們需要修改工廠代碼。仔細想想,如果對工廠進行抽象化,讓每個工廠只負責一種產品的生產,那這樣當增加一種產品時,就不需要修改已有的工廠了,只需要新增加一個工廠就行了,這樣就避免修改整個工廠了
# -*- coding: utf-8 -*-
import random
import abc
class BasicCourse(object):
"""
基礎課程
"""
def get_labs(self):
return "basic_course: labs"
def __str__(self):
return "BasicCourse"
class ProjectCourse(object):
"""
項目課
"""
def get_labs(self):
return "project_course: labs"
def __str__(self):
return "ProjectCourse"
class Factory(metaclass=abc.ABCMeta):
"""
抽象工廠類
"""
@abc.abstractmethod
def create_course(self):
pass
class BasicCourseFactory(Factory):
"""
基礎課程工廠類
"""
def create_course(self):
return BasicCourse()
class ProjectCourseFactory(Factory):
"""
項目課程工廠類
"""
def create_course(self):
return ProjectCourse()
def get_factory():
"""
隨機獲取一個工廠類
"""
return random.choice([BasicCourseFactory, ProjectCourseFactory])()
if __name__ == '__main__':
factory = get_factory()
course = factory.create_course()
print(course.get_labs())
我們有兩種課程:BasicCourse 和 ProjectCourse,分別對應基礎課和項目課。接著,我們創建了一個抽象的工廠 Factory,該工廠有一抽象方法Factory.create_course用於創建課程,最後我們基於抽象工廠實現了生產基礎課程的工廠BasicCourseFactory和生產項目課的工廠ProjectCourseFactory。這樣當我們新增加一種課程時,就不需要修改已經存在的基礎課工廠和項目課工廠了。這裡需要說明下,我們通過 Python 的abc模塊實現抽象類和抽象方法。
抽象工廠模式
在工廠方法模式中,我們會遇到一個問題,當產品非常多時,繼續使用工廠方法模式會產生非常多的工廠類。
如果按照工廠方法模式的作法,我們需要創建 Linux 虛擬機工廠類和 Mac 虛擬機工廠類, 這樣我們就會有一堆工廠類了。我們就不能創建出一個能同時創建課程和虛擬機的工廠嗎?
-*- coding: utf-8 -*-
import random
import abc
# 兩種類型的課程
class BasicCourse(object):
"""
基礎課程
"""
def get_labs(self):
return "basic_course: labs"
def __str__(self):
return "BasicCourse"
class ProjectCourse(object):
"""
項目課
"""
def get_labs(self):
return "project_course: labs"
def __str__(self):
return "ProjectCourse"
# 兩種類型的虛擬機
class LinuxVm(object):
"""
Linux 虛擬機
"""
def start(self):
return "Linux vm running"
class MacVm(object):
"""
Mac OSX 虛擬機
"""
def start(self):
return "Mac OSX vm running"
class Factory(metaclass=abc.ABCMeta):
"""
抽象工廠類, 現在工廠類不僅能創建課程,還能創建虛擬機了
"""
@abc.abstractmethod
def create_course(self):
pass
@abc.abstractmethod
def create_vm(self):
pass
class BasicCourseLinuxFactory(Factory):
"""
基礎課程工廠類
"""
def create_course(self):
return BasicCourse()
def create_vm(self):
return LinuxVm()
class ProjectCourseMacFactory(Factory):
"""
項目課程工廠類
"""
def create_course(self):
return ProjectCourse()
def create_vm(self):
return MacVm()
def get_factory():
"""
隨機獲取一個工廠類
"""
return random.choice([BasicCourseLinuxFactory, ProjectCourseMacFactory])()
if __name__ == '__main__':
factory = get_factory()
course = factory.create_course()
vm = factory.create_vm()
print(course.get_labs())
print(vm.start())
本文來自博客園,作者:OCEANEYES.GZY,轉載請註明原文鏈接:https://www.cnblogs.com/oceaneyes-gzy/p/16462976.html