github地址:https://github.com/cheesezh/python_design_patternspython
有6個客戶想作產品展現網站,其中3個想作整天貓商城那樣的「電商風格」展現頁面,其中3個想作成博客園那樣的「博客風格」展現博客。應該如何實現?git
class WebSite(): def __init__(self, name): self.name = name def use(self): print("網站風格:", self.name) def main(): web1 = WebSite("電商風格") web1.use() web2 = WebSite("電商風格") web2.use() web3 = WebSite("電商風格") web3.use() web4 = WebSite("博客風格") web4.use() web5 = WebSite("博客風格") web5.use() web6 = WebSite("博客風格") web6.use() main()
網站風格: 電商風格 網站風格: 電商風格 網站風格: 電商風格 網站風格: 博客風格 網站風格: 博客風格 網站風格: 博客風格
根據上邊的代碼,若是要作三個「電商風格」,三個「博客風格」的網站,須要六個網站類的實例,而實際上它們本質上都是同樣的代碼,若是網站增多,實例數量也會增多,這對服務器的資源浪費很嚴重。github
如今各個大型博客網站,電子商務網站,每個博客或者商家都是一個小型網站,它們根據用戶ID號的不一樣,來區分不一樣的用戶,具體數據和模版能夠不一樣,可是代碼核心和數據庫倒是共享的。web
這就須要用到享元模式。數據庫
享元模式,運用共享技術有效的支持大量細粒度的對象。主要包括如下幾個類:服務器
from abc import ABCMeta, abstractmethod class Flyweight(): """ Flyweight類,它是全部具體享元類的超類或接口,經過這個接口,Flyweight能夠接受並做用於外部狀態。 """ __metaclass__ = ABCMeta @abstractmethod def operation(self, extrinsicstate): pass class ConcreteFlyweight(Flyweight): """ ConcreteFlyweight是繼承Flyweight超類或者是想Flyweight接口,併爲內部狀態增長存儲空間 """ def operation(self, extrinsicstate): print("具體Flyweight:", extrinsicstate) class UnsharedConcreteFlyweight(Flyweight): """ UnsharedConcreteFlyweight是指那些不須要共享的Flyweight子類。由於Flyweight接口,共享成爲可能,但它並不強制共享 """ def operation(self, extrinsicstate): print("不共享的具體Flyweight:", extrinsicstate) class FlyweightFactory(): """ FlyweightFactory是一個享元工廠,用來建立並管理Flyweight對象。它主要是用來確保合理地共享Flyweight,當用戶請求一個 Flyweight時,FlyweightFactory對象提供一個已建立的實例或者建立一個(若是不存在的話)。 """ def __init__(self): self.flyweights = dict() self.flyweights['X'] = ConcreteFlyweight() self.flyweights['Y'] = ConcreteFlyweight() self.flyweights['Z'] = ConcreteFlyweight() def get_flyweight(self, key): return self.flyweights[key] def main(): # 代碼外部狀態 extrinsicstate = 22 f = FlyweightFactory() fx = f.get_flyweight("X") extrinsicstate -= 1 fx.operation(extrinsicstate) fy = f.get_flyweight("Y") extrinsicstate -= 1 fy.operation(extrinsicstate) fz = f.get_flyweight("Z") extrinsicstate -= 1 fy.operation(extrinsicstate) uf = UnsharedConcreteFlyweight() extrinsicstate -= 1 uf.operation(extrinsicstate) main()
具體Flyweight: 21 具體Flyweight: 20 具體Flyweight: 19 不共享的具體Flyweight: 18
上述代碼中,FlyweightFactory根據客戶需求返回早已生成好的對象,可是實際上不必定須要,徹底能夠初始化時什麼也不作,到須要時,再判斷對象是否爲null來決定是否實例化;網站
爲何會有UnsharedConcreteFlyweight存在呢?這是由於儘管咱們大部分時間都須要共享對象來下降內存損耗,但個別時候也有可能不須要共享的,那麼此時的UnsharedConcreteFlyweight就有存在的必要了,它能夠解決那些不須要共享對象的問題。設計
from abc import ABCMeta, abstractmethod class WebSite(): """ 網站抽象類:Flyweight類,它是全部具體享元類的超類或接口,經過這個接口,Flyweight能夠接受並做用於外部狀態。 """ __metaclass__ = ABCMeta @abstractmethod def use(self): pass class ConcreteWebSite(WebSite): """ 具體網站類:ConcreteFlyweight是繼承Flyweight超類或者是想Flyweight接口,併爲內部狀態增長存儲空間 """ def __init__(self, name): self.name = name def use(self): print("網站風格:", self.name) class WebSiteFactory(): """ 網站工廠類:FlyweightFactory是一個享元工廠,用來建立並管理Flyweight對象。它主要是用來確保合理地共享Flyweight, 當用戶請求一個Flyweight時,FlyweightFactory對象提供一個已建立的實例或者建立一個(若是不存在的話)。 """ def __init__(self): self.flyweights = dict() def get_website(self, key): if key not in self.flyweights: self.flyweights[key] = ConcreteWebSite(key) return self.flyweights[key] def main(): f = WebSiteFactory() fx = f.get_website("電商風格") fx.use() fy = f.get_website("電商風格") fy.use() fz = f.get_website("電商風格") fz.use() fa = f.get_website("博客風格") fa.use() fb = f.get_website("博客風格") fb.use() fc = f.get_website("博客風格") fc.use() print("網站風格總數:", len(f.flyweights)) main()
網站風格: 電商風格 網站風格: 電商風格 網站風格: 電商風格 網站風格: 博客風格 網站風格: 博客風格 網站風格: 博客風格 網站風格總數: 2
這樣寫基本實現了享元模式共享對象的目的,也就是,不管建立多少個網站,只要是「電商風格」,那就都同樣,只要是「博客風格」,也都同樣。可是給不一樣企業建立網站,它們的數據確定會不一樣,因此上述代碼沒有體香對象間的不一樣,只體現了共享的部分。code
在享元對象內部而且不會隨環境改變而改變的共享部分,能夠成爲是享元對象的內部狀態;對象
隨着環境改變而改變,不可共享的狀態就是享元對象的外部狀態;
享元模式能夠避免大量很是類似類的開銷。在程序設計中,有時須要生成大量細粒度的類實例來表示數據。若是能發現這些實例除了幾個參數外基本上都是相同的,有時候就可以大幅度地減小須要實例化的類的數量。若是能把那些參數轉移到類實例的外面,在方法調用時將它們傳遞進來,就能夠經過共享大幅度地減小單個實例的數目。
享元模式Flyweight執行時所需的狀態是有內部的,也可能有外部的,內部狀態存儲於ConcreteFlyweight對象之中,而外部狀態則應該考慮有客戶端對象存儲或計算,當調用Flyweight對象的操做時,將該狀態傳遞給它。
在網站的例子中,客戶帳號就是外部狀態,應該由專門的對象來處理。
from abc import ABCMeta, abstractmethod class User(): """ 用戶類,網站的客戶帳號,是「網站類」的外部狀態 """ def __init__(self, name): self.name = name class WebSite(): """ 網站抽象類:Flyweight類,它是全部具體享元類的超類或接口,經過這個接口,Flyweight能夠接受並做用於外部狀態。 """ __metaclass__ = ABCMeta @abstractmethod def use(self, user): pass class ConcreteWebSite(WebSite): """ 具體網站類:ConcreteFlyweight是繼承Flyweight超類或者是想Flyweight接口,併爲內部狀態增長存儲空間 """ def __init__(self, name): self.name = name def use(self, user): print(user.name, "- 網站風格:", self.name) class WebSiteFactory(): """ 網站工廠類:FlyweightFactory是一個享元工廠,用來建立並管理Flyweight對象。它主要是用來確保合理地共享Flyweight, 當用戶請求一個Flyweight時,FlyweightFactory對象提供一個已建立的實例或者建立一個(若是不存在的話)。 """ def __init__(self): self.flyweights = dict() def get_website(self, key): if key not in self.flyweights: self.flyweights[key] = ConcreteWebSite(key) return self.flyweights[key] def main(): f = WebSiteFactory() fx = f.get_website("電商風格") fx.use(User("賀賀")) fy = f.get_website("電商風格") fy.use(User("曼曼")) fz = f.get_website("電商風格") fz.use(User("云云")) fa = f.get_website("博客風格") fa.use(User("靈靈")) fb = f.get_website("博客風格") fb.use(User("依依")) fc = f.get_website("博客風格") fc.use(User("靈依")) print("網站風格總數:", len(f.flyweights)) main()
賀賀 - 網站風格: 電商風格 曼曼 - 網站風格: 電商風格 云云 - 網站風格: 電商風格 靈靈 - 網站風格: 博客風格 依依 - 網站風格: 博客風格 靈依 - 網站風格: 博客風格 網站風格總數: 2
何時須要考慮使用享元模式呢?
在實際使用中,享元模式到底能達到什麼效果呢?
由於使用了享元模式,因此有了共享對象,實例總數就大大減小了,若是共享的對象越多,存儲節約也就越多,節約量隨着共享狀態的增多而增大。
須要注意的是,享元模式須要維護一個記錄了系統已有的全部享元的列表,而這自己須要耗費資源,另外享元模式會使系統變得更加複雜。