Python与设计模式之建造者模式和原型模式

1、Python与设计模式--建造者模式

建造者模式的定义如下:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式的作用,就是将“构建”和“表示”分离,以达到解耦的作用。在上面订单的构建过程中,如果将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

 

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

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

 

建造者模式的优点:
1、封装性好,用户可以不知道对象的内部构造和细节,就可以直接建造对象;
2、系统扩展容易;
3、建造者模式易于使用,非常灵活。在构造性的场景中很容易实现“流水线”;
4、便于控制细节。
使用场景:
1、目标对象由组件构成的场景中,很适合建造者模式。例如,在一款赛车游戏中,车辆生成时,需要根据级别、环境等,选择轮胎、悬挂、骨架等部件,构造一辆“赛车”;
2、在具体的场景中,对象内部接口需要根据不同的参数而调用顺序有所不同时,可以使用建造者模式。例如:一个植物养殖器系统,对于某些不同的植物,浇水、施加肥料的顺序要求可能会不同,因而可以在Director中维护一个类似于队列的结构,在实例化时作为参数代入到具体建造者中。

建造者模式的缺点

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

 

2、Python与设计模式--原型模式

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

 

大家如果用过类似于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
深拷贝后,其对象内的引用内容也被进行了复制。

原型模式的优点:
1、性能极佳,直接拷贝比在内存里直接新建实例节省不少的资源;
2、简化对象创建,同时避免了构造函数的约束,不受构造函数的限制直接复制对象,是优点,也有隐患,这一点还是需要多留意一些。
使用场景:
1、对象在修改过后,需要复制多份的场景。如本例和其它一些涉及到复制、粘贴的场景;
2、需要优化资源的情况。如,需要在内存中创建非常多的实例,可以通过原型模式来减少资源消耗。此时,原型模式与工厂模式配合起来,不管在逻辑上还是结构上,都会达到不错的效果;
3、某些重复性的复杂工作不需要多次进行。如对于一个设备的访问权限,多个对象不用各申请一遍权限,由一个设备申请后,通过原型模式将权限交给可信赖的对象,既可以提升效率,又可以节约资源。

原型模式的缺点:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔的猫96

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值