有测试问题请微信联系作者,备注来意(点击此处添加)
240
一名普通的测试打工人;专注自动化测试技术研究、实践、总结、分享、交流。
用我8年+的经历,给大家带来更多实用的干货。
人若有志,就不会在半坡停止。

【Python进阶】Python设计模式

设计模式介绍

什么是设计模式

设计模式是面对各种问题进行提炼和抽象而形成的解决方案。这些设计方案是前人不断试验,考虑了封装性、复用性、效率、可修改、可移植等各种因素的高度总结。它不限于一种特定的语言,它是一种解决问题的思想和方法

为什么要用设计模式

按照设计模式编写的代码,其可读性也会大大提升,利于团队项目的继承和扩展。

设计模式法分为三个大类

  • 创建类设计模式
  • 结构类设计模式
  • 行为类设计模式

创建类设计模式(5种)

单例模式、工厂模式(简单工厂模式、抽象工厂模式)、建造者模式、原型模式

结构类设计模式(7种)

代理模式、装饰器模式、适配器模式、门面模式、组合模式、享元模式、桥梁模式

行为类设计模式(11种)

策略模式、责任链模式、命令模式、中介者模式、模板模式、迭代器模式、访问者模式、观察者模式、解释器模式、备忘录模式、状态模式
设计模式也衍生出了很多的新的种类,不局限于这23种

设计模式与架构、框架的关系

软件框架与设计模式的关系

软件框架,是提取了特定领域的软件的共性部分所形成的软件体系,它并不是一个成熟的软件,而更像是一个“半成品”,程序员在框架之上,可以很方便地某些特定领域实现又快又可靠的二次开发
设计模式 和 软件框架 在软件设计中是两个不同的研究领域:

  1. 设计模式它指的是针对一类问题的解决方法,一个设计模式可应用于不同的框架和被不同的语言所实现;
    而框架则是一个应用的体系结构,是一种或多种设计模式和代码的混合体;
  2. 设计模式相较于框架更容易移植,并且可以用各种语言实现,而软件框架则受限于领域大环境。虽然设计模式和软件框架有很多不同,但在某些方面他们二者是统一的,即重视软件复用,提高开发效率。

软件架构与设计模式的关系:

软件架构是个比较大的概念,架构要考虑软件的整体结构、层次划分以及不同部分间的协作和交互等,架构的着眼点偏整体。
相比之下,框架和设计模式的范围则具体很多,框架着眼于领域内的解决方法,设计模式则针对一类问题的解决方案和设计思路。
总体来说,软件架构可以由不同的框架和不同的设计模式,再加上特定的构件组合来实现;框架可以根据设计模式结合特定编程语言和环境来实现。设计模式就是解决单一问题的设计思路和解决方法。

单例模式

单例模式是一种常用的设计模式,它能够确保一个类只有一个实例,并提供一个全局访问点。在某些特定的场景下,使用单例模式能够有效地解决一些问题,提高代码的可维护性和性能。本文将从不同的角度探讨单例模式的使用场景。

应用场景

  • 资源共享场景
    在某些情况下,系统中只需要存在一个共享的资源,比如数据库连接池、线程池、缓存等。如果每次需要使用这些资源时都创建新的实例,会导致资源的浪费,并且可能会出现竞争条件。这时候使用单例模式可以确保资源的共享和唯一性,避免资源的重复创建和冲突。
  • 配置文件场景
    在很多应用程序中,都会使用配置文件来存储一些固定的配置信息,比如数据库连接信息、系统参数等。这些配置信息在整个应用程序中是唯一的,如果每次需要使用配置信息时都读取一次配置文件,会导致性能的损耗。使用单例模式可以将配置信息读取一次并保存在单例对象中,以后每次需要使用配置信息时直接从单例对象中获取,避免了重复读取配置文件的开销。
  • 日志记录场景
    在大部分应用程序中,都需要记录一些日志信息,比如错误日志、调试日志等。如果每次记录日志时都创建一个新的日志对象,会导致大量的内存开销,并且可能会出现日志信息的丢失。使用单例模式可以确保日志对象的唯一性,避免了内存的浪费和日志的丢失。
  • 线程池场景
    在并发编程中,经常需要使用线程池来管理线程的创建和销毁。如果每次需要执行任务时都创建一个新的线程池,会导致线程的频繁
    创建和销毁,降低了系统的性能。使用单例模式可以确保线程池的唯一性,避免了线程的重复创建和销毁,提高了系统的性能。
  • 计数器场景
    在某些应用程序中,需要使用计数器来统计某个事件的发生次数,比如网站的访问量统计、订单的数量统计等。如果每次统计时都创建一个新的计数器对象,会导致计数的不准确和资源的浪费。使用单例模式可以确保计数器的唯一性,避免了计数的不准确和资源的浪费。
  • 任务调度场景
    在很多应用程序中,需要使用任务调度来定期执行一些任务,比如定时发送邮件、定时备份数据库等。如果每次需要执行任务时都创建一个新的任务调度器,会导致任务的重复执行和资源的浪费。使用单例模式可以确保任务调度器的唯一性,避免了任务的重复执行和资源的浪费。
    单例模式在资源共享、配置文件、日志记录、线程池、计数器和任务调度等场景下都能发挥重要的作用。通过使用单例模式,可以确保类的唯一性和全局访问性,提高了代码的可维护性和性能。然而,单例模式也有一些缺点,比如可能导致代码的耦合性增加、单例对象的创建和销毁时机不可控等。因此,在使用单例模式时需要结合具体的场景和需求,合理地运用该模式,以达到最佳的效果。

应用举例

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

单例模式的优点

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

单例模式的缺点

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

代码实例

"""__new__特殊方法的使用---单例模式"""
# 单例模式是一种设计模式,它要求单例对象的类必须保证只有一个实例存在。
class SinglePerson:
    _instance = None            # 使用场景:token资源共享等

    def __new__(cls):
        if cls._instance is None:       # 保证创建实例时,只有一个实例
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        pass


i1 = SinglePerson()
i2 = SinglePerson()
print(id(i1))
print(id(i2))

工厂模式

定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。
产品类定义产品的公共属性和接口工厂类定义产品实例化的“方式”

应用场景

当系统实例要求比较灵活和可扩展时,可以考虑工厂模式或者抽象工厂模式实现。
如:在通信系统中,高层通信协议会很多样化,同时,上层协议依赖于下层协议,那么就可以对应建立对应层级的抽象工厂,根据不同的“产品需求”去生产定制的实例。

工厂模式、抽象工厂模式的优点

  1. 工厂模式巨有非常好的封装性,代码结构清晰;在抽象工厂模式中,其结构还可以随着需要进行更深或者更浅的抽象层级调整,非常灵活
  2. 屏蔽产品类,使产品的被使用业务场景和产品的功能细节可以分而开发进行,是比较典型的解耦框架

工厂类模式的缺点

  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 foodFactory:          
    type = ""
    # @classmethod          # 可添加静态方法,省去了将工厂实例化的过程。这种模式就叫做简单工厂模式。
    def createFood(self, foodClass):    # 传参为具体产品的类对象
        print(self.type, " factory produce a instance.", foodClass)
        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"


if __name__ == "__main__":
    burger_factory = burgerFactory()                                # 1.创建汉堡工厂实例
    cheese_burger = burger_factory.createFood(cheeseBurger)         # 2.利用工厂进行创建出具体产品实例(芝士汉堡实例)
    print((cheese_burger.getName(), cheese_burger.getPrice()))      # 芝士汉堡实例,利用继承父类的getName()、getPrice()方法进行获取产品数据
    # snack_factorry = snackFactory()
    # beverage_factory = beverageFactory()
    # 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()))

建造者模式

建造者模式的作用,就是将“构建”和“表示”分离,以达到解耦的作用。
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

应用场景

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

优点

  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

# 订单
class order:
    burger=""
    snack=""
    beverage=""
    def __init__(self,orderBuilder):                    # 参数为建造者实例
        self.burger=orderBuilder.bBurger
        self.snack=orderBuilder.bSnack
        self.beverage=orderBuilder.bBeverage
    def show(self):
        try:
            print("Burger:%s"%self.burger.getName())        # 使用实例对象中方法获取属性
        except Exception as e:
            print("没有点汉堡", e)
        try:
            print("Snack:%s"%self.snack.getName())
        except Exception as e:
            print("没有点小吃", e)
        try:
            print("Beverage:%s"%self.beverage.getName())
        except Exception as e:
            print("没有点饮料", e)

# 建造者
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)

class orderDirector:       # 封装构建步骤
    order_builder=""
    def __init__(self,order_builder):
        self.order_builder=order_builder
    def createOrder(self,burger=None,snack=None,beverage=None):
        self.order_builder.addBurger(burger)
        self.order_builder.addSnack(snack)
        self.order_builder.addBeverage(beverage)
        return self.order_builder.build()

if  __name__=="__main__":
    order_builder=orderBuilder()                        # 1.实例化建造者
    order_builder.addBurger(spicyChickenBurger())       # 2.添加汉堡,传入辣味鸡肉汉堡实例,生成实例麻辣鸡肉汉堡属性
    # order_builder.addSnack(chips())
    # order_builder.addBeverage(milk())
    order_1=order_builder.build()                       # 3.构建方法,调用order并传入自身实例
    order_1.show()                                      # 4.使用辣味肌肉汉堡实例,进行调用获取属性方法
    print("="*50)
    """引入orderDirector类后操作流程"""
    order_builder2 = orderBuilder()                        # 1.实例化建造者
    orderDirector = orderDirector(order_builder2)           # 2.orderDirector实例化,传入参数为建造者
    create_order = orderDirector.createOrder(spicyChickenBurger(), chips())     # 3.创建订单实例,传入产品实例---省去构建步骤
    create_order.show()                                    # 4.获取订单属性

原型模式

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

应用场景

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

优点

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

缺点

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

代码实例

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

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)           # 利用copy进行实例的镜像复制
    def deep_clone(self):
        return deepcopy(self)       # 利用deepcopy进行实例的镜像深拷贝
    """
    浅拷贝会拷贝对象内容及其内容的引用或者子对象的引用,但不会拷贝引用的内容和子对象本身;
    而深拷贝不仅拷贝了对象和内容的引用,也会拷贝引用的内容。
    所以,一般深拷贝比浅拷贝复制得更加完全,但也更占资源(包括时间和空间资源)。
    """

if  __name__=="__main__":
    dog_layer=simpleLayer()
    dog_layer.paint("Dog")
    dog_layer.fillBackground([0,0,255,0])
    print("old Background:",dog_layer.getBackgroud())
    print("old Painting:",dog_layer.getContent())
    another_dog_layer=dog_layer.clone()
    another_dog_layer.setParent(128)        # 浅拷贝后,修改引用变量后,原来的数据也会发生变化(深拷贝不会出现)
    another_dog_layer.paint("Puppy")
    print("old Background:", dog_layer.getBackgroud())
    print("old Painting:", dog_layer.getContent())
    print("Copy Background:", another_dog_layer.getBackgroud())
    print("Copy Painting:", another_dog_layer.getContent())

代理模式(使用频率非常高)

为某对象提供一个代理,以控制对此对象的访问和控制。
代理模式在使用过程中,应尽量对抽象主题类进行代理,而尽量不要对加过修饰和方法的子类代理。
如上例中,如果有一个xServer继承了Server,并新加了方法xMethod,xServer的代理应以Server为主题进行设计,而尽量不要以xServer为主题,以xServer为主题的代理可以从ServerProxy继承并添加对应的方法。

应用场景

  1. 针对某特定对象进行功能和增强性扩展。如IP防火墙、远程访问代理等技术的应用
  2. 对主题对象进行保护。如大流量代理,安全代理等
  3. 减轻主题对象负载。如权限代理等

优点

  1. 职责清晰:非常符合单一职责原则,主题对象实现真实业务逻辑,而非本职责的事务,交由代理完成
  2. 扩展性强:面对主题对象可能会有的改变,代理模式在不改变对外接口的情况下,可以实现最大程度的扩展
  3. 保证主题对象的处理逻辑:
    代理可以通过检查参数的方式,保证主题对象的处理逻辑输入在理想范围内

缺点

可能会降低整体业务的处理效率和速度

代码实例

class Server:
    content = ""
    def recv(self,info):        # 接受数据
        pass
    def send(self,info):
        pass
    def show(self):
        pass
class infoServer(Server):
    def recv(self,info):        # 接受数据,数据赋值到Server类content变量中
        self.content = info
        return "infoServer recv OK!"
    def send(self,info):
        pass
    def show(self):
        print("SHOW:%s"% self.content)
print("==============上面为服务器数据处理,下面为服务器代理和白名单处理")
class serverProxy:
    pass
class infoServerProxy(serverProxy):     # server的接口代理
    server=""
    def __init__(self,server):          # 参数为服务实例对象
        self.server=server
    def recv(self,info):
        print("infoServerProxy recv 进行中")
        return self.server.recv(info)   # 服务实例对象进行调用服务的recv方法进行信息接受
    def show(self):
        self.server.show()

class whiteInfoServerProxy(infoServerProxy):        # 服务器白名单管理
    white_list=[]                              # 定义白名单列表
    def recv(self,info):
        try:
            assert type(info)==dict
        except:
            return "info structure is not correct"
        addr=info.get("addr", 0)                # 查找不到则返回0
        if not addr in self.white_list:
            return "你的地址不在白名单中。"
        else:
            print("whiteInfoServerProxy recv")
            content=info.get("content","")
            return self.server.recv(content)    # 在白名单中才会调用服务器代理
    def addWhite(self,addr):
        self.white_list.append(addr)
    def rmvWhite(self,addr):
        self.white_list.remove(addr)
    def clearWhite(self):
        self.white_list=[]
        
if  __name__=="__main__":
    info_struct = dict()
    info_struct["addr"] = 10010
    info_struct["content"] = "Hello World!"
    info_server = infoServer()          # 服务器实例
    info_server_proxy = whiteInfoServerProxy(info_server)       # 服务器代理创建infoServer服务器实例
    print("初始化后")
    print(info_server_proxy.recv(info_struct))          # 白名单处理
    print("调用show前")
    info_server_proxy.show()                            # 服务器实例方法查看接受数据(未添加白名单,服务器不会接受到数据
    print("添加上白名单后")
    info_server_proxy.addWhite(10010)                   # 添加白名单
    print(info_server_proxy.recv(info_struct))          # 白名单处理通过,服务器代理进行数据处理
    info_server_proxy.show()

装饰器模式

动态地给一个对象添加一些额外的职责。在增加功能方面,装饰器模式比生成子类更为灵活。

装饰器模式和上一节说到的代理模式非常相似,可以认为,装饰器模式就是代理模式的一个特殊应用,两者的共同点是都具有相同的接口,不同点是侧重对主题类的过程的控制,而装饰模式则侧重对类功能的加强或减弱

应用场景

需要扩展、增强或者减弱一个类的功能

优点

  1. 装饰器模式是继承方式的一个替代方案,可以轻量级的扩展被装饰对象的功能
  2. Python的装饰器模式是实现AOP的一种方式,便于相同操作位于不同调用位置的统一管理

缺点

多层装饰器的调试和维护有比较大的困难。

代码实例

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 drinkDecorator:         # 饮料装饰器
    def getName(self):
        pass
    def getPrice(self):
        pass

class iceDecorator(drinkDecorator):     # 冰块---装饰器
    def __init__(self, beverage):       # 参数为产品的实例对象
        self.beverage = beverage
    def getName(self):
        return self.beverage.getName()+" +ice"
    def getPrice(self):
        return self.beverage.getPrice()+0.3

class sugarDecorator(drinkDecorator):       # 糖---装饰器
    def __init__(self,beverage):
        self.beverage=beverage
    def getName(self):
        return self.beverage.getName()+" +sugar"
    def getPrice(self):
        return self.beverage.getPrice()+0.5
if  __name__=="__main__":
    coke_cola=coke()
    print("Name:%s"%coke_cola.getName())
    print("Price:%s"%coke_cola.getPrice())
    ice_coke = iceDecorator(coke_cola)          # 加冰装饰器,参数为产品的实例对象
    print("Name:%s" % ice_coke.getName())       # 装饰器进行获取产品数据并进行处理
    print("Price:%s" % ice_coke.getPrice())

Python自带装饰器

AOP即Aspect Oriented Programming(面向切面的编程)可以解释为:如果几个或更多个逻辑过程中(这类逻辑过程可能位于不同的对象,不同的接口当中),有重复的操作行为,就可以将这些行为提取出来(即形成切面),进行统一管理和维护。
举例:
系统中需要在各个地方打印日志,就可以将打印日志这一操作提取出来,作为切面进行统一维护。从编程思想的关系来看,可以认为AOP和OOP(面向对象的编程)是并列关系,二者是可以替换的,也可以结合起来用。
在Python语言中,是天然支持装饰器的

def log(func):
    def wrapper(*args, **kw):
        print 'call %s():' % func.__name__
        return func(*args, **kw)
    return wrapper

@log
def now():
    print '2016-12-04'
if  __name__=="__main__":
    now()

适配器模式

将一个类的接口变换成客户端期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
适配器模式和装饰模式有一定的相似性,都起包装的作用,但二者本质上又是不同的,装饰模式的结果,是给一个对象增加了一些额外的职责,而适配器模式,则是将另一个对象进行了“伪装”。

适配器可以认为是对现在业务的补偿式应用,所以,尽量不要在设计阶段使用适配器模式,在两个系统需要兼容时可以考虑使用适配器模式。

应用场景

不修改现有接口,同时也要使该接口适用或兼容新场景业务中,适合使用适配器模式。
例如,在一个嵌入式系统中,原本要将数据从Flash读入,现在需要将数据从磁盘读入,这种情况可以使用适配器模式,将从磁盘读入数据的接口进行“伪装”,以从Flash中读数据的接口形式,从磁盘读入数据。

优点

  1. 适配器模式可以让两个接口不同,甚至关系不大的两个类一起运行
  2. 提高了类的复用度,经过“伪装”的类,可以充当新的角色
  3. 适配器可以灵活“拆卸”。

缺点

适配器模式与原配接口相比,毕竟增加了一层调用关系,所以,在设计系统时,不要使用适配器模式。

代码实例

class ACpnStaff:        # A公司接口
    name=""
    id=""
    phone=""
    def __init__(self,id):
        self.id=id
    def getName(self):
        print("A protocol getName method...id:%s"%self.id)
        return self.name
    def setName(self,name):
        print("A protocol setName method...id:%s"%self.id)
        self.name=name
    def getPhone(self):
        print("A protocol getPhone method...id:%s"%self.id)
        return self.phone
    def setPhone(self,phone):
        print("A protocol setPhone method...id:%s"%self.id)
        self.phone=phone
class BCpnStaff:        # B公司接口
    name=""
    id=""
    telephone=""
    def __init__(self,id):
        self.id=id
    def get_name(self):
        print("B protocol get_name method...id:%s"%self.id)
        return self.name
    def set_name(self,name):
        print("B protocol set_name method...id:%s"%self.id)
        self.name=name
    def get_telephone(self):
        print("B protocol get_telephone method...id:%s"%self.id)
        return self.telephone
    def set_telephone(self,telephone):
        print("B protocol get_name method...id:%s"%self.id)
        self.telephone=telephone

class CpnStaffAdapter:      # 适配器模式
    b_cpn=""
    def __init__(self,id):
        self.b_cpn=BCpnStaff(id)        # 获取B公司实例对象,一遍后续调用
    def getName(self):                  # 接口方法设置与A公司保持一致,A公司就可以不改变接口方法,直接访问B公司
        return self.b_cpn.get_name()
    def getPhone(self):
        return self.b_cpn.get_telephone()
    def setName(self,name):
        self.b_cpn.set_name(name)
    def setPhone(self,phone):
        self.b_cpn.set_telephone(phone)
        
if __name__=="__main__":
    acpn_staff=ACpnStaff("123")
    acpn_staff.setName("X-A")           # A公司的方法
    acpn_staff.setPhone("10012345678")
    print("A Staff Name:%s"%acpn_staff.getName())
    print("A Staff Phone:%s"%acpn_staff.getPhone())
    print("上面添加A公司员工")
    bcpn_staff=CpnStaffAdapter("456")       # 适配器实例,参数为要查询员工的id
    bcpn_staff.setName("Y-B")
    bcpn_staff.setPhone("99987654321")
    print("B Staff Name:%s"%bcpn_staff.getName())   # 用原来A公司的接口名(方法名)来访问B公司(适配器中方法名只是和A公司一样而已)
    print("B Staff Phone:%s"%bcpn_staff.getPhone())

门面模式(外观模式)

门面模式也叫外观模式,定义如下:
要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。
门面模式提供一个高层次的接口,使得子系统更易于使用。门面模式注重“统一的对象”,也就是提供一个访问子系统的接口
门面模式与之前说过的模板模式有类似的地方,都是对一些需要重复方法的封装。但从本质上来说,是不同的。
模板模式是对类本身的方法的封装,其被封装的方法也可以单独使用;而门面模式,是对子系统的封装,其被封装的接口理论上是不会被单独提出来用的。

应用场景

  1. 为一个复杂的子系统提供一个外界访问的接口。这类例子是生活还是蛮常见的,例如:电视遥控器的抽象模型,电信运营商的用户交互设备等
  2. 需要简化操作界面时。例如:常见的扁平化系统操作界面等,在生活中和工业中都很常见。

优点

  1. 减少了系统之间的相互依赖,提高了系统的灵活
  2. 提高了整体系统的安全性:
    封装起的系统对外的接口才可以用,隐藏了很多内部接口细节,若方法不允许使用,则在门面中可以进行灵活控制。

缺点

门面模式的缺点在于,不符合开闭原则,一旦系统成形后需要修改,几乎只能重写门面代码,这比继承或者覆写等方式,或者其它一些符合开闭原则的模式风险都会大一些。

代码实例

class AlarmSensor:      # 报警
    def run(self):
        print("Alarm Ring...")
class WaterSprinker:    # 喷水
    def run(self):
        print("Spray Water...")
class EmergencyDialer:  # 拨打电话
    def run(self):
        print("Dial 119...")

class EmergencyFacade:      # 门面模式构建---集合初始化,集合构建方法
    def __init__(self):
        self.alarm_sensor=AlarmSensor()
        self.water_sprinker=WaterSprinker()
        self.emergency_dialer=EmergencyDialer()
    def runAll(self):
        self.alarm_sensor.run()
        self.water_sprinker.run()
        self.emergency_dialer.run()


if __name__=="__main__":
    emergency_facade=EmergencyFacade()
    emergency_facade.runAll()

组合模式

组合模式也叫作部分-整体模式,其定义如下:将对象组合成树形结构以表示“部分”和“整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

应用场景

维护部分与整体的逻辑关系,或者动态调用整体或部分的功能接口,可以考虑使用组合模式。
例如:非常多的操作系统(如Linux)都把文件系统设计成树形结构,再比如说分布式应用中借助Zookeeper,也可以组织和调用分布式集群中的结点功能。

优点

  1. 节点增加和减少是非常自由和方便的,这也是树形结构的一大特点
  2. 所有节点,不管是分支节点还是叶子结点,不管是调用一个结点,还是调用一个结点群,都是非常方便的。

缺点

由于叶子结点和分支结点直接使用了实现类,而不方便使用抽象类,这大大限制了接口的影响范围;若结点接口发生变更,对系统造成的风险会比较大。

代码实例

class Company:
    name = ''
    def __init__(self, name):
        self.name = name
    def add(self, company):
        pass
    def remove(self, company):
        pass
    def display(self, depth):
        pass
    def listDuty(self):
        pass

class ConcreteCompany(Company):     # 具体公司
    childrenCompany = None
    def __init__(self, name):
        Company.__init__(self,name)
        self.childrenCompany = []       # 子公司实例对象列表
    def add(self, company):
        self.childrenCompany.append(company)
    def remove(self, company):
        self.childrenCompany.remove(company)
    def display(self, depth):
        print('-'*depth + self.name)
        for component in self.childrenCompany:
            component.display(depth+1)
    def listDuty(self):
        # print("公司部门对象列表:", self.childrenCompany)
        for component in self.childrenCompany:      # 遍历列表子公司列表(列表中为子公司对象),子公司对象进行自己的方法访问
            component.listDuty()
class HRDepartment(Company):            # 人力资源部
    def __init__(self, name):
         Company.__init__(self,name)
    def display(self, depth):
        print('-'*depth + self.name)
    def listDuty(self):
        print(f'{self.name}\t Enrolling & Transfering management.')

class FinanceDepartment(Company):       # 财务部
    def __init__(self, name):
        Company.__init__(self,name)
    def display(self, depth):
        print("-" * depth + self.name)
    def listDuty(self):
        print(f'{self.name}\tFinance Management.')

class RdDepartment(Company):            # 研发部
    def __init__(self,name):
        Company.__init__(self,name)
    def display(self, depth):
        print("-"*depth+self.name)
    def listDuty(self):
        print(f"{self.name}\tResearch & Development.")


if __name__=="__main__":
    root = ConcreteCompany('HeadQuarter')   # 第一层组织架构
    root.add(HRDepartment('HQ HR'))         # 添加第二层组织架构
    root.add(FinanceDepartment('HQ Finance'))
    root.add(RdDepartment("HQ R&D"))

    comp = ConcreteCompany('East Branch')       # 先创建第二层组织架构
    comp.add(HRDepartment('East.Br HR'))        # 追加到第二层组织架构中
    comp.add(FinanceDepartment('East.Br Finance'))
    comp.add(RdDepartment("East.Br R&D"))
    root.add(comp)                      # 添加到第二层中

    comp1 = ConcreteCompany('Northast Branch')
    comp1.add(HRDepartment('Northeast.Br HR'))
    comp1.add(FinanceDepartment('Northeast.Br Finance'))
    comp1.add(RdDepartment("Northeast.Br R&D"))
    comp.add(comp1)

    comp2 = ConcreteCompany('Southeast Branch')
    comp2.add(HRDepartment('Southeast.Br HR'))
    comp2.add(FinanceDepartment('Southeast.Br Finance'))
    comp2.add(RdDepartment("Southeast.Br R&D"))
    comp.add(comp2)

    root.display(1)

    root.listDuty()

享元模式

使用共享对象支持大量细粒度对象。大量细粒度的对象的支持共享,可能会涉及这些对象的两类信息:内部状态信息和外部状态信息。
内部状态信息就是可共享出来的信息,它们存储在享元对象内部,不会随着特定环境的改变而改变;外部状态信息就不可共享的信息了。
享元模式中只包含内部状态信息,而不应该包含外部状态信息。这点在设计业务架构时,应该有所考虑。

应用场景

  1. 系统中存在大量的相似对象时,可以选择享元模式提高资源利用率。咖啡订购平台比较小,若假设一个电商平台,每个买家和卖家建立起买卖关系后,买家对象和卖家对象都是占用资源的。如果一个卖家同时与多个买家建立起买卖关系呢?此时享元模式的优势就体现出来了
  2. 需要缓冲池的场景中,可以使用享元模式。如进程池,线程池等技术,就可以使用享元模式(事实上,很多的池技术中已经使得了享元模式)。

优点

减少重复对象,大大节约了系统资源。

缺点

  1. 享元模式虽然节约了系统资源,但同时也提高了系统的复杂性,尤其当遇到外部状态和内部状态混在一起时,需要先将其进行分离,才可以使用享元模式。否则,会引起逻辑混乱或业务风险
  2. 享元模式中需要额外注意线程安全问题。

代码实例

class Coffee:
    name = ''
    price =0
    def __init__(self,name):
        self.name = name
        self.price = len(name)
    def show(self):
        print("Coffee Name:%s Price:%s"%(self.name,self.price))
# class Customer:
#     name=""
#     def __init__(self,name):
#         self.name=name
#     def order(self,coffee_name):
#         print("%s ordered a cup of coffee:%s"%(self.name,coffee_name))
#         return Coffee(coffee_name)
class Customer:         # 重写客户类,原来的客户类如上
    coffee_factory=""
    name=""
    def __init__(self,name,coffee_factory):
        self.name=name
        self.coffee_factory=coffee_factory
    def order(self,coffee_name):
        print("%s ordered a cup of coffee:%s"%(self.name,coffee_name))
        return self.coffee_factory.getCoffee(coffee_name)       # 调用咖啡工厂

class CoffeeFactory:
    coffee_dict = {}
    def getCoffee(self, name):                      # 单例模式,有则返回对象实例,没有则创建并返回对象实例
        if self.coffee_dict.get(name) is None:
            self.coffee_dict[name] = Coffee(name)
        return self.coffee_dict[name]
    def getCoffeeCount(self):
        return len(self.coffee_dict)
if __name__=="__main__":
    coffee_factory=CoffeeFactory()
    customer_1=Customer("A Client",coffee_factory)          # 1.客户1创建功工厂实例
    customer_2=Customer("B Client",coffee_factory)
    customer_3=Customer("C Client",coffee_factory)
    c1_capp=customer_1.order("cappuccino")                  # 2.工厂创建cappuccino产品实例
    c1_capp.show()                                          # 3.cappuccino产品实例进行show方法获取产品属性
    c2_mocha=customer_2.order("mocha")
    c2_mocha.show()
    c3_capp=customer_3.order("cappuccino")                  # 4.客户3再次使用工厂创建cappuccino产品实例,因为单例模式,所以返回还是之前的cappuccino产品实例
    print("客户1和客户2产品实例是否一样", id(c1_capp) == id(c3_capp))  # 客户1和客户2创建的产品实例都是一个
    c3_capp.show()
    print("Num of Coffee Instance:%s"%coffee_factory.getCoffeeCount())

桥梁模式

桥梁模式又叫桥接模式,定义如下:将抽象与实现解耦(注意此处的抽象和实现,并非抽象类和实现类的那种关系,而是一种角色的关系,这里需要好好区分一下),可以使其独立变化。在形如上例中,Pen只负责画,但没有形状,它终究是不知道要画什么的,所以我们把它叫做抽象化角色;而Shape是具体的形状,我们把它叫做实现化角色。抽象化角色和实现化角色是解耦的,这也就意味着,所谓的桥,就是抽象化角色的抽象类和实现化角色的抽象类之间的引用关系。

应用场景

  1. 不适用继承或者原继承关系中抽象类可能频繁变动的情况,可以将原类进行拆分,拆成实现化角色和抽象化角色。
    例如本例中:若将形状、粗细、绘画样式等属于汇集在一个类中,一旦抽象类中有所变动,将造成巨大的风险
  2. 重用性比较大的场景。比如开关控制逻辑的程序,开关就是抽象化角色,开关的形式有很多种,操作的实现化角色也有很多种,采用桥梁模式,(如当前例子)开关即可进行复用,整体会将设计的粒度减小。

优点

  1. 抽象角色与实现角色相分离,二者可以独立设计,不受约束
  2. 扩展性强:抽象角色和实现角色可以非常灵活地扩展。

缺点

增加对系统理解的难度。

代码实例

class Shape:                   # 形状
    name=""
    param=""
    def __init__(self,*param):
        pass
    def getName(self):
        return self.name
    def getParam(self):
        return self.name,self.param

class Pen:                      # 笔
    shape=""
    type=""
    def __init__(self,shape):
        self.shape=shape
    def draw(self):
        pass
class Rectangle(Shape):         # 长方形---实现化角色
    def __init__(self,long,width):
        self.name="Rectangle"
        self.param="Long:%s Width:%s"%(long,width)
        print("Create a rectangle:%s"%self.param)
class Circle(Shape):            # 圆形
    def __init__(self,radius):
        self.name="Circle"
        self.param="Radius:%s"%radius
        print("Create a circle:%s"%self.param)

class NormalPen(Pen):            # 普通笔---抽象画角色
    def __init__(self,shape):
        Pen.__init__(self,shape)
        self.type="Normal Line"     # 水平线
    def draw(self):             # 画画
        print("DRAWING %s:%s----PARAMS:%s"%(self.type,self.shape.getName(),self.shape.getParam()))
class BrushPen(Pen):            # 毛笔
    def __init__(self,shape):
        Pen.__init__(self,shape)
        self.type="Brush Line"
    def draw(self):
        print("DRAWING %s:%s----PARAMS:%s" % (self.type,self.shape.getName(), self.shape.getParam()))
if __name__=="__main__":
    normal_pen=NormalPen(Rectangle("20cm","10cm"))  # 抽象化角色和实现化角色是解耦的。桥,就是抽象化角色的抽象类和实现化角色的抽象类之间的引用关系。
    brush_pen=BrushPen(Circle("15cm"))
    normal_pen.draw()
    brush_pen.draw()

策略模式

定义一组算法,将每个算法都封装起来,并使他们之间可互换。以下述例子为例,customer类扮演的角色(Context上下文)直接依赖抽象策略的接口,在具体策略实现类中即可定义个性化的策略方式,且可以方便替换。

应用场景

算法策略经常地需要被替换时,可以使用策略模式。如:现在超市前台,会常遇到刷卡、某宝支付、某信支付等方式,就可以参考策略模式。

优点

  1. 各个策略可以自由切换:这也是依赖抽象类设计接口的好处之一
  2. 减少代码冗余;3、扩展性优秀,移植方便,使用灵活。

缺点

  1. 项目比较庞大时,策略可能比较多,不便于维护
  2. 策略的使用方必须知道有哪些策略,才能决定使用哪一个策略,这与迪米特法则是相违背的。

代码实例

class customer:         # 顾客
    customer_name=""
    snd_way=""
    info=""
    phone=""
    email=""
    def setPhone(self,phone):
        self.phone=phone
    def setEmail(self,mail):
        self.email=mail
    def getPhone(self):
        return self.phone
    def getEmail(self):
        return self.email
    def setInfo(self,info):
        self.info=info
    def setName(self,name):
        self.customer_name=name
    def setBrdWay(self,snd_way):        # 设置发送信息的方式
        self.snd_way=snd_way
    def sndMsg(self):
        self.snd_way.send(self.info)
class msgSender:         # 消息发送基类
    dst_code=""
    def setCode(self,code):
        self.dst_code=code
    def send(self,info):
        pass
class emailSender(msgSender):       # email发送
    def send(self,info):
        print("EMAIL_ADDRESS:%s EMAIL:%s"%(self.dst_code,info))
class textSender(msgSender):        # 文本发送
    def send(self,info):
        print("TEXT_CODE:%s EMAIL:%s"%(self.dst_code,info))
if  __name__=="__main__":
    customer_x=customer()
    customer_x.setName("CUSTOMER_X")
    customer_x.setPhone("10023456789")
    customer_x.setEmail("customer_x@xmail.com")
    customer_x.setInfo("Welcome to our new party!") # 1.上述为客户信息及要发送的内容维护
    text_sender=textSender()                        # 2.文本发送实例化
    text_sender.setCode(customer_x.getPhone())      # 3.实例化对象获取手机号
    customer_x.setBrdWay(text_sender)               # 4.客户信息中设置发送方式(设置的为实例化对象)
    customer_x.sndMsg()                             # 5.利用实例化对象调用自己的send方法来发送消息内容
    mail_sender=emailSender()
    mail_sender.setCode(customer_x.getEmail())
    customer_x.setBrdWay(mail_sender)
    customer_x.sndMsg()

责任链模式

使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

需要说明的是,责任链模式中的应该只有一个处理者,也就是说,本例中的“最终批准”为该对象所谓的“请求处理”。

应用场景

若一个请求可能由一个对请求有链式优先级的处理群所处理时,可以考虑责任链模式。
如:银行的客户请求处理系统也可以用责任链模式实现(VIP客户和普通用户处理方式当然会有不同)。

优点

将请求者与处理者分离,请求者并不知道请求是被哪个处理者所处理,易于扩展。

缺点

  1. 如果责任链比较长,会有比较大的性能问题
  2. 如果责任链比较长,若业务出现问题,比较难定位是哪个处理者的问题。

代码实例

class manager:
    successor = None
    name = ''
    def __init__(self, name):
        self.name = name
    def setSuccessor(self, successor):      # 上一级类的实例
        self.successor = successor
    def handleRequest(self, request):
        pass
class lineManager(manager):
    def handleRequest(self, request):
        if request.requestType == 'DaysOff' and request.number <= 3:        # 请假小于3天,自己处理
            print('%s:%s 天数:%d 同意并结束' % (self.name, request.requestContent, request.number))
        else:
            print('%s:%s 天数:%d 同意并继续' % (self.name, request.requestContent, request.number))
            if self.successor != None:                                      # 请假大于3天,交给继承的上级实例来处理
                self.successor.handleRequest(request)
class departmentManager(manager):
    def handleRequest(self, request):
        if request.requestType == 'DaysOff' and request.number <= 7:
            print('%s:%s 天数:%d 同意并结束' % (self.name, request.requestContent, request.number))
        else:
            print('%s:%s 天数:%d 同意并继续' % (self.name, request.requestContent, request.number))
            if self.successor != None:
                self.successor.handleRequest(request)
class generalManager(manager):              # 总经理
    def handleRequest(self, request):
        if request.requestType == 'DaysOff':
            print('%s:%s 天数:%d 同意并结束' % (self.name, request.requestContent, request.number))
class request:
    requestType = ''
    requestContent = ''
    number = 0
if  __name__=="__main__":
    line_manager = lineManager('LINE MANAGER')
    department_manager = departmentManager('DEPARTMENT MANAGER')
    general_manager = generalManager('GENERAL MANAGER')     # 1.上面为各个管理层进行初始化实例

    line_manager.setSuccessor(department_manager)
    department_manager.setSuccessor(general_manager)        # 2.绑定层级关系,参数实例为当前实例的successor属性

    req = request()
    req.requestType = 'DaysOff'
    req.requestContent = '请1天假'
    req.number = 1
    line_manager.handleRequest(req)                         # 3. 先最底层管理进行请假处理,方法中会根据请假天数进行判断是否给上级处理

    req.requestType = 'DaysOff'
    req.requestContent = '请5天假'
    req.number = 5
    line_manager.handleRequest(req)

    req.requestType = 'DaysOff'
    req.requestContent = '请10天假'
    req.number = 10
    line_manager.handleRequest(req)

命令模式

将一个请求封装成一个对象,从而可以使用不同的请求将客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。命令模式中通常涉及三类对象的抽象:Receiver,Command,Invoker(本例中的waiterSys)。

命令模式的定义中提到了“撤销和恢复功能”,也给了各位开发人员一个命令模式使用过程中的建议:各个Receiver中可以设计一个回滚接口,支持命令的“撤销”。

应用场景

触发-反馈机制的系统,都可以使用命令模式思想。
如:基于管道结构的命令系统(如SHELL),可以直接套用命令模式;此外,GUI系统中的操作反馈(如点击、键入等),也可以使用命令模式思想。

四、命令模式的缺点

优点

  1. 低耦合:调用者和接收者之间没有什么直接关系,二者通过命令中的execute接口联系
  2. 扩展性好:新命令很容易加入,也很容易拼出“组合命令”。

缺点

如果业务场景中命令比较多,那么对应命令类和命令对象的数量也会增加,这样系统会膨胀得很大。

代码实例

class backSys:                          # 后台系统
    def cook(self,dish):
        pass
class mainFoodSys(backSys):             # 后台系统-主食系统
    def cook(self,dish):
        print("MAINFOOD:Cook %s"%dish)
class coolDishSys(backSys):             # 后台系统-凉菜系统
    def cook(self,dish):
        print("COOLDISH:Cook %s"%dish)
class hotDishSys(backSys):              # 后台系统-热菜系统
    def cook(self,dish):
        print("HOTDISH:Cook %s"%dish)

class waiterSys:                        # 服务员系统
    menu_map=dict()
    commandList=[]
    def setOrder(self,command):
        print("WAITER:Add dish")
        self.commandList.append(command)

    def cancelOrder(self,command):
        print("WAITER:Cancel order...")
        self.commandList.remove(command)

    def notify(self):                   # 通知,执行菜单命令
        print("WAITER:Nofify...")
        for command in self.commandList:
            command.execute()

class Command:                          # 命令
    receiver = None
    def __init__(self, receiver):
        self.receiver = receiver
    def execute(self):
        pass
class foodCommand(Command):             # 食物命令
    dish=""
    def __init__(self,receiver,dish):
        self.receiver=receiver
        self.dish=dish
    def execute(self):
        self.receiver.cook(self.dish)

class mainFoodCommand(foodCommand):
    pass
class coolDishCommand(foodCommand):
    pass
class hotDishCommand(foodCommand):
    pass


class menuAll:                          # 菜单汇总
    menu_map=dict()
    def loadMenu(self):                 # 菜单分类定义
        self.menu_map["hot"] = ["Yu-Shiang Shredded Pork", "Sauteed Tofu, Home Style", "Sauteed Snow Peas"]
        self.menu_map["cool"] = ["Cucumber", "Preserved egg"]
        self.menu_map["main"] = ["Rice", "Pie"]
    def isHot(self,dish):               # 菜单分类判断
        if dish in self.menu_map["hot"]:
            return True
        return False
    def isCool(self,dish):
        if dish in self.menu_map["cool"]:
            return True
        return False
    def isMain(self,dish):
        if dish in self.menu_map["main"]:
            return True
        return False


if  __name__=="__main__":
    dish_list=["Yu-Shiang Shredded Pork","Sauteed Tofu, Home Style","Cucumber","Rice"]      # 点的菜列表
    waiter_sys=waiterSys()
    main_food_sys=mainFoodSys()
    cool_dish_sys=coolDishSys()
    hot_dish_sys=hotDishSys()
    menu=menuAll()                      # 1.上述为初始化获得实例
    menu.loadMenu()                     # 2.菜单定义(可以放到__init__初始化中)
    for dish in dish_list:              # 3.循环点的菜,进行分类处理
        if menu.isCool(dish):
            cmd=coolDishCommand(cool_dish_sys,dish)     # 4.获取食物命令实例对象,参数分别为:凉菜系统实例化对象、菜品
        elif menu.isHot(dish):
            cmd=hotDishCommand(hot_dish_sys,dish)
        elif menu.isMain(dish):
            cmd=mainFoodCommand(main_food_sys,dish)
        else:
            continue
        waiter_sys.setOrder(cmd)            # 5.服务员系统进行每个菜单的订单提交
    waiter_sys.notify()                     # 6.执行所有订单命令,后台系统进行每类食品的处理

中介者模式

用一个中介对象封装一系列的对象交互。中介者使各对象不需要显式地互相作用,从而使其耦合松散,并可以独立地改变它们之间的交互。

应用场景

设计类图时,出现了网状结构时,可以考虑设计成星型结构,这样就可以使用中介者模式了。
如:机场调度系统(多个跑道、飞机、指挥塔之间的调度)、路由系统;著名的MVC框架中,其中的C(Controller)就是M(Model)和V(View)的中介者。

优点

  1. 减少类与类的依赖,降低了类和类之间的耦合
  2. 容易扩展规模

缺点

中介者本身的复杂性可能会很大,例如:同事类的方法如果很多的话,本例中的execute逻辑会很复杂。

代码实例

class colleague:                        # 同事
    mediator = None
    def __init__(self,mediator):
        self.mediator = mediator
class purchaseColleague(colleague):     # 采购同事
    def buyStuff(self,num):
        print("PURCHASE:Bought %s"%num)
        self.mediator.execute("buy",num)        # 调用中介者,执行关联处理
    def getNotice(self,content):
        print("PURCHASE:Get Notice--%s"%content)
class warehouseColleague(colleague):    # 仓库同事
    total=0
    threshold=100
    def setThreshold(self,threshold):
        self.threshold=threshold        # 设置门槛库存
    def isEnough(self):
        if self.total<self.threshold:
            print("WAREHOUSE:Warning...Stock is low... ")
            self.mediator.execute("warning",self.total)
            return False
        else:
            return True
    def inc(self,num):
        self.total+=num
        print("WAREHOUSE:Increase %s"%num)
        self.mediator.execute("increase",num)
        self.isEnough()
    def dec(self,num):
        if num>self.total:
            print("WAREHOUSE:Error...Stock is not enough")
        else:
            self.total-=num
            print("WAREHOUSE:Decrease %s"%num)
            self.mediator.execute("decrease",num)
        self.isEnough()
class salesColleague(colleague):       # 销售同事
    def sellStuff(self,num):
        print("SALES:Sell %s"%num)
        self.mediator.execute("sell",num)
    def getNotice(self, content):
        print("SALES:Get Notice--%s" % content)

class abstractMediator:         # 抽象中介
    purchase=""
    sales=""
    warehouse=""
    def setPurchase(self,purchase):
        self.purchase=purchase
    def setWarehouse(self,warehouse):
        self.warehouse=warehouse
    def setSales(self,sales):
        self.sales=sales
    def execute(self,content,num):
        pass
class stockMediator(abstractMediator):  # 库存中介
    def execute(self,content,num):          # 中介者模式中的execute是最重要的方法,它根据同事类传递的信息,直接协调各个同事的工作。
        print("MEDIATOR:Get Info--%s"%content)
        if  content=="buy":
            self.warehouse.inc(num)
            self.sales.getNotice("Bought %s"%num)
        elif content=="increase":
            self.sales.getNotice("Inc %s"%num)
            self.purchase.getNotice("Inc %s"%num)
        elif content=="decrease":
            self.sales.getNotice("Dec %s"%num)
            self.purchase.getNotice("Dec %s"%num)
        elif content=="warning":
            self.sales.getNotice("Stock is low.%s Left."%num)
            self.purchase.getNotice("Stock is low. Please Buy More!!! %s Left"%num)
        elif content=="sell":
            self.warehouse.dec(num)
            self.purchase.getNotice("Sold %s"%num)
        else:
            pass
if  __name__=="__main__":
    mobile_mediator=stockMediator()                         # 1.获取中介者实例化对象
    mobile_purchase=purchaseColleague(mobile_mediator)
    mobile_warehouse=warehouseColleague(mobile_mediator)
    mobile_sales=salesColleague(mobile_mediator)            # 2,上述为采购、仓库、销售实例化对象,参数为中介者实例化对象

    mobile_mediator.setPurchase(mobile_purchase)
    mobile_mediator.setWarehouse(mobile_warehouse)
    mobile_mediator.setSales(mobile_sales)                  # 3.中介者调用对应分类方法,进行采购、仓库、销售的实例化对象存储

    mobile_warehouse.setThreshold(200)
    mobile_purchase.buyStuff(300)                           # 4.采购部要购买300库存,方法中会调用中介者进行判断协调各个部门数据处理
    mobile_sales.sellStuff(120)

模板模式

定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定的步骤。子类实现的具体方法叫作基本方法,实现对基本方法高度的框架方法,叫作模板方法。

在模板模式中,像这样类似于login等根据特定情况,定制某些特定动作的函数,被称作钩子函数。

应用场景

  1. 某父类的子类中有公有的方法,并且逻辑基本相同,可以使用模板模式。必要时可以使用钩子方法约束其行为。
  2. 比较复杂的算法,可以把核心算法提取出来,周边功能在子类中实现。
    例如,机器学习中的监督学习算法有很多,如:决策树、KNN、SVM等,但机器学习的流程大致相同,都包含输入样本、拟合(fit)、预测等过程,这样就可以把这些过程提取出来,构造模板方法,并通过钩子方法控制流程。

优点

  1. 可变的部分可以充分扩展,不变的步骤可以充分封装
  2. 提取公共代码,减少冗余代码,便于维护
  3. 具体过程可以定制,总体流程方便掌控

缺点

模板模式在抽象类中定义了子类的方法,即子类对父类产生了影响,部分影响了代码的可读性。

代码实例

class StockQueryDevice:
    stock_code="0"
    stock_price=0.0
    def login(self,usr,pwd):
        pass
    def setCode(self,code):
        self.stock_code=code
    def queryPrice(self):
        pass
    def showPrice(self):
        pass
    def operateQuery(self,usr,pwd,code):
        self.login(usr,pwd)             # 调用时,当前实例化对象是WebAStockQueryDevice,所以调用它的login、setcode等方法
        self.setCode(code)
        self.queryPrice()
        self.showPrice()
        return True

class WebAStockQueryDevice(StockQueryDevice):    # A股票查询客户端
    def login(self,usr,pwd):
        if usr=="myStockA" and pwd=="myPwdA":
            print("Web A:Login OK... user:%s pwd:%s"%(usr,pwd))
            return True
        else:
            print("Web A:Login ERROR... user:%s pwd:%s"%(usr,pwd))
            return False
    def queryPrice(self):
        print("Web A Querying...code:%s "%self.stock_code)
        self.stock_price=20.00
    def showPrice(self):
        print("Web A Stock Price...code:%s price:%s"%(self.stock_code,self.stock_price))
class WebBStockQueryDevice(StockQueryDevice):    # B股票查询客户端
    def login(self,usr,pwd):
        if usr=="myStockB" and pwd=="myPwdB":
            print("Web B:Login OK... user:%s pwd:%s"%(usr,pwd))
            return True
        else:
            print("Web B:Login ERROR... user:%s pwd:%s"%(usr,pwd))
            return False
    def queryPrice(self):
        print("Web B Querying...code:%s "%self.stock_code)
        self.stock_price=30.00
    def showPrice(self):
        print("Web B Stock Price...code:%s price:%s"%(self.stock_code,self.stock_price))

if  __name__=="__main__":
    web_a_query_dev=WebAStockQueryDevice()
    web_a_query_dev.operateQuery("myStockA","myPwdA","12345")       # 一套流程操作方法集成到父类中,可供每个实例调用

迭代器模式

提供了一种访问容器对象中各个元素的统一接口,而不必暴露容器内部的细节。

迭代器是指实现了__next__()方法的对象,这个方法返回容器中下一个元素,并在容器末尾时抛出StopIteration异常。通过使用迭代器模式,我们可以很方便地遍历序列、聚合和其他可迭代对象。

应用场景

  1. 在需要遍历序列或聚合(如列表、元组、字典等)的时候,可以使用迭代器模式。
  2. 迭代器模式可以用于处理大量数据的内存优化,实现了数据的按需加载。在Python中,生成器函数就是使用迭代器模式的常见例子。

优点

  1. 简化了遍历过程,无需关注底层实现;
  2. 降低了数据结构与遍历算法之间的耦合性;
  3. 支持延迟加载,节省内存资源。

缺点

  1. 迭代器只能遍历容器中的数据,不能修改数据;
  2. 迭代器只能单向遍历,不能反向遍历。

代码实例

class List():       # 列表迭代器
    def __init__(self, list):
        self.list = list

    def get_interator(self):  # 获取迭代器
        return Iterator(self.list)

class Iterator:         # 定义迭代器
    def __init__(self, list):
        self.list = list
        self.index = 0

    def __next__(self):
        if self.index >= len(self.list):
            raise StopIteration
        lt = self.list[self.index]
        self.index += 1
        return lt

lt = List([11,22,55])
count = lt.get_interator()
print(next(count))
print(next(count))
print(next(count))

class DictInterator():        # 字典迭代器
    def __init__(self, dic):
        self.current = 0
        self.keys = list(dic.keys())
        self.values = list(dic.values())

    def __iter__(self):  # 返回迭代器对象本身
        print("返回迭代器对象本身")
        return self

    def __next__(self):  # 定义遍历逻辑
        if self.current == len(self.keys):
            print(f"当前第 {self.current} 个数据,停止遍历")
            raise StopIteration
        else:
            key_result = self.keys[self.current]
            value_result = self.values[self.current]
            self.current += 1
            return key_result, value_result
if __name__== "__main__":
    my_dct = {"a": 1, "b": 2, "c": 3}
    my_iter = DictInterator(my_dct)
    print(next(my_iter))
    print(next(my_iter))
    for key, value in my_iter:
        print(key, value)
    # 相当于:
    for key in my_dct:
        print(key, my_dct[key])

访问者模式

封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义于作用于这些元素的新操作。

提到访问者模式,就不得不提一下双分派。分派分为静态分派和动态分派。首先解释下静态分派,静态分派即根据请求者的名称和接收到的参数,决定多态时处理的操作。比如在Java或者C++中,定义名称相同但参数不同的函数时,会根据最终输入的参数来决定调用哪个函数。双分派顾名思义,即最终的操作决定于两个接收者的类型,在本例中,药品和工作人员互相调用了对方(药品的accept和工作人员的visit中,对方都是参数),就是双分派的一种应用。
Python原生是不支持静态分派的,因而也不直接支持更高层次的分派。访问者模式实现的分派,是一种动态双分派。但这并不妨碍Python通过访问者模式实现一种基于类的“双分派效果”。

应用场景

要遍历不同的对象,根据对象进行不同的操作的场景;或者一个对象被多个不同对象顺次处理的情况,可以考虑使用访问者模式。
如:报表生成器也可以使用访问者模式实现,报表的数据源由多个不同的对象提供,每个对象都是Visitor,报表这个Element顺次Accept各访问者完善并生成对象。

优点

  1. 将不同的职责非常明确地分离开来,符合单一职责原则
  2. 职责的分开也直接导致扩展非常优良,灵活性非常高,加减元素和访问者都非常容易。

缺点

  1. 访问者得知了元素细节,与最小隔离原则相悖
  2. 元素变更依旧可能引起Visitor的修改。

代码实例

class Medicine:     # 药品
    name=""
    price=0.0
    def __init__(self,name,price):
        self.name=name
        self.price=price
    def getName(self):
        return self.name
    def setName(self,name):
        self.name=name
    def getPrice(self):
        return self.price
    def setPrice(self,price):
        self.price=price
    def accept(self,visitor):
        pass
class Antibiotic(Medicine):         # 药品---抗生素
    def accept(self,visitor):
        visitor.visit(self)
class Coldrex(Medicine):            # 药品---感冒药
    def accept(self,visitor):
        visitor.visit(self)         # 访问者实例化进行访问自己的visit方法,参数为传入自身
class Visitor:                      # 访问者
    name=""
    def setName(self,name):
        self.name=name
    def visit(self,medicine):
        pass
class Charger(Visitor):             # 划价员
    def visit(self,medicine):
        # 传参为自身,用自身方法获取药品数据
        print("CHARGE: %s lists the Medicine %s. Price:%s " % (self.name,medicine.getName(),medicine.getPrice()))
class Pharmacy(Visitor):            # 药品管理者
    def visit(self,medicine):
        print("PHARMACY:%s offers the Medicine %s. Price:%s" % (self.name,medicine.getName(),medicine.getPrice()))

class ObjectStructure:
    pass
class Prescription(ObjectStructure):        # 药方
    medicines=[]
    def addMedicine(self,medicine):
        self.medicines.append(medicine)
    def rmvMedicine(self,medicine):
        self.medicines.append(medicine)
    def visit(self,visitor):                # 参数为访问者实例
        for medc in self.medicines:
            medc.accept(visitor)

if __name__=="__main__":
    yinqiao_pill=Coldrex("Yinqiao Pill",2.0)        # 1.感冒药实例化
    penicillin=Antibiotic("Penicillin",3.0)
    doctor_prsrp=Prescription()                     # 2.药方实例化
    doctor_prsrp.addMedicine(yinqiao_pill)          # 3.药品加入到药方中,存储到列表中
    doctor_prsrp.addMedicine(penicillin)
    charger=Charger()
    charger.setName("Doctor Strange")               # 4.划价员实例化
    pharmacy=Pharmacy()
    pharmacy.setName("Doctor Wei")
    # 5.药方去循环药品实例列表->实例去访问自己方法,参数为访问者->在用参数的访问者实例去打印数据,并且参数为自身,用于后面访问者实例中打印自身的数据
    doctor_prsrp.visit(charger)
    doctor_prsrp.visit(pharmacy)

观察者模式

观察者模式也叫发布-订阅模式,其定义如下:定义对象间一种一对多依赖关系,使得当该对象状态改变时,所有依赖于它的对象都会得到通知,并被自动更新。观察者模式的通知方式可以通过直接调用等同步方式实现(如函数调用,HTTP接口调用等),也可以通过消息队列异步调用(同步调用指被观察者发布消息后,必须等所有观察者响应结束后才可以进行接下来的操作;异步调用指被观察者发布消息后,即可进行接下来的操作。)。事实上,许多开源的消息队列就直接支持发布-订阅模式,如Zero MQ等。

应用场景

  1. 消息交换场景。如上述说到的消息队列等
  2. 多级触发场景。比如支持中断模式的场景中,一个中断即会引发一连串反应,就可以使用观察者模式。

优点

  1. 观察者与被观察者之间是抽象耦合的
  2. 可以将许多符合单一职责原则的模块进行触发,也可以很方便地实现广播。

缺点

  1. 观察者模式可能会带来整体系统效率的浪费
  2. 如果被观察者之间有依赖关系,其逻辑关系的梳理需要费些心思。

代码实例

class Observer:
    def update(self):
        pass
class AlarmSensor(Observer):    # 报警传感器
    def update(self,action):
        print("Alarm Got: %s" % action)
        self.runAlarm()
    def runAlarm(self):
        print("Alarm Ring...")
class WaterSprinker(Observer):  # 喷水传感器
    def update(self,action):
        print("Sprinker Got: %s" % action)
        self.runSprinker()
    def runSprinker(self):
        print("Spray Water...")
class EmergencyDialer(Observer):    # 紧急电话传感器
    def update(self,action):
        print("Dialer Got: %s"%action)
        self.runDialer()
    def runDialer(self):
        print("Dial 119...")

class Observed:                     # 观察者
    observers=[]
    action=""
    def addObserver(self,observer):
        self.observers.append(observer)
    def notifyAll(self):
        for obs in self.observers:
            obs.update(self.action)
class smokeSensor(Observed):        # 烟雾传感器
    def setAction(self,action):
        self.action=action
    def isFire(self):
        return True

if __name__=="__main__":
    alarm=AlarmSensor()
    sprinker=WaterSprinker()
    dialer=EmergencyDialer()            # 1.被观察者进行实例化

    smoke_sensor=smokeSensor()          # 2.观察者进行实例化
    smoke_sensor.addObserver(alarm)
    smoke_sensor.addObserver(sprinker)
    smoke_sensor.addObserver(dialer)    # 3.被观察者加入观察者数组中


    if smoke_sensor.isFire():           # 4.观察者进行触发,并通知所有观察者
        smoke_sensor.setAction("On Fire!")
        smoke_sensor.notifyAll()

解释器模式

给定一种语言,定义它的文法表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。典型的解释器模式中会有终结符和非终结符之说,语法也根据两种终结符,决定语句最终含义。如下例中,非终结符就是空格,终结符就是整个句尾。

应用场景

  1. 若一个问题重复发生,可以考虑使用解释器模式。这点在数据处理和日志处理过程中使用较多,当数据的需求方需要将数据纳为己用时,必须将数据“翻译”成本系统的数据规格;同样的道理,日志分析平台也需要根据不同的日志格式翻译成统一的“语言”。
  2. 特定语法解释器。如各种解释型语言的解释器,再比如自然语言中基于语法的文本分析等。

优点

在语法分析的场景中,具有比较好的扩展性。规则修改和制订比较灵活。

缺点

  1. 解释规则多样化会导致解释器的爆炸
  2. 解释器目标比较单一,行为模式比较固定,因而重要的模块中尽量不要使用解释器模式。

代码实例

   def execute(self,play_key,play_value):      # 执行演奏
        pass

class NormGuitar(Expression):
    def execute(self, key, value):
        print("Normal Guitar Playing--Chord:%s Play Tune:%s"%(key,value))

if __name__=="__main__":
    context = PlayContext()
    context.play_text = "C53231323 Em43231323 F43231323 G63231323"
    guitar=NormGuitar()
    guitar.interpret(context)

备忘录模式

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原来保存的状态。
在备忘录模式中,如果要保存的状态多,可以创造一个备忘录管理者角色来管理备忘录。

应用场景

  1. 需要保存和恢复数据的相关状态场景。如下:
  • 保存游戏状态的场景
  • 撤销场景,如Ctrl-Z操作
  • 事务回滚的应用
    一般情况下事务回滚有两种方式:(两种方式各有优缺点,要结合业务场景,决定使用哪种模式)
    • 一是把从恢复点开始的操作都反向执行一遍
    • 二是直接恢复到恢复点的各种状态。
  1. 副本监控场景。备忘录可以当作一个临时的副本监控,实现非实时和准实时的监控。

代码实例

import random
class GameCharacter():      # 游戏角色
    vitality = 0
    attack = 0
    defense = 0
    def displayState(self):
        print('Current Values:')
        print('Life:%d' % self.vitality)
        print('Attack:%d' % self.attack)
        print('Defence:%d' % self.defense)
    def initState(self,vitality,attack,defense):
        self.vitality = vitality
        self.attack = attack
        self.defense = defense
    def saveState(self):                # 保存备忘录
        return Memento(self.vitality, self.attack, self.defense)
    def recoverState(self, memento):    # 从备忘录实例中获取之前保存的数据
        self.vitality = memento.vitality
        self.attack = memento.attack
        self.defense = memento.defense

class FightCharactor(GameCharacter):        # 战斗动作
    def fight(self):
        self.vitality -= random.randint(1,10)

class Memento:          # 备忘录模式
    vitality = 0
    attack = 0
    defense = 0
    def __init__(self, vitality, attack, defense):
        self.vitality = vitality
        self.attack = attack
        self.defense = defense

if __name__=="__main__":
    game_chrctr = FightCharactor()
    game_chrctr.initState(100,79,60)
    game_chrctr.displayState()
    memento = game_chrctr.saveState()       # 1.保存备忘录,并返回备忘录实例
    game_chrctr.fight()                     # 2.修改数据
    game_chrctr.displayState()
    game_chrctr.recoverState(memento)       # 3.从备忘录实例中获取数据并恢复
    game_chrctr.displayState()

状态模式

当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。

应用场景

行为状态改变的场景。这点在各种控制器中非常常见,同时,逻辑结构为状态转移图的场景中都非常适用。

优点

  1. 状态模式的优点是结构清晰,相比于if…else…简约了不少
  2. 封装性好,外部调用不必知道内部实现细节。

缺点

在状态比较多时,子类也会非常多,不便于管理。

代码实例

class LiftState:                # 电梯状态
    def open(self):
        pass
    def close(self):
        pass
    def run(self):
        pass
    def stop(self):
        pass

class OpenState(LiftState):     # 打开状态
    def open(self):
        print("OPEN:The door is opened...")
        return self
    def close(self):
        print("OPEN:The door start to close...")
        print("OPEN:The door is closed")
        return StopState()
    def run(self):
        print("OPEN:Run Forbidden.")
        return self
    def stop(self):
        print("OPEN:Stop Forbidden.")
        return self
class RunState(LiftState):      # 运行状态
    def open(self):
        print("RUN:Open Forbidden.")
        return self
    def close(self):
        print("RUN:Close Forbidden.")
        return self
    def run(self):
        print("RUN:The lift is running...")
        return self
    def stop(self):
        print("RUN:The lift start to stop...")
        print("RUN:The lift stopped...")
        return StopState()
class StopState(LiftState):     # 停止状态
    def open(self):
        print("STOP:The door is opening...")
        print("STOP:The door is opened...")
        return OpenState()     # 规则定义,停止状态方法中是可以open的,返回OpenState实例,调度方法中会设置状态为open
    def close(self):
        print("STOP:Close Forbidden")
        return self      # 规则定义,停止状态方法中不可以close关闭,返回当前StopState实例,调度方法中会设置状态为stop(其实实例未变化)
    def run(self):
        print("STOP:The lift start to run...")
        return RunState()
    def stop(self):
        print("STOP:The lift is stopped.")
        return self

class Context:              # 调度状态转移,上下文记录
    lift_state=""
    def getState(self):
        return self.lift_state
    def setState(self,lift_state):
        self.lift_state=lift_state
    def open(self):
        self.setState(self.lift_state.open())
    def close(self):
        self.setState(self.lift_state.close())
    def run(self):
        self.setState(self.lift_state.run())
    def stop(self):
        self.setState(self.lift_state.stop())

if __name__=="__main__":
    ctx = Context()
    ctx.setState(StopState())       # 1.设置当前状态为停止状态,参数为停止状态实例对象
    # 2.调度器open方法,会调用停止状态实例对象中的open,并返回实例对象,并根据返回实例对象来修改当前状态(具体open状态方法中会定义规则能否打开)
    ctx.open()
    ctx.run()       # 3.OpenState方法中定义run方法不可执行,所以调度器当前状态还是OpenState实例
    ctx.close()     # 4.当前OpenState实例,同理close可执行,并返回StopState实例
    ctx.run()
    ctx.stop()

设计原则

六大设计原则

在法理学中,法律规则与法律原则都是法律规范的重要构成。但二者也会有些不同:法律规则是指采取一定的结构形式具体规定人们的法律权利、法律义务以及相应的法律后果的行为规范,内容比较明确,比如,交通法规中规定,禁止闯红灯;法律原则是指在一定法律体系中作为法律规则的指导思想,基本或本原的、综合的、稳定的原理和准则,内容上只包含“大方针”,而并未有具体规则,比如,如果车上有马上临产的孕妇,闯红灯不会被处罚,这是符合重视生命的原则。设计模式与设计原则,基本符合规则与原则的关系设计模式一个个具体问题的解决方案设计原则则反映了这些设计模式的指导思想;同时,设计原则可衍生出的设计模式也不仅限于上述介绍到了23种设计模式,任何一种针对特定业务场景中的解决方法,虽然找不到对应的设计模式与之匹配,但若符合设计原则,也可以认为是一种全新的设计模式。从这个意义上来说,设计模式是程序设计方法的形,而设计原则是程序设计方法的神。

单一职责原则:

单一职责原则英文原名为Single Responsibility Principle,简称SRP原则。其含义为:应该有且仅有一个原因引起类的变更。举个例子来说明单一职责原则:一个视频播放系统,一个客户端类有两个功能接口,即视频播放接口和音频播放接口。虽然这样的设计很常见,但却不满足单一职责原则的。原因是,如果对视频播放有变更需求或者对音频播放有修改需求,都会变更视频客户端的类结构。符合单一原则的设计是,将视频播放单元和音频播放单元各建一个类,播放客户端继承两个类,构成客户端。单一职责原则的最大难点在于职责的划分,试想,以上划分是否是符合单一职责了?既是,也不是。试想,如果将视频传输和音频传输的协议信息和数据信息区分开,为符合这种粒度的单一职责原则就必须要有协议传输类和数据传输类的划分。如果接着细分,可能一个简单的小模块,都要设计非常多的类。因此,单一职责原则粒度的选择,应该根据业务流程和人员分工来进行考虑。一些基本的划分,似乎已经成了行业规范性的内容,比如,业务逻辑用户信息管理的划分等。

里氏替换原则:

里氏替换原则英文原名为Liskov Substitution Principle,简称LSP原则。它是面向对象设计的最为基本原则之一。
里氏替换原则的含义为:任何基类可以出现的地方,子类一定可以出现。
LSP是继承复用的基石,只有当子类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,子类也能够在基类的基础上增加新的行为。举例说明:对于一个鸟类,可以衍生出麻雀、喜鹊、布谷等子类,这些子类都可继承鸟类的鸣叫、飞行、吃食等接口。而对于一个鸡类,虽然它在生物学上属于鸟类,但它不会飞,那么符合LSP设计原则的情况下,鸡就不应该是鸟的一个子类:在鸟类调用飞行接口的地方,鸡类并不能出现。如果鸡类要使用鸟类的接口,应该使用关联关系,而不是继承关系。

依赖倒置原则:

依赖倒置原则英文原名为Dependence Inversion Principle,简称DIP原则。它的含义为:高层模块不应该依赖于低层模块,两者都应该依赖其抽象。抽象不应该依赖于细节,细节应该依赖于抽象。我们将每个不可细分的逻辑叫作原子逻辑,原子逻辑组装,形成低层模块,低层模块组装形成高层模块。依赖倒置原则的含义为,高层模块和低层模块都应该由各自的抽象模块派生而来,同时接口设计应该依赖于抽象,而非具体模块。举个例子:司机与汽车是依赖的关系,司机可以有实习司机类、老司机类等派生;汽车可以有轿车、SUV、卡车等派生类。如果司机中设计一个接口drive,汽车是其参数,符合DIP设计原则的参数,应该是在基类司机类中,将基类汽车类作为参数,而司机的派生类中,drive的参数同样应该为基类汽车类,而不应该是汽车类的任一个派生类。如果规定实习司机只能开轿车等业务逻辑,应该在其接口中进行判断,而不应该将参数替换成子类轿车。

接口隔离原则:

接口隔离原则英文原名为Interface Segregation Principle,简称ISP原则。其含义为:类间的依赖关系不应该建立一个大的接口,而应该建立其最小的接口,即客户端不应该依赖那些它不需要的接口。这里的接口的概念是非常重要的。从逻辑上来讲,这里的接口可以指一些属性和方法的集合;从业务上来讲,接口就可以指特定业务下的接口(如函数,URL调用等)。接口应该尽量小,同时仅留给客户端必要的接口,弃用没有必要的接口。举例说明:如果要根据具体的数据,生成饼图、直方图、表格,这个类该如何设计?如果将生成饼图、直方图、表格等“接口”(这里的接口就是“操作”的集合的概念),写在一个类中,是不符合接口隔离原则的。符合ISP原则的设计应该是设计三个类,每个类分别实现饼图、直方图、表格的绘制。接口隔离原则和单一职责原则一样,涉及到粒度的问题,解决粒度大小,同样依赖于具体的业务场景,需要读者根据实践去权衡。

迪米特法则(最少知识原则):

迪米特法则(Law of Demeter)也叫最少知识原则,英文Least Knowledge Principle,简称LKP原则。其含义为:一个对象应该对其它对象有最少的了解。举例说明:一个公司有多个部门,每个部门有多个员工,如果公司CEO要下发通知给每个员工,是调用接口直接通知所有员工么?其实不然,CEO只需和它的“朋友”类部门Leader交流就好,部门Leader再下发通知信息即可。而CEO类不需要与员工进行“交流”。
迪米特法则要求对象应该仅对自己的朋友类交流,而不应该对非朋友类交流。那什么才是朋友类呢?
一般来说,朋友类具有以下特征:
1)当前对象本身(self);
2)以参量形式传入到当前对象方法中的对象;
3)当前对象的实例变量直接引用的对象;
4)当前对象的实例变量如果是一个聚集,那么聚集中的元素也都是朋友;
5)当前对象所创建的对象。

开闭原则:

开闭原则英文原名为Open ClosedPrinciple,简称OCP原则。其含义为:一个软件实体,如类、模块、函数等,应该对扩展开放,对修改关闭。开闭原则是非常基础的一个原则,也有人把开闭原则称为“原则的原则”。前面讲到过,模块分原子模块,低层模块,高层模块,业务层可以认为是最高层次的模块。对扩展开放,意味着模块的行为是可以扩展的,当高层模块需求改变时,我们可以对低层模块进行扩展,使其具有满足高层模块的新功能;对修改关闭,即对低层模块行为进行扩展时,不必改动模块的源代码。最理想的情况是,业务变动时,仅修改业务代码,不修改依赖的模块(类、函数等)代码,通过扩展依赖的模块单元来实现业务变化。举例说明:假设一个原始基类水果类,苹果类是它的派生类,苹果中包含水果的各种属性,如形状、颜色等;另有两个类,农民类和花园类,最高层次(业务层次)为农民在花园种苹果。如果此时,农民决定不种苹果了,改种梨,符合OCP原则的设计应该为基于水果类构建一个新的类,即梨类(对扩展开放),而并不应该去修改苹果类,使它成为一个梨类(对修改关闭)。修改应仅在最高层,即业务层中进行。

遵循设计原则的好处

由于设计原则是设计模式的提炼,因而设计原则的好处与设计模式是一致的,即:代码易于理解;更适于团体合作;适应需求变化等。

设计原则与设计模式

创建类设计模式与设计原则

  • 工厂模式:工厂方法模式是一种解耦结构,工厂类只需要知道抽象产品类,符合最少知识原则(迪米特法则);同时符合依赖倒置原则和里氏替换原则;
  • 抽象工厂模式:抽象工厂模式具有工厂模式的优点,但同时,如果产品族要扩展,工厂类也要修改,违反了开闭原则;
  • 模板模式:优秀的扩展能力符合开闭原则。

结构类设计模式与设计原则

  • 代理模式:代理模式在业务逻辑中将对主体对象的操作进行封装,合适的应用会符合开闭原则和单一职责原则;事实上,几乎带有解耦作用的结构类设计模式都多少符合些开闭原则;
  • 门面模式:门面模式不符合开闭原则,有时不符合单一职责原则,如若不注意,也会触碰接口隔离原则;
  • 组合模式:符合开闭原则,但由于一般在拼接树时使用实现类,故不符合依赖倒置原则;
  • 桥梁模式:桥梁模式堪称依赖倒置原则的典范,同时也符合开闭原则。

行为类设计模式与设计原则

策略模式:符合开闭原则,但高层模块调用时,不符合迪米特法则。行为类设计模式多少会符合些单一职责原则,典型的如观察者模式、中介者模式、访问者模式等;
责任链模式:符合单一职责原则和迪米特法则;
命令模式:符合开闭原则。

在不同的业务逻辑中,不同的设计模式也会显示出不同的设计原则特点,从这个意义上来说,设计模式是设计原则的体现,但体现不是固定的,是根据业务而有所不同的。

posted @ 2024-01-22 16:46  三叔测试笔记  阅读(9)  评论(0编辑  收藏  举报
返回顶部 跳转底部