zoukankan      html  css  js  c++  java
  • 创建类设计模式

    单例模式

    一、总线

    总线是计算机各种功能部件或者设备之间传送数据、控制信号等信息的公共通信解决方案之一。现假设有如下场景:某中央处理器(CPU)通过某种协议总线与一个信号灯相连,信号灯有64种颜色可以设置,中央处理器上运行着三个线程,都可以对这个信号灯进行控制,并且可以独立设置该信号灯的颜色。抽象掉协议细节(用打印表示),如何实现线程对信号等的控制逻辑。
    加线程锁进行控制,无疑是最先想到的方法,但各个线程对锁的控制,无疑加大了模块之间的耦合。下面,我们就用设计模式中的单例模式,来解决这个问题。
    什么是单例模式?单例模式是指:保证一个类仅有一个实例,并提供一个访问它的全局访问点。具体到此例中,总线对象,就是一个单例,它仅有一个实例,各个线程对总线的访问只有一个全局访问点,即惟一的实例。


    Python代码如下:

    复制代码
    #encoding=utf8
    import threading
    import time
    #这里使用方法__new__来实现单例模式
    class Singleton(object):#抽象单例
        def __new__(cls, *args, **kw):
            if not hasattr(cls, '_instance'):
                orig = super(Singleton, cls)
                cls._instance = orig.__new__(cls, *args, **kw)
            return cls._instance
    #总线
    class Bus(Singleton):
        lock = threading.RLock()
        def sendData(self,data):
            self.lock.acquire()
            time.sleep(3)
            print "Sending Signal Data...",data
            self.lock.release()
    #线程对象,为更加说明单例的含义,这里将Bus对象实例化写在了run里
    class VisitEntity(threading.Thread):
        my_bus=""
        name=""
        def getName(self):
            return self.name
        def setName(self, name):
            self.name=name
        def run(self):
            self.my_bus=Bus()
            self.my_bus.sendData(self.name)
    
    if  __name__=="__main__":
        for i in range(3):
            print "Entity %d begin to run..."%i
            my_entity=VisitEntity()
            my_entity.setName("Entity_"+str(i))
            my_entity.start()
    复制代码

    运行结果如下:
    Entity 0 begin to run...
    Entity 1 begin to run...
    Entity 2 begin to run...
    Sending Signal Data... Entity_0
    Sending Signal Data... Entity_1
    Sending Signal Data... Entity_2
    在程序运行过程中,三个线程同时运行(运行结果的前三行先很快打印出来),而后分别占用总线资源(后三行每隔3秒打印一行)。虽然看上去总线Bus被实例化了三次,但实际上在内存里只有一个实例。

    二、单例模式

    单例模式是所有设计模式中比较简单的一类,其定义如下:Ensure a class has only one instance, and provide a global point of access to it.(保证某一个类只有一个实例,而且在全局只有一个访问点)


    sgl.png

    三、单例模式的优点和应用

    单例模式的优点

    1、由于单例模式要求在全局内只有一个实例,因而可以节省比较多的内存空间;
    2、全局只有一个接入点,可以更好地进行数据同步控制,避免多重占用;
    3、单例可长驻内存,减少系统开销。

    单例模式的应用举例

    1、生成全局惟一的序列号;
    2、访问全局复用的惟一资源,如磁盘、总线等;
    3、单个对象占用的资源过多,如数据库等;
    4、系统全局统一管理,如Windows下的Task Manager;
    5、网站计数器。

    四、单例模式的缺点

    1、单例模式的扩展是比较困难的;
    2、赋于了单例以太多的职责,某种程度上违反单一职责原则(六大原则后面会讲到);
    3、单例模式是并发协作软件模块中需要最先完成的,因而其不利于测试;
    4、单例模式在某种情况下会导致“资源瓶颈”。

    工厂类相关模式

    一、快餐点餐系统

    想必大家一定见过类似于麦当劳自助点餐台一类的点餐系统吧。在一个大的触摸显示屏上,有三类可以选择的上餐品:汉堡等主餐、小食、饮料。当我们选择好自己需要的食物,支付完成后,订单就生成了。下面,我们用今天的主角--工厂模式--来生成这些食物的逻辑主体。
    首先,来看主餐的生成(仅以两种汉堡为例)。

    复制代码
    class Burger():
        name=""
        price=0.0
        def getPrice(self):
            return self.price
        def setPrice(self,price):
            self.price=price
        def getName(self):
            return self.name
    class cheeseBurger(Burger):
        def __init__(self):
            self.name="cheese burger"
            self.price=10.0
    class spicyChickenBurger(Burger):
        def __init__(self):
            self.name="spicy chicken burger"
            self.price=15.0
    复制代码

    其次,是小食。(内容基本一致)

    复制代码
    class Snack():
        name = ""
        price = 0.0
        type = "SNACK"
        def getPrice(self):
            return self.price
        def setPrice(self, price):
            self.price = price
        def getName(self):
            return self.name
    
    
    class chips(Snack):
        def __init__(self):
            self.name = "chips"
            self.price = 6.0
    
    
    class chickenWings(Snack):
        def __init__(self):
            self.name = "chicken wings"
            self.price = 12.0
    复制代码

    最后,是饮料。

    复制代码
    class Beverage():
        name = ""
        price = 0.0
        type = "BEVERAGE"
        def getPrice(self):
            return self.price
        def setPrice(self, price):
            self.price = price
        def getName(self):
            return self.name
    
    
    class coke(Beverage):
        def __init__(self):
            self.name = "coke"
            self.price = 4.0
    
    
    class milk(Beverage):
        def __init__(self):
            self.name = "milk"
            self.price = 5.0
    复制代码

    以上的Burger,Snack,Beverage,都可以认为是该快餐店的产品,由于只提供了抽象方法,我们把它们叫抽象产品类,而cheese burger等6个由抽象产品类衍生出的子类,叫作具体产品类。
    接下来,“工厂”就要出现了。

    复制代码
    class foodFactory():
        type=""
        def createFood(self,foodClass):
            print self.type," factory produce a instance."
            foodIns=foodClass()
            return foodIns
    class burgerFactory(foodFactory):
        def __init__(self):
            self.type="BURGER"
    class snackFactory(foodFactory):
        def __init__(self):
            self.type="SNACK"
    class beverageFactory(foodFactory):
        def __init__(self):
            self.type="BEVERAGE"
    复制代码

    同样,foodFactory为抽象的工厂类,而burgerFactory,snackFactory,beverageFactory为具体的工厂类。
    在业务场景中,工厂模式是如何“生产”产品的呢?

    复制代码
    if  __name__=="__main__":
        burger_factory=burgerFactory()
        snack_factorry=snackFactory()
        beverage_factory=beverageFactory()
        cheese_burger=burger_factory.createFood(cheeseBurger)
        print cheese_burger.getName(),cheese_burger.getPrice()
        chicken_wings=snack_factorry.createFood(chickenWings)
        print chicken_wings.getName(),chicken_wings.getPrice()
        coke_drink=beverage_factory.createFood(coke)
        print coke_drink.getName(),coke_drink.getPrice()
    复制代码

    可见,业务中先生成了工厂,然后用工厂中的createFood方法和对应的参数直接生成产品实例。
    打印结果如下:
    BURGER factory produce a instance.
    cheese burger 10.0
    SNACK factory produce a instance.
    chicken wings 12.0
    BEVERAGE factory produce a instance.
    coke 4.0

    二、工厂模式、简单工厂模式、抽象工厂模式

    工厂模式的定义如下:定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。其通用类图如下。其产品类定义产品的公共属性和接口,工厂类定义产品实例化的“方式”。
    f1.pngf2.png
    在上述例子中,工厂在使用前必须实例化。如果,把工厂加个类方法,写成如下形式:

    复制代码
    class simpleFoodFactory():
        @classmethod
        def createFood(cls,foodClass):
            print "Simple factory produce a instance."
            foodIns = foodClass()
            return foodIns
    复制代码

    在场景中写成如下形式:
    spicy_chicken_burger=simpleFoodFactory.createFood(spicyChickenBurger)
    这样,省去了将工厂实例化的过程。这种模式就叫做简单工厂模式。
    还是在上述例子中,createFood方法中必须传入foodClass才可以指定生成的food实例种类,如果,将每一个细致的产品都建立对应的工厂(如cheeseBurger建立对应一个cheeseBurgerFactory),这样,生成食物时,foodClass也不必指定。事实上,此时,burgerFactory就是具体食物工厂的一层抽象。这种模式,就是抽象工厂模式。

    三、工厂模式的优点和应用

    工厂模式、抽象工厂模式的优点:
    1、工厂模式巨有非常好的封装性,代码结构清晰;在抽象工厂模式中,其结构还可以随着需要进行更深或者更浅的抽象层级调整,非常灵活;
    2、屏蔽产品类,使产品的被使用业务场景和产品的功能细节可以分而开发进行,是比较典型的解耦框架。
    工厂模式、抽象工厂模式的使用场景:
    1、当系统实例要求比较灵活和可扩展时,可以考虑工厂模式或者抽象工厂模式实现。比如,
    在通信系统中,高层通信协议会很多样化,同时,上层协议依赖于下层协议,那么就可以对应建立对应层级的抽象工厂,根据不同的“产品需求”去生产定制的实例。

    四、工厂类模式的不足

    1、工厂模式相对于直接生成实例过程要复杂一些,所以,在小项目中,可以不使用工厂模式;
    2、抽象工厂模式中,产品类的扩展比较麻烦。毕竟,每一个工厂对应每一类产品,产品扩展,就意味着相应的抽象工厂也要扩展

     

    建造者模式

    一、快餐点餐系统

    今天的例子,还是上一次谈到的快餐点餐系统。只不过,今天我们从订单的角度来构造这个系统。
    最先还是有请上次的主角们:
    主餐:

    复制代码
    class Burger():
        name=""
        price=0.0
        def getPrice(self):
            return self.price
        def setPrice(self,price):
            self.price=price
        def getName(self):
            return self.name
    class cheeseBurger(Burger):
        def __init__(self):
            self.name="cheese burger"
            self.price=10.0
    class spicyChickenBurger(Burger):
        def __init__(self):
            self.name="spicy chicken burger"
            self.price=15.0
    复制代码

    小食:

    复制代码
    class Snack():
        name = ""
        price = 0.0
        type = "SNACK"
        def getPrice(self):
            return self.price
        def setPrice(self, price):
            self.price = price
        def getName(self):
            return self.name
    
    
    class chips(Snack):
        def __init__(self):
            self.name = "chips"
            self.price = 6.0
    
    
    class chickenWings(Snack):
        def __init__(self):
            self.name = "chicken wings"
            self.price = 12.0
    复制代码

    饮料:

    复制代码
    class Beverage():
        name = ""
        price = 0.0
        type = "BEVERAGE"
        def getPrice(self):
            return self.price
        def setPrice(self, price):
            self.price = price
        def getName(self):
            return self.name
    
    
    class coke(Beverage):
        def __init__(self):
            self.name = "coke"
            self.price = 4.0
    
    
    class milk(Beverage):
        def __init__(self):
            self.name = "milk"
            self.price = 5.0
    复制代码

    最终,我们是要建造一个订单,因而,需要一个订单类。假设,一个订单,包括一份主食,一份小食,一种饮料。(省去一些异常判断)

    复制代码
    class order():
        burger=""
        snack=""
        beverage=""
        def __init__(self,orderBuilder):
            self.burger=orderBuilder.bBurger
            self.snack=orderBuilder.bSnack
            self.beverage=orderBuilder.bBeverage
        def show(self):
            print "Burger:%s"%self.burger.getName()
            print "Snack:%s"%self.snack.getName()
            print "Beverage:%s"%self.beverage.getName()
    复制代码

    代码中的orderBuilder是什么鬼?这个orderBuilder就是建造者模式中所谓的“建造者”了,先不要问为什么不在order类中把所有内容都填上,而非要用builder去创建。接着往下看。
    orderBuilder的实现如下:

    复制代码
    class orderBuilder():
        bBurger=""
        bSnack=""
        bBeverage=""
        def addBurger(self,xBurger):
            self.bBurger=xBurger
        def addSnack(self,xSnack):
            self.bSnack=xSnack
        def addBeverage(self,xBeverage):
            self.bBeverage=xBeverage
        def build(self):
            return order(self)
    复制代码

    在场景中如下去实现订单的生成:

    复制代码
    if  __name__=="__main__":
        order_builder=orderBuilder()
        order_builder.addBurger(spicyChickenBurger())
        order_builder.addSnack(chips())
        order_builder.addBeverage(milk())
        order_1=order_builder.build()
        order_1.show()
    复制代码

    打印结果如下:
    Burger:spicy chicken burger
    Snack:chips
    Beverage:milk

    二、建造者模式

    建造者模式的定义如下:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
    建造者模式的作用,就是将“构建”和“表示”分离,以达到解耦的作用。在上面订单的构建过程中,如果将order直接通过参数定义好(其构建与表示没有分离),同时在多处进行订单生成,此时需要修改订单内容,则需要一处处去修改,业务风险也就提高了不少。
    在建造者模式中,还可以加一个Director类,用以安排已有模块的构造步骤。对于在建造者中有比较严格的顺序要求时,该类会有比较大的用处。在上述例子中,Director实现如下:

    复制代码
    class orderDirector():
        order_builder=""
        def __init__(self,order_builder):
            self.order_builder=order_builder
        def createOrder(self,burger,snack,beverage):
            self.order_builder.addBurger(burger)
            self.order_builder.addSnack(snack)
            self.order_builder.addBeverage(beverage)
            return self.order_builder.build()
    复制代码

    通过createOrder直接代入参数,即可直接生成订单。


    f.png

    三、建造者模式的优点和使用场景

    优点:

    1、封装性好,用户可以不知道对象的内部构造和细节,就可以直接建造对象;
    2、系统扩展容易;
    3、建造者模式易于使用,非常灵活。在构造性的场景中很容易实现“流水线”;
    4、便于控制细节。

    使用场景:

    1、目标对象由组件构成的场景中,很适合建造者模式。例如,在一款赛车游戏中,车辆生成时,需要根据级别、环境等,选择轮胎、悬挂、骨架等部件,构造一辆“赛车”;
    2、在具体的场景中,对象内部接口需要根据不同的参数而调用顺序有所不同时,可以使用建造者模式。例如:一个植物养殖器系统,对于某些不同的植物,浇水、施加肥料的顺序要求可能会不同,因而可以在Director中维护一个类似于队列的结构,在实例化时作为参数代入到具体建造者中。

    四、建造者模式的缺点

    1、“加工工艺”对用户不透明。(封装的两面性)

    原型模式

    一、图层

    大家如果用过类似于Photoshop的平面设计软件,一定都知道图层的概念。图层概念的提出,使得设计、图形修改等操作更加便利。设计师既可以修改和绘制当前图像对象,又可以保留其它图像对象,逻辑清晰,且可以及时得到反馈。本节内容,将以图层为主角,介绍原型模式。
    首先,设计一个图层对象。

    复制代码
    class simpleLayer:
        background=[0,0,0,0]
        content="blank"
        def getContent(self):
            return self.content
        def getBackgroud(self):
            return self.background
        def paint(self,painting):
            self.content=painting
        def setParent(self,p):
            self.background[3]=p
        def fillBackground(self,back):
            self.background=back
    复制代码

    在实际的实现中,图层实现会很复杂,这里仅介绍相关的设计模式,做了比较大的抽象,用background表示背景的RGBA,简单用content表示内容,除了直接绘画,还可以设置透明度。
    新建图层,填充蓝底并画一只狗,可以简单表示如下:

    复制代码
    if  __name__=="__main__":
        dog_layer=simpleLayer()
        dog_layer.paint("Dog")
        dog_layer.fillBackground([0,0,255,0])
        print "Background:",dog_layer.getBackgroud()
        print "Painting:",dog_layer.getContent()
    复制代码

    打印如下:
    Background: [0, 0, 255, 0]
    Painting: Dog


    接下来,如果需要再生成一个同样的图层,再填充同样的颜色,再画一只同样狗,该如何做呢?还是按照新建图层、填充背景、画的顺序么?或许你已经发现了,这里可以用复制的方法来实现,而复制(clone)这个动作,就是原型模式的精髓了。
    按照此思路,在图层类中新加入两个方法:clone和deep_clone

    复制代码
    from copy import copy, deepcopy
    class simpleLayer:
        background=[0,0,0,0]
        content="blank"
        def getContent(self):
            return self.content
        def getBackgroud(self):
            return self.background
        def paint(self,painting):
            self.content=painting
        def setParent(self,p):
            self.background[3]=p
        def fillBackground(self,back):
            self.background=back
        def clone(self):
            return copy(self)
        def deep_clone(self):
            return deepcopy(self)
    if  __name__=="__main__":
        dog_layer=simpleLayer()
        dog_layer.paint("Dog")
        dog_layer.fillBackground([0,0,255,0])
        print "Background:",dog_layer.getBackgroud()
        print "Painting:",dog_layer.getContent()
        another_dog_layer=dog_layer.clone()
        print "Background:", another_dog_layer.getBackgroud()
        print "Painting:", another_dog_layer.getContent()
    复制代码

    打印结果如下:
    Background: [0, 0, 255, 0]
    Painting: Dog
    Background: [0, 0, 255, 0]
    Painting: Dog


    clone和deep_clone有什么区别呢?大多数编程语言中,都会涉及到深拷贝和浅拷贝的问题,一般来说,浅拷贝会拷贝对象内容及其内容的引用或者子对象的引用,但不会拷贝引用的内容和子对象本身;而深拷贝不仅拷贝了对象和内容的引用,也会拷贝引用的内容。所以,一般深拷贝比浅拷贝复制得更加完全,但也更占资源(包括时间和空间资源)。举个例子,下面的场景,可以说明深拷贝和浅拷贝的区别。

    复制代码
    if  __name__=="__main__":
        dog_layer=simpleLayer()
        dog_layer.paint("Dog")
        dog_layer.fillBackground([0,0,255,0])
        print "Original Background:",dog_layer.getBackgroud()
        print "Original Painting:",dog_layer.getContent()
        another_dog_layer=dog_layer.clone()
        another_dog_layer.setParent(128)
        another_dog_layer.paint("Puppy")
        print "Original Background:", dog_layer.getBackgroud()
        print "Original Painting:", dog_layer.getContent()
        print "Copy Background:", another_dog_layer.getBackgroud()
        print "Copy Painting:", another_dog_layer.getContent()
    复制代码

    打印如下:
    Original Background: [0, 0, 255, 0]
    Original Painting: Dog
    Original Background: [0, 0, 255, 128]
    Original Painting: Dog
    Copy Background: [0, 0, 255, 128]
    Copy Painting: Puppy


    浅拷贝后,直接对拷贝后引用(这里的数组)进行操作,原始对象中该引用的内容也会变动。如果将another_dog_layer=dog_layer.clone()换成another_dog_layer=dog_layer.deep_clone(),即把浅拷贝换成深拷贝,其如果如下:


    Original Background: [0, 0, 255, 0]
    Original Painting: Dog
    Original Background: [0, 0, 255, 0]
    Original Painting: Dog
    Copy Background: [0, 0, 255, 128]
    Copy Painting: Puppy


    深拷贝后,其对象内的引用内容也被进行了复制。

    二、原型模式

    原型模式定义如下:用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
    需要注意一点的是,进行clone操作后,新对象的构造函数没有被二次执行,新对象的内容是从内存里直接拷贝的。


    f1.png

    三、原型模式的优点和使用场景

    优点:

    1、性能极佳,直接拷贝比在内存里直接新建实例节省不少的资源;
    2、简化对象创建,同时避免了构造函数的约束,不受构造函数的限制直接复制对象,是优点,也有隐患,这一点还是需要多留意一些。

    使用场景:

    1、对象在修改过后,需要复制多份的场景。如本例和其它一些涉及到复制、粘贴的场景;
    2、需要优化资源的情况。如,需要在内存中创建非常多的实例,可以通过原型模式来减少资源消耗。此时,原型模式与工厂模式配合起来,不管在逻辑上还是结构上,都会达到不错的效果;
    3、某些重复性的复杂工作不需要多次进行。如对于一个设备的访问权限,多个对象不用各申请一遍权限,由一个设备申请后,通过原型模式将权限交给可信赖的对象,既可以提升效率,又可以节约资源。

    四、原型模式的缺点

    1、深拷贝和浅拷贝的使用需要事先考虑周到;
    2、某些编程语言中,拷贝会影响到静态变量和静态函数的使用。

  • 相关阅读:
    10天学安卓-第八天
    10天学安卓-第七天
    10天学安卓-第六天
    10天学安卓-第五天
    10天学安卓-第四天
    10天学安卓-第三天
    透过 Cucumber 学习 BDD
    应对复杂软件的思考
    管理任务就是管理时间
    Running Dubbo On Spring Boot
  • 原文地址:https://www.cnblogs.com/bubu99/p/14197109.html
Copyright © 2011-2022 走看看