zoukankan      html  css  js  c++  java
  • [Python设计模式] 第26章 千人千面,内在共享——享元模式

    github地址:https://github.com/cheesezh/python_design_patterns

    背景

    有6个客户想做产品展示网站,其中3个想做成天猫商城那样的“电商风格”展示页面,其中3个想做成博客园那样的“博客风格”展示博客。应该如何实现?

    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()
    
    网站风格: 电商风格
    网站风格: 电商风格
    网站风格: 电商风格
    网站风格: 博客风格
    网站风格: 博客风格
    网站风格: 博客风格
    

    点评

    根据上边的代码,如果要做三个“电商风格”,三个“博客风格”的网站,需要六个网站类的实例,而实际上它们本质上都是一样的代码,如果网站增多,实例数量也会增多,这对服务器的资源浪费很严重。

    现在各个大型博客网站,电子商务网站,每一个博客或者商家都是一个小型网站,它们根据用户ID号的不同,来区分不同的用户,具体数据和模版可以不同,但是代码核心和数据库却是共享的。

    这就需要用到享元模式。

    享元模式

    享元模式,运用共享技术有效的支持大量细粒度的对象。主要包括以下几个类:

    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
    

    点评

    这样写基本实现了享元模式共享对象的目的,也就是,无论创建多少个网站,只要是“电商风格”,那就都一样,只要是“博客风格”,也都一样。但是给不同企业创建网站,它们的数据肯定会不同,所以上述代码没有体香对象间的不同,只体现了共享的部分。

    内部状态和外部状态

    在享元对象内部并且不会随环境改变而改变的共享部分,可以成为是享元对象的内部状态;

    随着环境改变而改变,不可共享的状态就是享元对象的外部状态;

    享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时候就能够大幅度地减少需要实例化的类的数量。如果能把那些参数转移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。

    享元模式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
    

    总结

    什么时候需要考虑使用享元模式呢?

    • 如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用
    • 对象的大多数状态都是内部状态,如果可以删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象

    在实际使用中,享元模式到底能达到什么效果呢?

    因为使用了享元模式,所以有了共享对象,实例总数就大大减少了,如果共享的对象越多,存储节约也就越多,节约量随着共享状态的增多而增大。

    需要注意的是,享元模式需要维护一个记录了系统已有的所有享元的列表,而这本身需要耗费资源,另外享元模式会使系统变得更加复杂。

  • 相关阅读:
    Ie console未定义
    jquery bind和live区别
    php insert 空
    ie8及以下不支持getElemlentsByClassName
    jquery ajax parseerror
    你的灯亮着吗——发现问题的真正所在(摘录)
    转:getElementById引起的jQuery的选择器bug
    JS判断是否为数字、JS判断是否为整数、JS判断是否为浮点数
    Microsoft .NET Framework 4 (Standalone Installer)
    Not enough memory is available to complete this request
  • 原文地址:https://www.cnblogs.com/CheeseZH/p/9491322.html
Copyright © 2011-2022 走看看