大话设计模型 Task02:策略、装饰、代理

一、简单工厂模式

在这里插入图片描述
在这里插入图片描述

问题描述

小菜今年计算机专业大四了,学了不少软件开发方面的东西,也学着编了些小程序,踌躇满志,一心要找一个好单位。当投递了无数份简历后,终于收到了一个单位的面试通知,小菜欣喜若狂。到了人家单位,前台小姐给了他一份题目,上面写着:"请用C++、Java, C#或VB.NET任意一种面向对象语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果。

模式定义

简单工厂模式(Simple Factory Pattern)是用一个单独的类来实现具体的实例化过程,避免客户端对具体实例化过程的显式指定。

问题分析

面向对象:封装、继承、多态

简单工厂模式可以用于这个问题。将加减乘除等运算类独立实现,并创建工厂类维护对运算类的使用。之后若需要增加新的运算符号,则只需增加运算类,修改工厂类即可,这也同时降低修改后的重复编译量(松耦合)。此外,简单工厂模式降了低业务逻辑和界面逻辑的耦合,易于复用(不是复制),若未来该windows程序迁移到web版,业务部分(计算部分)不需要修改。

代码实现

class Operation(object):
    __numberA = 0
    __numberB = 0

    def get_numberA(self):
        return self.__numberA

    def set_numberA(self, numberA):
        self.__numberA = numberA

    def get_numberB(self):
        return self.__numberB

    def set_numberB(self, numberB):
        self.__numberB = numberB

    def get_result(self):
        result = 0
        return result

    numberA = property(get_numberA, set_numberA)
    numberB = property(get_numberB, set_numberB)


class OperationAdd(Operation):  # 加法类,继承运算类
    def get_result(self):
        result = self.numberA + self.numberB
        return result


class OperationSub(Operation):  # 减法类,继承运算类
    def get_result(self):
        result = self.numberA - self.numberB
        return result


class OperationMul(Operation):  # 乘法类,继承运算类
    def get_result(self):
        result = self.numberA * self.numberB
        return result


class OperationDiv(Operation):  # 除法类,继承运算类
    def get_result(self):
        if self.numberB == 0:
            raise Exception("除数不能为0")
        result = self.numberA / self.numberB
        return result


class OperationFactory(object):  # 简单运算工厂类
    def createOperate(self, operate):
        if operate == "+":
            self.oper = OperationAdd()
        elif operate == "-":
            self.oper = OperationSub()
        elif operate == "*":
            self.oper = OperationMul()
        elif operate == "/":
            self.oper = OperationDiv()
        return self.oper


if __name__ == '__main__':
    oper = OperationFactory().createOperate("+")
    oper.numberA = 1
    oper.numberB = 2
    result = oper.get_result()
    print(result)

二、策略模式

在这里插入图片描述
在这里插入图片描述

问题描述

小菜,给你出个作业,做一个商场收银软件,营业员根据客户所购买商品的单价和数量,向客户收费。

问题分析

简单工厂模式虽然也能解决这个问题,但这个模式只是解决对象的创建问题,而且由于工厂本身包括了所有的收费方式,商场是可能经常性地更改打折额度和返利额度,每次维护或扩展收费方式都要改动这个工厂,以致代码需重新编译部署,这真的是很糟精的处理方式,所以用它不是最好的办法。面对算法的时常变动,应该有更好的办法。

模式定义

策略模式(Strategy Pattern)是指定义一个算法家族,使得家族内的不同算法都遵从算法家族的接口及方法规范,从而可以实现算法间互相替换,且不会影响到使用算法的客户。

代码实现

import tkinter
import tkinter.ttk


class CashSuper(object):  # 现金收取超类的抽象方法,收取现金,参数为原价,返回为当前价
    def __init__(self):
        pass

    def accept_cash(self, money):
        pass


class CashNormal(CashSuper):  # 正常收费,原价返回
    def accept_cash(self, money):
        return money


class CashRebate(CashSuper):  # 打折收费,初始化时,必需要输入折扣率,如八折,就是0.8
    __moneyRebate = 1

    def cash_rebate(self, moneyRebateStr):
        self.__moneyRebate = float(moneyRebateStr)

    def accept_cash(self, money):
        return money * self.__moneyRebate


class CashReturn(CashSuper):  # 返利收费,初始化时必须要输入返利条件和返利值,比如满300返100,则moneyCondition为300,moneyReturn为100
    __moneyCondition = 0
    __moneyReturn = 0

    def cash_return(self, moneyConditionStr, moneyReturnStr):
        self.__moneyCondition = float(moneyConditionStr)
        self.__moneyReturn = float(moneyReturnStr)

    def accept_cash(self, money):
        result = money
        if (money >= self.__moneyCondition):  # 若大于返利条件,则需要减去返利值
            result = money - money // self.__moneyCondition * self.__moneyReturn
        return result


class CashContext(object):
    def __init__(self, typ):
        self.cs = CashSuper()
        if typ == "正常收费":
            self.cs = CashNormal()
        elif typ == "满300返100":
            self.cs = CashReturn()
            self.cs.cash_return("300", "100")
        elif typ == "打8折":
            self.cs = CashRebate()
            self.cs.cash_rebate("0.8")

    def get_result(self, money):
        return self.cs.accept_cash(money)


class CashWindow(object):
    def __init__(self):
        self.total = 0  # 声明一个total来计算总计
        root = tkinter.Tk()
        self.label1 = tkinter.Label(root, text="单价:")
        self.txtPrice = tkinter.Entry(root, width=24, )
        self.label2 = tkinter.Label(root, text="数量:")
        self.txtNum = tkinter.Entry(root, width=24, )
        self.ibxList = tkinter.Text(root, width=45, height=10)
        self.label4 = tkinter.Label(root, text="总计:")

        self.iblResult = tkinter.StringVar()
        self.iblResult.set("%.2f" % self.total)  # 保留两位小数的浮点型
        self.result = tkinter.Label(root, textvariable=self.iblResult, height=2, font=('TimeNewsRoman', 24))
        self.button = tkinter.Button(root, text="确定", width=10, command=self.btnOK_click)
        self.buttonReset = tkinter.Button(root, text="重置", width=10, command=self.btnReset_click)

        self.label3 = tkinter.Label(root, text="计算方式:")
        self.comboVar = tkinter.StringVar()
        self.combobox = tkinter.ttk.Combobox(root, textvariable=self.comboVar, width=22, )
        self.combobox['value'] = ("正常收费", "打8折", "满300返100")  # 在combobox中加下拉选项
        self.combobox.current(0)
        self.layout()
        root.mainloop()

    def refresh(self):
        self.txtPrice.delete('0', 'end')
        self.txtNum.delete('0', 'end')

    def layout(self):
        self.label1.grid(row=0, column=0, padx=(10, 0), pady=10)
        self.txtPrice.grid(row=0, column=1, pady=10, padx=(0, 5), )
        self.label2.grid(row=1, column=0, padx=(10, 0))
        self.txtNum.grid(row=1, column=1, padx=(0, 5), )
        self.label3.grid(row=2, column=0, padx=(10, 0))
        self.combobox.grid(row=2, column=1, padx=(0, 5), pady=10)

        self.ibxList.grid(row=4, column=0, columnspan=4, padx=(5, 5), pady=10)
        self.label4.grid(row=5, column=0, padx=(10, 0))
        self.result.grid(row=5, column=1, columnspan=3, rowspan=2)
        self.button.grid(row=0, column=2, columnspan=2, pady=10, padx=(0, 10))
        self.buttonReset.grid(row=1, column=2, columnspan=2, padx=(0, 10))

    def btnReset_click(self):
        self.total = 0
        self.ibxList.delete('1.0', 'end')
        self.iblResult.set("%.2f" % self.total)
        self.refresh()

    # 主要部分
    def btnOK_click(self):
        csuper = CashContext(self.comboVar.get())
        totalPrice = csuper.get_result(
            float(self.txtPrice.get()) * float(self.txtNum.get()))  # 声明一个totalPrice来计算每个商品的单价(txtPrice)*数量(txtNum)后的合计
        self.total = self.total + totalPrice  # 将每个商品合计计入总计
        self.ibxList.insert('end',
                            "单价:" + self.txtPrice.get() + "数量:" + self.txtNum.get() + " " + self.comboVar.get() + "合计:%.2f" % (
                                totalPrice) + "\n")  # 在列表框中显示信息
        self.iblResult.set("%.2f" % self.total)
        self.refresh()


if __name__ == '__main__':
    CashWindow()

三、装饰模式

在这里插入图片描述

问题描述

我现在要求你写一个可以给人搭配不同的服饰的系统,比如类似QQ、网络游戏或论坛都有的Avatar系统。你怎么开发?

问题分析

建造者模式要求建造的过程必须是稳定的,而现在我们这个例子,建造过程是不稳定的,比如完全可以内穿西装,外套T恤,再加披风,打上领带,皮鞋外再穿上破球鞋:当然也完全可以只穿条辫衩就算完成。换句话就是说,通过服饰组合出一个有个性的人完全可以有无数种方案,并非是固定的。我们需要把所需的功能按正确的顺序串联起来进行控制,此时可以考虑使用装饰模式。

模式定义

装饰模式(Decorator Pattern)是指创建一个装饰类,来包装原有的类,从而实现动态地向一个现有的对象添加一些额外的职责,同时不改变其原有的结构。装饰模式比生成子类更为灵活。

代码实现

class Person(object):
    def __init__(self, name=""):
        self.name = name

    def show(self):
        print("装扮的" + self.name)


# 服饰类(Decorator)
class Finery(Person):
    component = None

    def decorate(self, component: Person):
        self.component = component

    def show(self):
        if self.component != None:
            self.component.show()


# 具体服饰类(Concrete Decorator)
class Tshirts(Finery):
    def show(self):
        print("大T恤", end=" ")
        super().show()


class BigTrouser(Finery):
    def show(self):
        print("垮裤", end=" ")
        super().show()


class Sneakers(Finery):
    def show(self):
        print("破球鞋", end=" ")
        super().show()


class Suit(Finery):
    def show(self):
        print("西装", end=" ")
        super().show()


class Tie(Finery):
    def show(self):
        print("领带", end=" ")
        super().show()


class LeatherShoes(Finery):
    def show(self):
        print("皮鞋", end=" ")
        super().show()


if __name__ == '__main__':
    xc = Person('小菜')
    print("第一种装扮:")
    dtx = Tshirts()
    kk = BigTrouser()
    pqx = Sneakers()

    pqx.decorate(xc)
    kk.decorate(pqx)
    dtx.decorate(kk)
    dtx.show()

    print("第二种装扮:")
    xz = Suit()
    ld = Tie()
    px = LeatherShoes()

    px.decorate(xc)
    ld.decorate(px)
    xz.decorate(ld)
    xz.show()

    print("第三种装扮:")
    pqx2 = Sneakers()
    px2 = LeatherShoes()
    kk2 = BigTrouser()
    ld2 = Tie()

    pqx2.decorate(xc)
    px2.decorate(pqx2)
    kk2.decorate(px2)
    ld2.decorate(kk2)
    ld2.show()

四、代理模式

在这里插入图片描述
在这里插入图片描述

问题描述

隔壁班的卓贾易想追求娇娇,但是他自己不好意思,就委托和娇娇同班的戴励帮助他。

问题分析

在这里插入图片描述

模式定义

代理模式(Proxy Pattern)是指实现一个类代表另一个类的功能,为其他对象提供一种代理以控制对这个对象的访问。

  • 第一,远程代理,也就是为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
  • 第二种应用是虚拟代理,是根据需要创建开销很大的对象.通过它来存放实例化需要很长时间的真实对象。
  • 第三种应用是安全代理,用来控制真实对象访问时的权限[DP],一般用于对象应该有不同的访问权限的时候。第四种是智能指引,是指当调用真实的对象时,代理处理另外一些事。

代码实现

class SchoolGirl(object):
    __name = None

    def get_name(self):
        return self.__name

    def set_name(self, name):
        self.__name = name

    name = property(get_name, set_name)


class IGiveGift(object):
    def give_dolls(self):
        pass

    def give_flowers(self):
        pass

    def give_chocolate(self):
        pass


class Pursuit(IGiveGift):
    __mm = None

    def __init__(self, mm: SchoolGirl):
        self.__mm = mm

    def give_dolls(self):
        print(self.__mm.name + " 送你洋娃娃")

    def give_flowers(self):
        print(self.__mm.name + " 送你鲜花")

    def give_chocolate(self):
        print(self.__mm.name + " 送你巧克力")


class Proxy(IGiveGift):
    def __init__(self, mm: SchoolGirl):
        self.gg = Pursuit(mm)

    def give_dolls(self):
        self.gg.give_dolls()

    def give_flowers(self):
        self.gg.give_flowers()

    def give_chocolate(self):
        self.gg.give_chocolate()


if __name__ == '__main__':
    jiaojiao = SchoolGirl()
    jiaojiao.name = "李娇娇"

    daili = Proxy(jiaojiao)
    daili.give_dolls()
    daili.give_flowers()
    daili.give_chocolate()

五、工厂方法模式

h
在这里插入图片描述

问题描述

大学生薛磊风在过去三年里一直在帮助孤寡老人,每周都去老人家里,为老人洗衣扫地、买米买油。有一次,他不幸受了伤,便委托他的两个同学继续去帮助老人,且不必提及任何人的名字,只需说是学雷锋做好事即可。

帮助老人是长期工作,三名“学雷锋的大学生”毕业后,也依然会以“社区志愿者”的名义继续学雷锋做好事。而老人其实不需要知道是谁来做好事,只需要知道是学雷锋的人来帮助就可以了。

问题分析

这一程序可以先用简单工厂模式实现,但由于学雷锋的学生毕业后,会转变身份成为社区志愿者,此时若使用简单工厂模式会涉及到工厂类的修改,违背开放封闭原则。当我们使用工厂方法模式时,可以对学雷锋的学生和社区志愿者分别建立工厂类,让客户端决定实例化哪一个工厂类。

模式定义

工厂方法模式(Factory Method Pattern)是指定义一个用于创建对象的结构,让子类决定实例化哪个类。工厂方法使类的实例化过程延迟到其子类。

工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现功能类,即将简单工厂内部的逻辑判断已到了客户端进行。

简单工厂 vs. 工厂方法

简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对干客户端来说,去除了与具体产品的依赖。工厂方法克服了简单工厂违背开放-封闭原则的缺点,又保持了封装对象创建过程的优点。

代码实现

class LeiFeng(object):
    def sweep(self):
        print("扫地")

    def wash(self):
        print("洗衣")

    def buy_rice(self):
        print("买米")


class Undergraduate(LeiFeng):
    pass


class Volunteer(LeiFeng):
    pass


class SimpleFactory(object):
    def create_LeiFeng(self, typ: str) -> LeiFeng:
        self.result = None
        if typ == "学雷锋的大学生":
            self.result = Undergraduate()
        elif typ == "社区志愿者":
            self.result = Volunteer()
        return self.result


class IFactory(object):
    def create_LeiFeng(self):
        pass


class UndergraduateFactory(IFactory):
    def create_LeiFeng(self):
        return Undergraduate()


class VolunteerFactory(IFactory):
    def create_LeiFeng(self):
        return Volunteer()


if __name__ == '__main__':
    # 简单工厂模式
    studentA = SimpleFactory().create_LeiFeng("学雷锋的大学生")
    studentA.buy_rice()
    studentB = SimpleFactory().create_LeiFeng("学雷锋的大学生")
    studentB.sweep()
    studentC = SimpleFactory().create_LeiFeng("学雷锋的大学生")
    studentC.wash()

    # 工厂方法模式
    factory = UndergraduateFactory()
    student = factory.create_LeiFeng()

    student.buy_rice()
    student.sweep()
    student.wash()

    factory = VolunteerFactory()
    student = factory.create_LeiFeng()

    student.buy_rice()
    student.sweep()
    student.wash()

学习参考链接:

视频1: https://www.bilibili.com/video/BV1hG4y1d7GK?p=4&vd_source=c5d53beb52dbf1f3fcc4518fb4b1cbfa
视频2: https://www.bilibili.com/video/BV1454y187Er/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=c5d53beb52dbf1f3fcc4518fb4b1cbfa

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值