Python 设计模式(结构型)

文章目录

  • 代理模式
    • 场景
    • 示例
  • 门面模式
    • 场景
    • 示例
  • 桥接模式
    • 场景
    • 示例
  • 适配器模式
    • 场景
    • 示例
  • 外观模式
    • 对比门面模式
    • 场景
    • 示例
  • 享元模式
    • 场景
    • 示例
  • 装饰器模式
    • 场景
    • 示例
  • 组合模式
    • 场景
    • 示例

代理模式

在Python中,代理模式是一种结构型设计模式,它允许你提供一个代理对象,控制对另一个对象的访问。代理通常充当客户端和实际对象之间的中介,客户端通过代理间接访问实际对象,从而在访问过程中增加了一层间接性。

  1. 虚拟代理:当对象创建代价较高时,可以使用虚拟代理延迟对象的创建,直到客户端真正需要访问它时才进行创建。这样可以提高系统的性能和资源利用率。

  2. 保护代理:保护代理控制对对象的访问,可以在客户端访问对象之前执行额外的验证或权限检查。这种方式可以确保只有具有适当权限的客户端才能访问对象。

  3. 远程代理:远程代理允许在不同地址空间中的对象进行通信。客户端通过代理访问远程对象,代理在本地转发请求并获取远程对象的结果,这样就隐藏了远程通信的细节。

场景

  1. 延迟加载(Lazy Loading):当对象的创建和初始化成本很高时,可以使用代理模式延迟对象的创建,直到真正需要使用该对象时再进行初始化。这种延迟加载可以提高系统的性能和资源利用率。

  2. 权限控制:代理模式可以用于实现权限控制,确保只有具有适当权限的用户可以访问某些对象或执行某些操作。代理对象可以在执行实际操作之前验证用户的权限,并根据情况决定是否允许访问。

  3. 缓存:代理模式可以用于实现缓存功能,代理对象可以在执行实际操作之前检查缓存,如果缓存中存在所需的数据,则直接返回缓存中的数据,从而减少对实际对象的访问次数,提高系统的响应速度。

  4. 远程代理:当需要在不同地址空间中的对象之间进行通信时,可以使用远程代理模式。代理对象可以在本地代理远程对象的访问,隐藏了远程通信的细节,使得客户端可以像访问本地对象一样访问远程对象。

  5. 日志记录:代理模式可以用于实现日志记录功能,代理对象可以在执行实际操作之前记录相关的日志信息,例如请求参数、执行结果等,从而方便后续的跟踪和分析。

示例

# -*- coding: utf-8 -*-# 主题接口
class Account:def transfer(self, amount, to_account):pass# 真实账户
class RealAccount(Account):def __init__(self, balance):self.balance = balancedef transfer(self, amount, to_account):if self.balance >= amount:self.balance -= amountprint("转账成功!剩余余额:{}元".format(self.balance))else:print("转账失败,余额不足!")# 账户代理
class ProxyAccount(Account):def __init__(self, real_account):self.real_account = real_accountdef transfer(self, amount, to_account):if amount > 10000:print("转账金额超过1万元,需要进行额外的验证...")if self.verify():self.real_account.transfer(amount, to_account)else:print("转账验证失败,无法进行转账!")else:self.real_account.transfer(amount, to_account)def verify(self):# 模拟额外的验证逻辑,这里简单地返回Truereturn True# 客户端代码
if __name__ == "__main__":real_account = RealAccount(20000)  # 创建真实账户,初始余额20000元proxy_account = ProxyAccount(real_account)  # 创建代理账户# 转账10000元,不需要额外验证proxy_account.transfer(10000, "to_account")# 转账20000元,需要额外验证proxy_account.transfer(20000, "to_account")
  • 输出结果
转账成功!剩余余额:10000元
转账金额超过1万元,需要进行额外的验证...
转账失败,余额不足!

门面模式

门面模式是一种结构型设计模式,它提供了一个统一的接口,用于访问子系统中的一组接口。门面模式的核心思想是将复杂的子系统隐藏在一个简单的接口后面,使得客户端可以更容易地使用这些功能而不必了解其内部的复杂实现细节。

在门面模式中,通常会有一个称为门面(Facade)的类,它封装了对子系统的访问,并提供了一个简单的接口供客户端使用。客户端通过门面类来间接地访问子系统,而不必直接与子系统中的各个组件交互。

  1. 简化接口:门面模式可以为客户端提供一个简单的接口,隐藏了子系统的复杂性,使得客户端可以更容易地使用子系统的功能。

  2. 解耦合:门面模式可以将客户端与子系统解耦合,客户端不需要了解子系统的具体实现细节,只需要通过门面类来访问子系统,这样可以降低系统的耦合度。

  3. 提高可维护性:通过将子系统的功能封装在门面类中,可以更容易地对子系统进行修改和维护,而不会影响到客户端。

  4. 提高安全性:门面模式可以限制客户端对子系统的访问,只暴露必要的接口给客户端使用,从而提高系统的安全性。

场景

  1. 复杂的库或框架:当你使用一个复杂的库或框架时,可能会面临许多繁琐的配置和初始化步骤。通过使用门面模式,你可以封装这些复杂的初始化过程,并提供一个简单的接口供用户使用,从而简化了库或框架的使用。

  2. 子系统调用:当你需要使用一个包含多个子系统的大型系统时,可能需要频繁地调用多个子系统来完成任务。通过使用门面模式,你可以将这些子系统的调用封装在一个门面类中,并提供一个统一的接口供用户调用,从而简化了系统的使用。

  3. 简化接口:当一个类或模块拥有复杂的接口时,可以使用门面模式来提供一个更简单的接口供用户使用。这样可以降低用户学习成本,并提高代码的可读性和可维护性。

  4. 解耦合:当一个类或模块依赖于多个其他类或模块时,可能会出现紧耦合的情况。通过使用门面模式,你可以将这些依赖关系封装在一个门面类中,并提供一个统一的接口供用户调用,从而降低了类或模块之间的耦合度。

  5. 系统扩展:当你需要扩展一个系统时,可能会涉及到修改和添加多个类或模块。通过使用门面模式,你可以将这些修改和添加的过程封装在一个门面类中,并提供一个统一的接口供用户调用,从而简化了系统的扩展过程。

示例

# -*- coding: utf-8 -*-# 投影仪类
class Projector:def on(self):print("投影仪已开启")def off(self):print("投影仪已关闭")# 音响类
class AudioSystem:def on(self):print("音响已开启")def off(self):print("音响已关闭")# 灯光类
class Light:def on(self):print("灯光已开启")def off(self):print("灯光已关闭")# 家庭影院门面类
class HomeTheaterFacade:def __init__(self, projector, audio_system, light):self.projector = projectorself.audio_system = audio_systemself.light = lightdef watch_movie(self):print("准备观影...")self.projector.on()self.audio_system.on()self.light.off()def end_movie(self):print("结束观影...")self.projector.off()self.audio_system.off()self.light.on()# 客户端代码
if __name__ == "__main__":projector = Projector()audio_system = AudioSystem()light = Light()theater_facade = HomeTheaterFacade(projector, audio_system, light)theater_facade.watch_movie()print("\n--- 观影中 ---\n")theater_facade.end_movie()
  • 输出结果
准备观影...
投影仪已开启
音响已开启
灯光已关闭--- 观影中 ---结束观影...
投影仪已关闭
音响已关闭
灯光已开启

桥接模式

桥接模式是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立变化。桥接模式的核心思想是将一个大类或一组类拆分为两个独立的层次结构:抽象部分和实现部分,这两个部分可以分别进行扩展和修改,而不会相互影响。

在桥接模式中,通常会有一个称为抽象类的类(Abstraction),它包含一个指向实现类的引用,并定义了一组抽象方法,用于定义抽象部分的接口。然后有一个称为实现类的接口(Implementor),它定义了实现部分的接口。抽象类和实现类之间通过组合关系关联在一起,而不是通过继承关系。

  1. 分离抽象与实现:桥接模式将抽象部分与实现部分分离,使它们可以独立变化,从而提高了系统的灵活性和可扩展性。

  2. 简化继承关系:通过将抽象类和实现类分离,桥接模式避免了多重继承的复杂性,使系统更加清晰和易于理解。

  3. 优化扩展性:桥接模式使得抽象部分和实现部分可以独立地进行扩展和修改,从而更容易地实现新的功能或变化。

  4. 提高复用性:桥接模式可以将抽象部分和实现部分进行组合,从而实现不同组合方式的复用,提高了代码的复用性。

场景

  1. 多平台支持:当你需要为一个软件系统实现多平台支持时,可以使用桥接模式。你可以将不同平台的具体实现作为实现部分,而将通用的功能作为抽象部分,从而实现不同平台之间的解耦合,简化系统的维护和扩展。

  2. 数据库驱动程序:在开发数据库应用程序时,经常需要连接不同的数据库管理系统(如MySQL、PostgreSQL、SQLite等)。你可以使用桥接模式将不同数据库的具体实现作为实现部分,而将通用的数据库访问功能作为抽象部分,从而实现对不同数据库的透明访问。

  3. 图形界面工具包:在开发图形界面应用程序时,经常需要使用不同的图形界面工具包(如Tkinter、PyQt、wxPython等)。你可以使用桥接模式将不同工具包的具体实现作为实现部分,而将通用的界面操作功能作为抽象部分,从而实现对不同工具包的透明支持。

  4. 远程服务调用:在开发分布式系统时,经常需要调用远程服务(如Web服务、RESTful API等)。你可以使用桥接模式将不同远程服务的具体实现作为实现部分,而将通用的服务调用功能作为抽象部分,从而实现对不同远程服务的透明调用。

  5. 设备驱动程序:在开发设备驱动程序时,经常需要支持不同的硬件设备(如打印机、扫描仪、摄像头等)。你可以使用桥接模式将不同设备的具体实现作为实现部分,而将通用的设备操作功能作为抽象部分,从而实现对不同设备的透明访问。

示例

# -*- coding: utf-8 -*-# 形状抽象类
class Shape:def __init__(self, drawer):self.drawer = drawerdef draw(self):pass# 绘制器接口
class Drawer:def draw_shape(self):pass# 圆形类
class Circle(Shape):def draw(self):print("绘制圆形...")self.drawer.draw_shape()# 矩形类
class Rectangle(Shape):def draw(self):print("绘制矩形...")self.drawer.draw_shape()# 绘制器A:绘制圆形
class DrawerA(Drawer):def draw_shape(self):print("使用绘制器A绘制圆形")# 绘制器B:绘制矩形
class DrawerB(Drawer):def draw_shape(self):print("使用绘制器B绘制矩形")# 客户端代码
if __name__ == "__main__":drawer_a = DrawerA()drawer_b = DrawerB()circle = Circle(drawer_a)circle.draw()rectangle = Rectangle(drawer_b)rectangle.draw()
  • 输出结果
绘制圆形...
使用绘制器A绘制圆形
绘制矩形...
使用绘制器B绘制矩形

适配器模式

适配器模式是一种结构型设计模式,它允许接口不兼容的类能够一起工作。适配器模式通过引入一个适配器来解决接口不兼容的问题,使得原本不兼容的类可以协同工作。

适配器模式的核心思想是将一个类的接口转换成另一个类的接口,从而使得两个类能够相互合作。适配器模式通常有两种实现方式:类适配器和对象适配器。

  1. 类适配器:通过多重继承来实现适配器。适配器类继承自目标接口类,并同时持有源接口类的一个实例。

  2. 对象适配器:通过组合关系来实现适配器。适配器类持有源接口类的一个实例,并实现目标接口类的接口。

场景

  1. 第三方库或框架的接口适配:当你需要使用一个第三方库或框架,但其接口与你的系统不兼容时,可以使用适配器模式将其接口适配成你系统需要的接口。

  2. 系统集成:当你需要集成多个系统或组件时,可能会遇到接口不兼容的问题。通过使用适配器模式,你可以将各个系统或组件的接口适配成统一的接口,从而实现系统间的协同工作。

  3. 旧接口的转换:当你需要使用一个旧版本的接口,但你的系统已经使用了新版本的接口时,可以使用适配器模式将旧版本的接口适配成新版本的接口,从而实现对旧版本接口的兼容性。

  4. 数据库驱动程序的适配:在开发数据库应用程序时,可能需要连接不同的数据库管理系统,不同的数据库管理系统通常会提供不同的接口。通过使用适配器模式,你可以将不同数据库管理系统的接口适配成统一的接口,从而实现对不同数据库的透明访问。

  5. 服务调用的适配:在进行远程服务调用时,可能会遇到服务接口不兼容的问题。通过使用适配器模式,你可以将不同服务的接口适配成统一的接口,从而实现对不同服务的透明调用。

示例

# -*- coding: utf-8 -*-# 旧邮件发送器
class OldMailer:def send(self, to, message):print("发送邮件到:{}".format(to))print("邮件内容:{}".format(message))# 新邮件发送器
class NewMailer:def send_to_multiple(self, to_list, message):print("发送邮件到多个地址:{}".format(", ".join(to_list)))print("邮件内容:{}".format(message))# 适配器类
class MailerAdapter:def __init__(self, new_mailer):self.new_mailer = new_mailerdef send(self, to, message):self.new_mailer.send_to_multiple([to], message)# 客户端代码
if __name__ == "__main__":old_mailer = OldMailer()new_mailer = NewMailer()# 使用旧邮件发送器发送邮件old_mailer.send("example1@example.com", "这是一封测试邮件")# 使用适配器将新邮件发送器适配成旧邮件发送器的接口adapter = MailerAdapter(new_mailer)adapter.send("example2@example.com", "这是一封测试邮件")
  • 输出结果
发送邮件到:example1@example.com
邮件内容:这是一封测试邮件
发送邮件到多个地址:example2@example.com
邮件内容:这是一封测试邮件

外观模式

外观模式是一种结构型设计模式,它提供了一个统一的接口,用于访问子系统中的一组接口。外观模式的核心思想是将复杂的子系统隐藏在一个简单的接口后面,使得客户端可以更容易地使用这些功能而不必了解其内部的复杂实现细节。

  1. 外观(Facade):外观类是客户端访问子系统的入口,它提供了一个简单的接口,隐藏了子系统的复杂性,使得客户端可以更容易地使用子系统的功能。

  2. 子系统(Subsystem):子系统是一组相关联的类或模块,实现了系统的各种功能。外观类通过调用子系统中的方法来实现具体的功能。

对比门面模式

门面模式的目的是为了提供一个统一的接口,封装一个复杂子系统的接口,以简化客户端的操作。门面模式旨在隐藏系统的复杂性,提供一个简单的接口给客户端使用。

  • 门面模式的作用范围更广泛,可以封装一个整个子系统的接口,提供一个统一的接口给客户端使用。
  • 由于门面模式的作用范围更广泛,因此它的设计更加灵活,可以封装不同层次的复杂性,提供不同粒度的接口给客户端使用。

外观模式的目的也是为了提供一个统一的接口,但它更侧重于封装一组相关联的类或模块,而不仅仅是一个复杂的子系统。外观模式旨在简化客户端与一组相关对象的交互,提供一个统一的接口给客户端使用。

  • 外观模式的作用范围更局限,通常用于封装一组相关联的类或模块的接口,提供一个统一的接口给客户端使用。
  • 外观模式的作用范围更局限,因此它的设计相对更加固定,只能封装一组相关联的类或模块的接口。

场景

  1. 复杂系统的简化接口:当一个系统拥有多个复杂的子系统,并且客户端需要频繁地与这些子系统进行交互时,可以使用外观模式将这些子系统的功能封装在一个统一的接口后面,从而简化客户端的操作。

  2. 封装第三方库或服务:当你需要使用一个复杂的第三方库或服务时,可以使用外观模式将其功能封装在一个简单的接口后面,从而降低对第三方库或服务的依赖性,并提供一个更简单的接口给客户端使用。

  3. 提供简化的接口给客户端:当一个类或模块拥有复杂的接口时,可以使用外观模式提供一个简化的接口给客户端使用,从而降低客户端的学习成本,并提高代码的可读性和可维护性。

  4. 隐藏系统的实现细节:当一个系统拥有复杂的实现细节时,可以使用外观模式将这些实现细节隐藏起来,只暴露必要的接口给客户端使用,从而提高系统的安全性和稳定性。

  5. 简化系统集成:当一个系统需要与其他系统进行集成时,可能会涉及到多个接口的调用和数据交换。可以使用外观模式将这些复杂的集成过程封装在一个统一的接口后面,从而简化系统的集成过程。

示例

# -*- coding: utf-8 -*-# 投影仪类
class Projector:def on(self):print("投影仪已开启")def off(self):print("投影仪已关闭")# 音响类
class AudioSystem:def on(self):print("音响已开启")def off(self):print("音响已关闭")# 灯光类
class Light:def on(self):print("灯光已开启")def off(self):print("灯光已关闭")# 家庭影院外观类
class HomeTheaterFacade:def __init__(self, projector, audio_system, light):self.projector = projectorself.audio_system = audio_systemself.light = lightdef watch_movie(self):print("准备观影...")self.projector.on()self.audio_system.on()self.light.off()def end_movie(self):print("结束观影...")self.projector.off()self.audio_system.off()self.light.on()# 客户端代码
if __name__ == "__main__":projector = Projector()audio_system = AudioSystem()light = Light()# 创建家庭影院外观对象theater_facade = HomeTheaterFacade(projector, audio_system, light)# 观影theater_facade.watch_movie()print("\n--- 观影中 ---\n")# 结束观影theater_facade.end_movie()
  • 输出结果
准备观影...
投影仪已开启
音响已开启
灯光已关闭--- 观影中 ---结束观影...
投影仪已关闭
音响已关闭
灯光已开启

享元模式

享元模式(Flyweight Pattern)是一种结构型设计模式,它旨在通过共享对象来最大限度地减少内存使用和提高性能。享元模式通过共享大量细粒度对象的方式来优化系统,从而减少内存占用和提高运行效率。

享元模式的核心思想是将可共享的状态和不可共享的状态分离开来,将不可共享的状态作为对象的外部状态,而将可共享的状态作为对象的内部状态。通过共享内部状态,多个对象可以共享相同的状态,从而减少内存占用。

  1. 享元工厂(Flyweight Factory):享元工厂是用于创建和管理享元对象的工厂类。它维护一个享元池(Flyweight Pool),用于存储已创建的享元对象,并提供一个方法来获取享元对象。

  2. 享元(Flyweight):享元是一个接口或抽象类,用于定义享元对象的接口。享元对象通常包含内部状态和外部状态两部分,其中内部状态是可以共享的,而外部状态是不可共享的。

  3. 具体享元(Concrete Flyweight):具体享元是实现了享元接口的具体类,用于表示可共享的对象。具体享元对象通常包含内部状态的具体实现。

  4. 客户端(Client):客户端是使用享元模式的地方,它通过享元工厂来获取享元对象,并使用这些对象来完成具体的任务。

场景

  1. 文本编辑器中的字符对象:在文本编辑器中,可能需要创建大量相似的字符对象(例如字母、数字、符号等)。通过使用享元模式,可以共享相同字符的内部状态(例如字符的字体、颜色等),从而减少内存占用。

  2. 图形界面中的图元对象:在图形界面应用程序中,可能需要创建大量相似的图元对象(例如点、线、矩形、圆形等)。通过使用享元模式,可以共享相同图元的内部状态(例如图元的颜色、大小等),从而减少内存占用。

  3. 游戏开发中的粒子对象:在游戏开发中,可能需要创建大量相似的粒子对象(例如火花、爆炸、烟雾等)。通过使用享元模式,可以共享相同粒子的内部状态(例如粒子的贴图、大小、速度等),从而减少内存占用。

  4. Web应用程序中的缓存对象:在Web应用程序中,可能需要创建大量相似的缓存对象(例如页面缓存、数据库查询结果缓存等)。通过使用享元模式,可以共享相同缓存的内部状态(例如缓存的内容、过期时间等),从而减少内存占用。

  5. 连接池:在数据库连接池、线程池等场景中,可能需要创建大量相似的资源对象。通过使用享元模式,可以共享相同资源的内部状态(例如连接的数据库地址、用户名等),从而减少资源占用。

示例

# -*- coding: utf-8 -*-# 享元工厂类
class RectangleFactory:_rectangles = {}@staticmethoddef get_rectangle(color):if color not in RectangleFactory._rectangles:RectangleFactory._rectangles[color] = Rectangle(color)return RectangleFactory._rectangles[color]# 享元类
class Rectangle:def __init__(self, color):self.color = colordef draw(self, x, y):print("绘制颜色为{}的矩形,坐标为({}, {})".format(self.color, x, y))# 客户端代码
if __name__ == "__main__":colors = ["红色", "蓝色", "绿色"]for i in range(5):color = colors[i % len(colors)]rectangle = RectangleFactory.get_rectangle(color)rectangle.draw(i * 10, i * 10)
  • 输出结果
绘制颜色为红色的矩形,坐标为(0, 0)
绘制颜色为蓝色的矩形,坐标为(10, 10)
绘制颜色为绿色的矩形,坐标为(20, 20)
绘制颜色为红色的矩形,坐标为(30, 30)
绘制颜色为蓝色的矩形,坐标为(40, 40)

装饰器模式

装饰器模式是一种结构型设计模式,它允许在不修改已有代码的情况下,动态地向对象添加新的功能。装饰器模式通过将对象放入包装器中,从而给对象增加新的行为。

在Python中,装饰器通常是一个函数,它接受一个函数作为参数,并返回一个新的函数。装饰器函数可以在不修改原函数代码的情况下,动态地为函数添加额外的功能。

装饰器模式的核心思想是将对象的责任分离开来,每个装饰器对象只负责特定的功能。多个装饰器可以嵌套使用,从而实现多层装饰效果。

  1. 组件(Component):组件是一个接口或抽象类,定义了需要被装饰的对象的接口。

  2. 具体组件(Concrete Component):具体组件是实现了组件接口的具体类,是需要被装饰的对象。

  3. 装饰器(Decorator):装饰器是一个接口或抽象类,定义了装饰器对象的接口。

  4. 具体装饰器(Concrete Decorator):具体装饰器是实现了装饰器接口的具体类,用于给组件对象添加新的功能。

场景

  1. 日志记录:通过装饰器模式可以方便地为函数添加日志记录功能,记录函数的调用信息、参数、返回值等,从而方便调试和错误排查。

  2. 性能监控:通过装饰器模式可以方便地为函数添加性能监控功能,统计函数的执行时间、调用次数等信息,从而进行性能优化。

  3. 权限验证:通过装饰器模式可以方便地为函数添加权限验证功能,验证用户是否具有执行函数的权限,从而实现访问控制。

  4. 缓存:通过装饰器模式可以方便地为函数添加缓存功能,缓存函数的计算结果,提高函数的执行效率。

  5. 路由映射:在Web框架中,可以使用装饰器模式实现路由映射功能,将URL请求映射到对应的处理函数,从而实现请求分发。

  6. 事务管理:通过装饰器模式可以方便地为函数添加事务管理功能,管理函数的事务提交、回滚等操作,保证数据的一致性。

  7. 重试机制:通过装饰器模式可以方便地为函数添加重试机制功能,自动重试函数的执行,处理网络异常、数据库连接超时等情况。

  8. 参数校验:通过装饰器模式可以方便地为函数添加参数校验功能,检查函数的输入参数是否合法,防止参数错误导致的异常。

示例

# -*- coding: utf-8 -*-import functools# 模拟数据库连接对象
class Connection:def __init__(self):self.is_transaction_active = Falsedef begin_transaction(self):print("开始事务")self.is_transaction_active = Truedef commit_transaction(self):print("提交事务")self.is_transaction_active = Falsedef rollback_transaction(self):print("回滚事务")self.is_transaction_active = False# 装饰器:事务管理
def transaction(func):@functools.wraps(func)def wrapper(*args, **kwargs):# 获取数据库连接对象connection = args[0]  # 假设第一个参数是数据库连接对象# 开始事务connection.begin_transaction()try:# 执行函数result = func(*args, **kwargs)# 提交事务connection.commit_transaction()return resultexcept Exception as e:# 回滚事务connection.rollback_transaction()raise ereturn wrapper# 模拟数据库操作函数
@transaction
def update_data(connection):# 模拟数据库更新操作print("执行数据库更新操作")# 模拟抛出异常raise Exception("更新数据失败")# 客户端代码
if __name__ == "__main__":# 创建数据库连接对象connection = Connection()# 调用数据库操作函数try:update_data(connection)except Exception as e:print(f"数据库操作失败:{e}")
  • 输出结果
开始事务
执行数据库更新操作
回滚事务
数据库操作失败:更新数据失败

组合模式

组合模式是一种结构型设计模式,它允许将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式让客户端可以统一地处理单个对象和组合对象,从而简化了客户端代码。

  1. 组件(Component):组件是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。对于组合中的所有对象,它声明了一个接口,客户端可以通过这个接口访问和管理对象。

  2. 叶子(Leaf):叶子是组合中的叶子对象,它实现了组件接口,并且在组合中没有子节点。叶子对象是组合中的基本元素,它不包含其他对象。

  3. 容器(Composite):容器是组合中的容器对象,它实现了组件接口,并且可以包含其他组件对象作为子节点。容器对象通常有添加子节点、删除子节点、获取子节点等方法,用于管理子节点。

组合模式的主要思想是将单个对象和组合对象统一对待,客户端无需关心对象是单个对象还是组合对象,可以通过统一的接口来访问和管理对象。这样可以简化客户端代码,并且使得系统更加灵活和可扩展。

场景

  1. 树形结构的数据表示:组合模式适用于表示具有层次结构的数据,例如文件系统、组织架构、菜单系统等。通过使用组合模式,可以将单个对象和组合对象统一对待,从而简化了对树形结构数据的操作和管理。

  2. GUI界面中的控件组合:在GUI界面开发中,通常会有各种不同类型的控件,例如按钮、文本框、标签等。这些控件可以组合成更复杂的界面,例如面板、对话框、窗口等。通过使用组合模式,可以将单个控件和组合控件统一对待,从而简化了界面的构建和管理。

  3. 图形渲染引擎中的图元组合:在图形渲染引擎中,通常会有各种不同类型的图元,例如线段、矩形、圆形等。这些图元可以组合成更复杂的图形,例如多边形、图像等。通过使用组合模式,可以将单个图元和组合图元统一对待,从而简化了图形的构建和渲染。

  4. 组织结构中的部门和员工关系:在组织结构中,部门和员工之间存在层次关系,一个部门可以包含多个员工,而一个员工也可以是一个部门的领导。通过使用组合模式,可以将部门和员工统一对待,从而简化了组织结构的表示和管理。

示例

# -*- coding: utf-8 -*-# 组件接口
class Component:def __init__(self, name):self.name = namedef display(self, depth):pass# 叶子节点:按钮
class Button(Component):def display(self, depth):print(" " * depth + "按钮:" + self.name)# 叶子节点:文本框
class TextBox(Component):def display(self, depth):print(" " * depth + "文本框:" + self.name)# 叶子节点:标签
class Label(Component):def display(self, depth):print(" " * depth + "标签:" + self.name)# 容器节点:面板
class Panel(Component):def __init__(self, name):super().__init__(name)self.children = []def add(self, component):self.children.append(component)def remove(self, component):self.children.remove(component)def display(self, depth):print(" " * depth + "面板:" + self.name)for child in self.children:child.display(depth + 1)# 客户端代码
if __name__ == "__main__":# 创建面板对象main_panel = Panel("主面板")# 添加按钮、文本框和标签main_panel.add(Button("登录"))main_panel.add(TextBox("用户名"))main_panel.add(TextBox("密码"))main_panel.add(Label("提示信息"))# 创建嵌套面板对象sub_panel = Panel("子面板")sub_panel.add(Button("注册"))sub_panel.add(TextBox("新用户名"))sub_panel.add(TextBox("新密码"))# 将嵌套面板添加到主面板中main_panel.add(sub_panel)# 显示界面布局main_panel.display(0)
  • 输出结果
面板:主面板按钮:登录文本框:用户名文本框:密码标签:提示信息面板:子面板按钮:注册文本框:新用户名文本框:新密码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/25702.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

grok debugger 正则解析 网络安全设备日志

1、网络设备、安全设备不同品牌、不同型号的设备,日志格式都不一样,那针对这种情况,我们可以使用工具grok debugger进行日志格式解析,具体的网址为: 地址:https://grokdebug.herokuapp.com/ 也可以采用私有化部署&am…

使用Python去除PNG图片背景

要使用Python自动去除PNG图片的背景,你可以使用remove.bg的API,或者使用一些图像处理库如OpenCV和Pillow结合Mask R-CNN等深度学习模型。以下是一个使用Pillow库的简单示例: 安装所需库: pip install pillow numpy使用以下代码去…

归并排序的递归与非递归实现

递归实现 归并排序有点类似于二叉树的后序遍历,是一种基于分治思想的排序算法。具体过程如下: 但要注意,在归并时要额外开辟一个与原数组同等大小的空间用来存储每次归并排序后的值,然后再拷贝到原数组中。 代码实现&#xff1a…

【十大排序算法】归并排序

归并排序,如同秋日落叶,分散而细碎, 然而风吹叶动,自然而有序, 彼此相遇,轻轻合拢, 最终成就,秩序之谧。 文章目录 一、归并排序二、发展历史三、处理流程四、算法实现五、算法特性…

树莓派4B_OpenCv学习笔记5:读取窗口鼠标状态坐标_TrackBar滑动条控件的使用

今日继续学习树莓派4B 4G:(Raspberry Pi,简称RPi或RasPi) 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Opencv 版本是4.5.1: 今日学习:读取窗口鼠标状态坐标_TrackBar滑动条控件的使…

自然资源-《乡村振兴用地政策指南(2023年)》解读

自然资源-《乡村振兴用地政策指南(2023年)》解读 近期,自然资源部办公厅印发《乡村振兴用地政策指南(2023年)》(以下简称《指南》)。作为第一部针对乡村振兴用地政策的“工具包”,《…

Vue.js基础入门

Vue.js的基本概念和框架结构 Vue.js的基本概念 Vue实例 Vue实例是通过new Vue()创建的,它是Vue应用的核心。每个Vue应用都是由一个Vue实例开始的。示例代码: var app new Vue({el: #app,data: {message: Hello Vue!} });数据绑定 Vue.js提供了双向数据…

redis 05 复制 ,哨兵

01.redis的复制功能,使用命令slaveof 2. 2.1 2.2 3. 3.1 3.1.1 3.1.2 3.1.3 4 4.1 4.2 例子 5.1 这里是从客户端发出的指令 5.2 套接字就是socket 这里是和redis事件相关的知识 5.3 ping一下

idea编码问题:需要 <标识符> 非法的类型 、需要为 class、interface 或 enum 问题解决

目录 问题现象 问题解决 问题现象 今天在idea 使用中遇到的一个编码的问题就是&#xff0c;出现了这个&#xff1a; Error:(357, 28) java: /home/luya...........anageService.java:357: 需要 <标识符> Error:(357, 41) java: /home/luya............anageService.ja…

Cinema 4D 2024 软件安装教程、附安装包下载

Cinema 4D 2024 Cinema 4D&#xff08;C4D&#xff09;是一款由Maxon开发的三维建模、动画和渲染软件&#xff0c;广泛用于电影制作、广告、游戏开发、视觉效果等领域。Cinema 4D允许用户创建复杂的三维模型&#xff0c;包括角色、场景、物体等。它提供了多种建模工具&#x…

Channels无法使用ASGI问题

Django Channels是一个基于Django的扩展, 用于处理WebSockets, 长轮询和触发器事件等实时应用程序. 它允许Django处理异步请求, 并提供了与其他WebSockets库集成的功能.当我们在Django Channels中使用ASGI_APPLICATION设置时, 我们可以指定一个新的ASGI应用程序来处理ASGI请求.…

Day01 - Day05

Day01 - Day05 Day01&#xff08;1997年Text1&#xff09; After six months of arguing and final 16 hours of hot parliamentary debates, Australia’s Northern Territory became the first legal authority in the world to allow doctors to take the lives of incurab…

java非框架代码实现缓存并实现自动过期

要实现一个简单的Java缓存&#xff0c;可以使用ConcurrentHashMap和ScheduledExecutorService。以下是一个简单的示例&#xff1a; java import java.util.concurrent.*; public class SimpleCache<K, V> { private final ConcurrentHashMap<K, CacheItem<V>&g…

springboot中基于RestTemplate 类 实现调用第三方API接口,获取响应体内容不需要转换数据类型【丰富版】

RestTemplate 用法 和 http工具类 这篇就不说了 可以去看下面的博客 本篇文章是 针对 下面的博客 进行的扩展 https://blog.csdn.net/Drug_/article/details/137166797 我们在调用第三方 api接口 时候 在获取相应体的时候 不知道用什么数据类型 去接 响应体里的数据 用 字符串…

数据库期末设计——图书管理系统

目录 1.前置软件以及开发环境&#xff1a; 2.开发过程讲解 代码环节&#xff1a; 数据库代码 1.BookDao.java 2.BookTypeDao.java 3.UserDao.java 4.Book.java 5.BookType.java 6.User.java 7.DbUtil.java 8.Stringutil.java 9.BookAddInterFrm.java 10.BookMan…

前端学习----css基础语法

CSS概述 CAscading Style Sheets(级联样式表) CSS是一种样式语言,用于对HTML文档控制外观,自定义布局等,例如字体,颜色,边距等 可将页面的内容与表现形式分离,页面内容存放在HTML文档中,而用于定义表现形式的CSS在一个.css文件中或HTML文档的某一部分 HTML与CSS的关系 HTM…

freertos中的链表1 - 链表的数据结构

1.概述 freertos中链表的实现在 list.c 和 list.h。旨在通过学习freertos中的链表的数据结构&#xff0c;对freertos中的链表实现有一个整体的认识。freertos使用了三个数据结构来描述链表&#xff0c;分别是&#xff1a;List_t&#xff0c; MiniListItem_t&#xff0c;ListIt…

智能合约中时间依赖漏洞

时间依赖漏洞 时间依赖漏洞是智能合约中一个常见的安全问题&#xff0c;特别是在以太坊等区块链环境中。这是因为区块链的区块时间戳可以被矿工在一定程度上操纵&#xff0c;这使得依赖于时间戳的智能合约容易受到攻击。攻击者可以通过控制区块时间戳来触发合约中的某些条件&a…

B3637 最长上升子序列

最长上升子序列 题目描述 这是一个简单的动规板子题。 给出一个由 n ( n ≤ 5000 ) n(n\le 5000) n(n≤5000) 个不超过 1 0 6 10^6 106 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。 最长上升子序列是指&#xff0c;从原序列中按顺序取出一些数字排在一起…

产品创新:驱动企业增长的核心动力

在当今快速变化的市场环境中&#xff0c;产品创新已成为企业生存和发展的关键。产品创新不仅涉及全新产品或服务的开发&#xff0c;也包括对现有产品或服务的持续改进和优化。本文将深入探讨产品创新的定义、重要性以及如何通过创新驱动企业增长&#xff0c;并结合实际案例进行…