知識點:
紫陽網(wǎng)站建設(shè)公司成都創(chuàng)新互聯(lián),紫陽網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為紫陽1000+提供企業(yè)網(wǎng)站建設(shè)服務。企業(yè)網(wǎng)站搭建\外貿(mào)營銷網(wǎng)站建設(shè)要多少錢,請找那個售后服務好的紫陽做網(wǎng)站的公司定做!
- 所謂單例模式,也就是說不管什么時候我們要確保只有一個對象實例存在。
- 很多情況下,整個系統(tǒng)中只需要存在一個對象,所有的信息都從這個對象獲取,比如系統(tǒng)的配置對象,或者是線程池。
- 這些場景下,就非常適合使用單例模式??偨Y(jié)起來,就是說不管我們初始化一個對象多少次,真正干活的對象只會生成一次并且在首次生成。
# -*- 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):
""" 先判斷類變量中是否已經(jīng)保存了 _A 的實例,如果沒有則創(chuàng)建一個后返回"""
if Singleton._instance is None:
Singleton._instance = Singleton._A()
def __getattr__(self, attr):
""" 所有的屬性都應該直接從 Singleton._instance 獲取"""
return getattr(self._instance, attr)
if __name__ == '__main__':
# 創(chuàng)建兩個實例
s1 = Singleton()
s2 = Singleton()
print(id(s1), s1.display())
print(id(s2), s2.display())
使用類變量 Singleton._instance 來存儲創(chuàng)建的實例,并且保證只會創(chuàng)建一次實例。
由于 Python 是一門動態(tài)語言,我們可以在運行時改變類定義。
在首次初始化Singleton時,我們將首次生成類_A的實例,并將其存儲到 Singleton._instance 中,以后每次初始化 Singleton 時都從 Singleton._instance 獲取真正干活的實例,這樣我們就實現(xiàn)了單例模式。
# -*- coding: utf-8 -*-
class Singleton:
"""
單例類裝飾器,可以用于想實現(xiàn)單例的任何類。注意,不能用于多線程環(huán)境。
"""
def __init__(self, cls):
""" 需要的參數(shù)是一個類 """
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)
# -*- 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 數(shù)據(jù)庫
"""
return sqlite3.connect(current_app.config['SQLITE3_DATABASE'])
def teardown(self, exception):
"""
關(guān)閉 sqlite 鏈接
"""
ctx = stack.top
if hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db.close()
@property
def connection(self):
"""
單例模式在這里:使用 flask._app_ctx_stack 存放 sqlite 鏈接,
每次獲取數(shù)據(jù)庫鏈接時都通過 connection 獲取
"""
ctx = stack.top
if ctx is not None:
if not hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db = self.connect()
return ctx.sqlite3_db
一個函數(shù),傳入需要創(chuàng)建的產(chǎn)品類型,然后返回相應的產(chǎn)品
# -*- coding: utf-8 -*-
import random
class BasicCourse(object):
"""
基礎(chǔ)課程
"""
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):
""" 簡單工廠,用于創(chuàng)建課程"""
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())
上面的簡單工廠模式中,我們遇到了問題:如果需要增加一種課程,那我們需要修改工廠代碼。仔細想想,如果對工廠進行抽象化,讓每個工廠只負責一種產(chǎn)品的生產(chǎn),那這樣當增加一種產(chǎn)品時,就不需要修改已有的工廠了,只需要新增加一個工廠就行了,這樣就避免修改整個工廠了
# -*- coding: utf-8 -*-
import random
import abc
class BasicCourse(object):
"""
基礎(chǔ)課程
"""
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):
"""
基礎(chǔ)課程工廠類
"""
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,分別對應基礎(chǔ)課和項目課。接著,我們創(chuàng)建了一個抽象的工廠 Factory,該工廠有一抽象方法Factory.create_course用于創(chuàng)建課程,最后我們基于抽象工廠實現(xiàn)了生產(chǎn)基礎(chǔ)課程的工廠BasicCourseFactory和生產(chǎn)項目課的工廠ProjectCourseFactory。這樣當我們新增加一種課程時,就不需要修改已經(jīng)存在的基礎(chǔ)課工廠和項目課工廠了。這里需要說明下,我們通過 Python 的abc模塊實現(xiàn)抽象類和抽象方法。
在工廠方法模式中,我們會遇到一個問題,當產(chǎn)品非常多時,繼續(xù)使用工廠方法模式會產(chǎn)生非常多的工廠類。
如果按照工廠方法模式的作法,我們需要創(chuàng)建 Linux 虛擬機工廠類和 Mac 虛擬機工廠類, 這樣我們就會有一堆工廠類了。我們就不能創(chuàng)建出一個能同時創(chuàng)建課程和虛擬機的工廠嗎?
-*- coding: utf-8 -*-
import random
import abc
# 兩種類型的課程
class BasicCourse(object):
"""
基礎(chǔ)課程
"""
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):
"""
抽象工廠類, 現(xiàn)在工廠類不僅能創(chuàng)建課程,還能創(chuàng)建虛擬機了
"""
@abc.abstractmethod
def create_course(self):
pass
@abc.abstractmethod
def create_vm(self):
pass
class BasicCourseLinuxFactory(Factory):
"""
基礎(chǔ)課程工廠類
"""
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())