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
    

    总结

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

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

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

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

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

  • 相关阅读:
    解决xcode5升级后,Undefined symbols for architecture arm64:问题
    第8章 Foundation Kit介绍
    app 之间发送文件 ios
    iphone怎么检测屏幕是否被点亮 (用UIApplication的Delegate)
    CRM下载对象一直处于Wait状态的原因
    错误消息Customer classification does not exist when downloading
    How to resolve error message Distribution channel is not allowed for sales
    ABAP CCDEF, CCIMP, CCMAC, CCAU, CMXXX这些东东是什么鬼
    有了Debug权限就能干坏事?小心了,你的一举一动尽在系统监控中
    SAP GUI和Windows注册表
  • 原文地址:https://www.cnblogs.com/CheeseZH/p/9491322.html
Copyright © 2011-2022 走看看