设计模式是软件工程中的一种重要概念,它描述了解决特定问题的固定方法。设计模式可以分为三种主要类型:创建型、结构型和行为型。以下是部分设计模式的介绍:
-
创建型模式:关注对象的实例化过程,包括如何实例化对象、隐藏对象的创建细节等。例如,简单工厂模式、工厂方法模式和抽象工厂模式。这些模式帮助我们控制对象的创建方式,提供灵活性和可扩展性。
-
结构型模式:关注对象之间的组合方式,以达到构建更大结构的目标。例如,合成模式和装饰器模式。这些模式帮助定义对象之间的关系,从而实现更大的结构。
-
行为型模式:关注对象之间的通信方式,以及如何合作共同完成任务。例如,观察者模式、策略模式和命令模式。这些模式涉及到对象之间的交互、责任分配等。
设计模式是什么?
设计模式是经过总结、优化的,对我们经常会碰到的一些编程问题的可重用解决方案。一个设计模式并不像一个类或一个库那样能够直接作用于我们的代码。反之,设计模式更为高级,它是一种必须在特定情形下实现的一种方法模板。设计模式不会绑定具体的编程语言。一个好的设计模式应该能够用大部分编程语言实现(如果做不到全部的话,具体取决于语言特性)。最为重要的是,设计模式也是一把双刃剑,如果设计模式被用在不恰当的情形下将会造成灾难,进而带来无穷的麻烦。然而如果设计模式在正确的时间被用在正确地地方,它将是你的救星。
起初,你会认为“模式”就是为了解决一类特定问题而特别想出来的明智之举。说的没错,看起来的确是通过很多人一起工作,从不同的角度看待问题进而形成的一个最通用、最灵活的解决方案。也许这些问题你曾经见过或是曾经解决过,但是你的解决方案很可能没有模式这么完备。
虽然被称为“设计模式”,但是它们同“设计“领域并非紧密联系。设计模式同传统意义上的分析、设计与实现不同,事实上设计模式将一个完整的理念根植于程序中,所以它可能出现在分析阶段或是更高层的设计阶段。很有趣的是因为设计模式的具体体现是程序代码,因此可能会让你认为它不会在具体实现阶段之前出现(事实上在进入具体实现阶段之前你都没有意识到正在使用具体的设计模式)。
可以通过程序设计的基本概念来理解模式:增加一个抽象层。抽象一个事物就是隔离任何具体细节,这么做的目的是为了将那些不变的核心部分从其他细节中分离出来。当你发现你程序中的某些部分经常因为某些原因改动,而你不想让这些改动的部分引发其他部分的改动,这时候你就需要思考那些不会变动的设计方法了。这么做不仅会使代码可维护性更高,而且会让代码更易于理解,从而降低开发成本。
设计模式的核心思想是利用经过时间检验的解决方案来解决在软件开发过程中遇到的常见问题。设计模式不是代码,而是解决特定问题的策略和方法。它们通常基于以下几个方面:
-
可重用性:设计模式提供了一种标准化的方法来解决常见的设计问题,使得解决方案可以在不同的项目中重复使用。
-
可维护性:通过将设计模式应用于代码,可以提高代码的清晰度和组织性,使得未来的维护和扩展更加容易。
-
灵活性:设计模式通常允许系统更容易地适应变化,因为它们提供了一种灵活的方式来组织代码,以便于修改和扩展。
-
通信:设计模式为开发者提供了一种共同的语言,使得他们能够更有效地沟通设计决策和架构意图。
设计模式主要为了解决以下类型的问题:
- 复杂性管理:随着软件系统规模的增长,管理其复杂性变得越来越困难。设计模式提供了一种结构化的方法来组织和简化复杂性。
- 代码复用:设计模式鼓励开发者通过组合和继承来复用代码,而不是复制和粘贴。
- 可扩展性:设计模式通常设计为易于扩展,以适应未来的需求变化。
- 解耦:设计模式帮助减少组件之间的依赖,使得它们可以独立变化和升级。
设计模式提高了以下方面的效率:
- 开发效率:开发者可以利用已有的设计模式快速解决常见问题,而不需要从头开始设计解决方案。
- 团队协作:设计模式提供了一种标准化的沟通方式,有助于团队成员之间的协作和理解。
- 代码质量:遵循设计模式可以提高代码的一致性和质量,减少错误和缺陷。
- 适应性:设计模式使得系统更加灵活,能够更容易地适应需求的变化。
设计模式的7大原则:
-
单一职责原则(SRP):
- 假设有一个
Printer
类,它负责打印文档,并且能够发送打印任务到打印机。这个类有两个职责:打印文档和发送任务。根据SRP,我们应该将这两个职责分离到两个不同的类中,例如DocumentPrinter
和PrinterTaskSender
。
- 假设有一个
-
开闭原则(OCP):
- 假设有一个系统,它可以根据用户输入的不同类型的数据(如整数、浮点数、字符串)来执行不同的操作。如果使用OCP,我们可以定义一个接口
DataProcessor
,然后为每种数据类型实现一个具体的类。这样,当需要添加新的数据类型处理时,我们只需要添加一个新的类而不需要修改现有的代码。
- 假设有一个系统,它可以根据用户输入的不同类型的数据(如整数、浮点数、字符串)来执行不同的操作。如果使用OCP,我们可以定义一个接口
-
里氏替换原则(LSP):
- 如果有一个
Shape
类,它有一个方法calculateArea()
来计算面积。我们有一个Circle
类继承自Shape
。根据LSP,Circle
类的calculateArea()
方法必须能够替换掉Shape
类的同名方法,而不影响使用Shape
类的对象的客户端代码。
- 如果有一个
-
接口隔离原则(ISP):
- 假设有一个
Vehicle
接口,它包含多种车辆共有的方法,如start()
、stop()
和refuel()
。根据ISP,如果某些车辆不需要refuel()
方法,我们应该将接口拆分为更小的接口,如Operable
和Refuelable
,这样不同的车辆可以实现它们需要的接口。
- 假设有一个
-
依赖倒置原则(DIP):
- 假设有一个
PaymentProcessor
类,它依赖于具体的CreditCard
类来处理信用卡支付。根据DIP,我们应该让PaymentProcessor
依赖于一个抽象的PaymentMethod
接口,而不是具体的CreditCard
类。这样,如果需要添加新的支付方式,我们只需要实现PaymentMethod
接口而不需要修改PaymentProcessor
。
- 假设有一个
-
迪米特法则(LoD):
- 假设有一个
Department
类,它包含员工列表。根据LoD,Department
类不应该直接访问员工的个人详细信息,而应该通过员工类提供的方法来获取这些信息。这样可以减少类之间的耦合。
- 假设有一个
-
合成/聚合复用原则(CRP):
- 假设有一个
Document
类,它需要包含多个Paragraph
对象。根据CRP,Document
类应该使用Paragraph
对象的集合,而不是继承Paragraph
类。这样,Document
类可以复用Paragraph
类的功能,同时保持了类的独立性和可扩展性。
- 假设有一个
面向对象编程:
(Object-Oriented Programming,简称OOP)是一种编程范式,它使用“对象”来设计软件。对象可以包含数据(通常称为属性或字段)和代码(通常称为方法或函数)。
OOP的核心概念包括:
-
类(Class):
- 类是创建对象的蓝图或模板。它定义了对象的属性和行为。
-
对象(Object):
- 对象是类的实例。每个对象都有其自己的状态和行为。
-
封装(Encapsulation):
- 封装是将数据(属性)和行为(方法)组合在一起的过程,并隐藏内部细节,只提供必要的接口。
-
继承(Inheritance):
- 继承是一种机制,允许一个类(子类)继承另一个类(父类或超类)的属性和方法。
-
多态(Polymorphism):
- 多态允许同一个接口接受不同的数据类型。在OOP中,多态可以通过方法重载(编译时多态)和方法重写(运行时多态)实现。
-
抽象(Abstraction):
- 抽象是简化复杂的现实世界问题的过程,只关注对于当前目标相关的方面。
-
接口(Interface):
- 接口是一组方法规范,可以被类实现(implement)。它定义了类必须遵守的契约。
-
抽象类(Abstract Class):
- 抽象类是不能被实例化的类,通常包含抽象方法,这些方法由子类实现。
-
组合(Composition):
- 组合是一种设计原则,其中一个类包含另一个类的实例作为其属性,从而实现代码复用。
-
聚合(Aggregation):
- 聚合是一种特殊的组合关系,其中一个类(聚合类)包含另一个类(被聚合类)的引用,但被聚合类可以独立于聚合类存在。
-
设计模式(Design Patterns):
- 设计模式是解决特定问题的通用模板。它们遵循OOP原则,如SOLID原则。
-
异常处理(Exception Handling):
- 异常处理是管理程序运行时错误的一种机制,允许程序从错误中恢复而不是崩溃。
-
访问修饰符(Access Modifiers):
- 访问修饰符如public, private, protected等,控制类成员的可见性。
-
构造函数(Constructor):
- 构造函数是一种特殊的方法,用于初始化新创建的对象。
-
析构函数(Destructor):
- 析构函数是一种特殊的方法,用于在对象生命周期结束时执行清理工作。
-
静态成员(Static Members):
- 静态成员属于类本身,而不是类的任何特定实例。
-
友元(Friends):
- 友元是C++特有的概念,允许一个类或函数访问另一个类的私有或受保护的成员。
-
重载(Overloading):
- 重载允许类有多个同名的方法,只要它们的参数列表不同。
-
重写(Overriding):
- 重写是子类提供其自己的实现来覆盖父类的方法。
-
封装性(Encapsulation):
- 封装性是将对象的实现细节隐藏起来,只暴露一个可以被外界访问和使用的接口。
-
数据抽象(Data Abstraction):
- 数据抽象是创建模型来表示现实世界实体的过程,通常通过类和对象来实现。
-
消息传递(Message Passing):
- 对象间通过发送消息来交互,这些消息对应于对象的方法调用。
OOP是一种强大的编程范式,它通过模拟现实世界中的对象和它们之间的交互来构建软件系统。通过使用OOP的原则和模式,开发者可以创建出更加模块化、易于理解和维护的代码。
以下详细说一下创建型、结构型和行为型的细分详解:
创建型
1. Factory Method(工厂方法)
2. Abstract Factory(抽象工厂)
3. Builder(建造者)
4. Prototype(原型)
5. Singleton(单例)
结构型
6. Adapter Class/Object(适配器)
7. Bridge(桥接)
8. Composite(组合)
9. Decorator(装饰)
10. Facade(外观)
11. Flyweight(享元)
12. Proxy(代理)
行为型
13. Interpreter(解释器)
14. Template Method(模板方法)
15. Chain of Responsibility(责任链)
16. Command(命令)
17. Iterator(迭代器)
18. Mediator(中介者)
19. Memento(备忘录)
20. Observer(观察者)
21. State(状态)
22. Strategy(策略)
23. Visitor(访问者)
常用有:
- 单例模式:确保一个类只有一个实例,并提供一个全局访问点。单例模式可以分为预加载和懒加载两种类型。
- 合成模式:将对象组织到树结构中,用来描述整体与部分的关系。合成模式使得客户端把一个个单独的成分对象和由它们复合而成的合成对象同等看待。
- 装饰模式:以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态地撤消。
- 门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。
创建型
1.Factory Method(工厂方法)
意图:
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
适用性:
当一个类不知道它所必须创建的对象的类的时候。
当一个类希望由它的子类来指定它所创建的对象的时候。
当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
实现:
class Product:
def __init__(self, name):
self.name = name
def describe(self):
return f"这是 {self.name} 商品"
class ElectronicProduct(Product):
def __init__(self, name):
super().__init__(name)
class ClothingProduct(Product):
def __init__(self, name):
super().__init__(name)
class ProductFactory:
def create_product(self, product_type, name):
if product_type == "electronic":
return ElectronicProduct(name)
elif product_type == "clothing":
return ClothingProduct(name)
else:
raise ValueError("无效的产品类型")
# 在电商平台中使用
factory = ProductFactory()
electronic_product = factory.create_product("electronic", "手机")
print(electronic_product.describe())
clothing_product = factory.create_product("clothing", "衬衫")
print(clothing_product.describe())
在这个示例中,Product
是基类,ElectronicProduct
和 ClothingProduct
是具体的产品类。ProductFactory
负责根据不同的类型创建相应的产品对象。在电商平台中,可以根据不同的需求调用工厂方法来获取不同类型的商品对象,从而实现灵活的产品创建机制。这样可以将产品的创建与具体的业务逻辑解耦,使得代码更易于维护和扩展。
2. Abstract Factory(抽象工厂)
意图:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
适用性:
一个系统要独立于它的产品的创建、组合和表示时。
一个系统要由多个产品系列中的一个来配置时。
当你要强调一系列相关的产品对象的设计以便进行联合使用时。
当你提供一个产品类库,而只想显示它们的接口而不是实现时。
from abc import ABC, abstractmethod
# 产品接口
class ProductA(ABC):
@abstractmethod
def describe_a(self):
pass
class ProductB(ABC):
@abstractmethod
def describe_b(self):
pass
# 具体产品 A1
class ProductA1(ProductA):
def describe_a(self):
return "这是具体产品 A1"
# 具体产品 A2
class ProductA2(ProductA):
def describe_a(self):
return "这是具体产品 A2"
# 具体产品 B1
class ProductB1(ProductB):
def describe_b(self):
return "这是具体产品 B1"
# 具体产品 B2
class ProductB2(ProductB):
def describe_b(self):
return "这是具体产品 B2"
# 抽象工厂
class AbstractFactory(ABC):
@abstractmethod
def create_product_a(self):
pass
@abstractmethod
def create_product_b(self):
pass
# 具体工厂 1
class Factory1(AbstractFactory):
def create_product_a(self):
return ProductA1()
def create_product_b(self):
return ProductB1()
# 具体工厂 2
class Factory2(AbstractFactory):
def create_product_a(self):
return ProductA2()
def create_product_b(self):
return ProductB2()
# 在电商平台中使用
factory1 = Factory1()
product_a1 = factory1.create_product_a()
product_b1 = factory1.create_product_b()
print(product_a1.describe_a())
print(product_b1.describe_b())
factory2 = Factory2()
product_a2 = factory2.create_product_a()
product_b2 = factory2.create_product_b()
print(product_a2.describe_a())
print(product_b2.describe_b())
在这个示例中:
- 定义了两个抽象产品类
ProductA
和ProductB
,以及它们各自的具体产品类。 - 抽象工厂类
AbstractFactory
规定了创建两种产品的抽象方法。 - 然后有具体的工厂类
Factory1
和Factory2
分别实现了创建不同组合产品的逻辑。
在电商平台中,可以根据不同的场景或需求选择使用不同的工厂来获取配套的产品集合。这样使得系统在扩展产品系列或切换生产策略时更加灵活和方便,同时保持了产品创建逻辑的相对独立性和可维护性。
3. Builder(建造者)
意图:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用性:
当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
当构造过程必须允许被构造的对象有不同的表示时。
class OrderDetails:
def __init__(self):
self.products = []
self.customer_name = ""
self.shipping_address = ""
def add_product(self, product):
self.products.append(product)
def set_customer_name(self, name):
self.customer_name = name
def set_shipping_address(self, address):
self.shipping_address = address
def __str__(self):
return f"订单包含产品: {self.products}, 客户姓名: {self.customer_name}, 收货地址: {self.shipping_address}"
class OrderDetailsBuilder:
def __init__(self):
self.order_details = OrderDetails()
def add_product(self, product):
self.order_details.add_product(product)
def set_customer_name(self, name):
self.order_details.set_customer_name(name)
def set_shipping_address(self, address):
self.order_details.set_shipping_address(address)
def build(self):
return self.order_details
# 在电商平台中使用
builder = OrderDetailsBuilder()
builder.add_product("手机")
builder.add_product("耳机")
builder.set_customer_name("张三")
builder.set_shipping_address("某市某区某街道")
order_details = builder.build()
print(order_details)
在这个示例中:
OrderDetails
类表示订单详情,包含了产品列表、客户姓名和收货地址等信息。OrderDetailsBuilder
类负责逐步构建订单详情的各个部分。- 在电商平台中,可以通过建造者按照具体需求逐步添加和设置订单的不同属性,最后构建出完整的订单详情对象。这种模式使得订单构建过程更加灵活和清晰,不同的构建步骤可以独立变化和扩展,方便了复杂对象的构建和管理。
4. Prototype(原型)
意图:
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
适用性:
当要实例化的类是在运行时刻指定时,例如,通过动态装载;或者为了避免创建一个与产品类层次平行的工厂类层次时;或者当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
import copy
class ProductTemplate:
def __init__(self, name, description):
self.name = name
self.description = description
def clone(self):
return copy.deepcopy(self)
# 创建一个初始的商品模板
original_template = ProductTemplate("初始商品", "这是初始描述")
# 通过原型复制创建新的模板
new_template1 = original_template.clone()
new_template1.name = "新商品 1"
new_template2 = original_template.clone()
new_template2.name = "新商品 2"
print(original_template)
print(new_template1)
print(new_template2)
在电商平台中,可能会有一些通用的商品模板。
使用原型模式可以基于现有的模板快速创建新的类似对象,然后进行一些个性化的修改。
这样可以避免重复创建相似对象时的大量重复代码和数据初始化工作。通过克隆原型对象,可以高效地生成具有相似结构但又有差异的新对象,满足电商平台中快速创建和定制商品信息的需求。
比如在创建大量相似商品时,可以先创建一个基础模板,然后通过原型复制并修改关键属性来快速生成不同的商品实例,提高开发效率和代码的可维护性。
5. Singleton(单例)
意图:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用性:
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
Singleton(单例)模式有很多应用场景。
比如,电商平台的全局计数器,用于统计订单数量或访问量等关键数据,只需要一个这样的计数器实例即可。通过单例模式确保整个平台只有一个计数器对象在工作,保证数据的准确性和一致性。
再比如,平台的消息中心模块,负责接收和分发各种系统消息。将其设计为单例可以保证所有消息的处理都集中在这一个实例中进行,避免出现消息处理混乱的情况。
还有,电商平台的配置管理类也可以是单例的。这个单例对象负责加载和管理整个平台的各项配置参数,如支付配置、物流配置等。无论在平台的哪个部分需要获取配置信息,都通过这个唯一的配置管理单例对象来获取,确保了配置的统一性和实时性。
实现:
```python
class EcommerceConfig:
_instance = None
def __init__(self):
# 加载各种配置
self.payment_gateways = ["支付宝", "微信支付"]
self.logistics_partners = ["顺丰", "圆通"]
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = cls()
return cls._instance
# 在电商平台中使用
config = EcommerceConfig.get_instance()
print(config.payment_gateways)
print(config.logistics_partners)
```
结构型
6. Adapter Class/Object(适配器)
意图:
将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适用性:
你想使用一个已经存在的类,而它的接口不符合你的需求。
你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
(仅适用于对象Adapter )你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
class LegacyProductDetails:
""" 旧的产品详细信息类 """
def get_product_name(self):
return "Old Product Name"
def get_product_price(self):
return 100
def get_product_description(self):
return "Old description"
class NewProductAPI:
""" 新的产品接口 """
def get_product_info(self):
""" 获取产品综合信息 """
pass
class ProductAdapter(NewProductAPI):
""" 产品适配器 """
def __init__(self, legacy_product):
self.legacy_product = legacy_product
def get_product_info(self):
""" 将旧的产品信息适配到新接口 """
name = self.legacy_product.get_product_name()
price = self.legacy_product.get_product_price()
description = self.legacy_product.get_product_description()
return {
"name": name,
"price": price,
"description": description
}
# 创建旧的产品详细信息对象
legacy_product = LegacyProductDetails()
# 创建适配器
adapter = ProductAdapter(legacy_product)
# 使用适配器通过新接口获取产品信息
product_info = adapter.get_product_info()
print(product_info)
- 目标接口(Target):在这里就是
NewProductAPI
,它定义了新系统或模块期望的产品接口规范。 - 被适配者(Adaptee):即
LegacyProductDetails
,它是具有不同接口的旧的产品数据类。 - 适配器(Adapter):是
ProductAdapter
,它继承自目标接口(实现了新接口要求的方法),同时在内部持有被适配者的实例。通过在适配器的方法实现中,将被适配者的方法调用结果进行转换和整合,使其符合目标接口的要求。 - 适配器模式的核心在于通过创建一个中间层(适配器),将不兼容的接口转换为目标接口,使得不同来源的产品数据能够以统一的方式被新的系统或模块使用。这样可以在不修改旧代码的基础上,实现系统的扩展和整合。同时,适配器模式保持了系统的灵活性和可维护性,当旧的产品数据接口发生变化时,只需要修改适配器的代码即可,而不会影响到使用目标接口的其他部分。
7. Bridge(桥接)
意图:
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
适用性:
你不希望在抽象和它的实现部分之间有一个固定的绑定关系。例如这种情况可能是因为,在程序运行时刻实现部分应可以被选择或者切换。
类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这时Bridge 模式使你可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充。
对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。
(C++)你想对客户完全隐藏抽象的实现部分。在C++中,类的表示在类接口中是可见的。
有许多类要生成。这样一种类层次结构说明你必须将一个对象分解成两个部分。Rumbaugh 称这种类层次结构为“嵌套的普化”(nested generalizations )。
你想在多个对象间共享实现(可能使用引用计数),但同时要求客户并不知道这一点。一个简单的例子便是Coplien 的String 类[ Cop92 ],在这个类中多个对象可以共享同一个字符串表示(StringRep)。
#!/usr/bin/python
#coding:utf8
'''
Bridge
'''
# ConcreteImplementor 1/2
class DrawingAPI1(object):
def draw_circle(self, x, y, radius):
print('API1.circle at {}:{} radius {}'.format(x, y, radius))
# ConcreteImplementor 2/2
class DrawingAPI2(object):
def draw_circle(self, x, y, radius):
print('API2.circle at {}:{} radius {}'.format(x, y, radius))
# Refined Abstraction
class CircleShape(object):
def __init__(self, x, y, radius, drawing_api):
self._x = x
self._y = y
self._radius = radius
self._drawing_api = drawing_api
# low-level i.e. Implementation specific
def draw(self):
self._drawing_api.draw_circle(self._x, self._y, self._radius)
# high-level i.e. Abstraction specific
def scale(self, pct):
self._radius *= pct
def main():
shapes = (
CircleShape(1, 2, 3, DrawingAPI1()),
CircleShape(5, 7, 11, DrawingAPI2())
)
for shape in shapes:
shape.scale(2.5)
shape.draw()
if __name__ == '__main__':
main()
8. Composite(组合)
意图:
将对象组合成树形结构以表示“部分-整体”的层次结构。C o m p o s i t e 使得用户对单个对象和组合对象的使用具有一致性。
适用性:
你想表示对象的部分-整体层次结构。
你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
#!/usr/bin/python
#coding:utf8
"""
Composite
"""
class Component:
def __init__(self,strName):
self.m_strName = strName
def Add(self,com):
pass
def Display(self,nDepth):
pass
class Leaf(Component):
def Add(self,com):
print "leaf can't add"
def Display(self,nDepth):
strtemp = "-" * nDepth
strtemp=strtemp+self.m_strName
print strtemp
class Composite(Component):
def __init__(self,strName):
self.m_strName = strName
self.c = []
def Add(self,com):
self.c.append(com)
def Display(self,nDepth):
strtemp = "-"*nDepth
strtemp=strtemp+self.m_strName
print strtemp
for com in self.c:
com.Display(nDepth+2)
if __name__ == "__main__":
p = Composite("Wong")
p.Add(Leaf("Lee"))
p.Add(Leaf("Zhao"))
p1 = Composite("Wu")
p1.Add(Leaf("San"))
p.Add(p1)
p.Display(1);
9. Decorator(装饰)
意图:
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比生成子类更为灵活。
适用性:
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
处理那些可以撤消的职责。
当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
#!/usr/bin/python
#coding:utf8
'''
Decorator
'''
class foo(object):
def f1(self):
print("original f1")
def f2(self):
print("original f2")
class foo_decorator(object):
def __init__(self, decoratee):
self._decoratee = decoratee
def f1(self):
print("decorated f1")
self._decoratee.f1()
def __getattr__(self, name):
return getattr(self._decoratee, name)
u = foo()
v = foo_decorator(u)
v.f1()
v.f2()
10. Facade(外观)
意图:
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
适用性:
当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。Facade 可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过facade层。
客户程序与抽象类的实现部分之间存在着很大的依赖性。引入facade 将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
当你需要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过facade进行通讯,从而简化了它们之间的依赖关系。
class OrderModule:
def create_order(self):
print("创建订单")
class InventoryModule:
def check_inventory(self):
print("检查库存")
class PaymentModule:
def process_payment(self):
print("处理支付")
class EcommerceFacade:
def __init__(self):
self.order_module = OrderModule()
self.inventory_module = InventoryModule()
self.payment_module = PaymentModule()
def complete_purchase(self):
self.inventory_module.check_inventory()
self.order_module.create_order()
self.payment_module.process_payment()
# 模拟企业项目中使用
facade = EcommerceFacade()
facade.complete_purchase()
11. Flyweight(享元)
意图:
运用共享技术有效地支持大量细粒度的对象。
适用性:
一个应用程序使用了大量的对象。
完全由于使用大量的对象,造成很大的存储开销。
对象的大多数状态都可变为外部状态。
如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
应用程序不依赖于对象标识。由于Flyweight 对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。
12. Proxy(代理)
意图:
为其他对象提供一种代理以控制对这个对象的访问。
适用性:
在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。下面是一 些可以使用Proxy 模式常见情况:
1) 远程代理(Remote Proxy )为一个对象在不同的地址空间提供局部代表。 NEXTSTEP[Add94] 使用NXProxy 类实现了这一目的。Coplien[Cop92] 称这种代理为“大使” (Ambassador )。
2 )虚代理(Virtual Proxy )根据需要创建开销很大的对象。在动机一节描述的ImageProxy 就是这样一种代理的例子。
3) 保护代理(Protection Proxy )控制对原始对象的访问。保护代理用于对象应该有不同 的访问权限的时候。例如,在Choices 操作系统[ CIRM93]中KemelProxies为操作系统对象提供 了访问保护。
4 )智能指引(Smart Reference )取代了简单的指针,它在访问对象时执行一些附加操作。 它的典型用途包括:对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它(也称为SmartPointers[Ede92 ] )。
当第一次引用一个持久对象时,将它装入内存。
在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。
行为型
13. Interpreter(解释器)
意图:
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
适用性:
当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:
该文法简单对于复杂的文法, 文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式, 这样可以节省空间而且还可能节省时间。
效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下, 转换器仍可用解释器模式实现, 该模式仍是有用的。
14. Template Method(模板方法)
意图:
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
适用性:
一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是Opdyke 和Johnson所描述过的“重分解以一般化”的一个很好的例子[ OJ93 ]。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
控制子类扩展。模板方法只在特定点调用“hook ”操作(参见效果一节),这样就只允许在这些点进行扩展。
15. Chain of Responsibility(责任链)
意图:
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
适用性:
有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
可处理一个请求的对象集合应被动态指定。
16. Command(命令)
意图:
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。
适用性:
抽象出待执行的动作以参数化某对象,你可用过程语言中的回调(call back)函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。Command 模式是回调机制的一个面向对象的替代品。
在不同的时刻指定、排列和执行请求。一个Command对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传送给另一个不同的进程并在那儿实现该请求。
支持取消操作。Command的Excute 操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。Command 接口必须添加一个Unexecute操作,该操作取消上一次Execute调用的效果。执行的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用Unexecute和Execute来实现重数不限的“取消”和“重做”。
支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。在Command接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用Execute操作重新执行它们。
用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务( transaction)的信息系统中很常见。一个事务封装了对数据的一组变动。Command模式提供了对事务进行建模的方法。Command有一个公共的接口,使得你可以用同一种方式调用所有的事务。同时使用该模式也易于添加新事务以扩展系统。
17. Iterator(迭代器)
意图:
提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。
适用性:
访问一个聚合对象的内容而无需暴露它的内部表示。
支持对聚合对象的多种遍历。
为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。
18. Mediator(中介者)
意图:
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
适用性:
一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
想定制一个分布在多个类中的行为,而又不想生成太多的子类。
19. Memento(备忘录)
意图:
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
适用性:
必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态。
如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
20. Observer(观察者)
意图:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。
适用性:
当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
】
21. State(状态)
意图:
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
适用性:
一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常, 有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。
#!/usr/bin/python
#coding:utf8
'''
State
'''
class State(object):
"""Base state. This is to share functionality"""
def scan(self):
"""Scan the dial to the next station"""
self.pos += 1
if self.pos == len(self.stations):
self.pos = 0
print("Scanning... Station is", self.stations[self.pos], self.name)
class AmState(State):
def __init__(self, radio):
self.radio = radio
self.stations = ["1250", "1380", "1510"]
self.pos = 0
self.name = "AM"
def toggle_amfm(self):
print("Switching to FM")
self.radio.state = self.radio.fmstate
class FmState(State):
def __init__(self, radio):
self.radio = radio
self.stations = ["81.3", "89.1", "103.9"]
self.pos = 0
self.name = "FM"
def toggle_amfm(self):
print("Switching to AM")
self.radio.state = self.radio.amstate
class Radio(object):
"""A radio. It has a scan button, and an AM/FM toggle switch."""
def __init__(self):
"""We have an AM state and an FM state"""
self.amstate = AmState(self)
self.fmstate = FmState(self)
self.state = self.amstate
def toggle_amfm(self):
self.state.toggle_amfm()
def scan(self):
self.state.scan()
# Test our radio out
if __name__ == '__main__':
radio = Radio()
actions = [radio.scan] * 2 + [radio.toggle_amfm] + [radio.scan] * 2
actions = actions * 2
for action in actions:
action()
22. Strategy(策略)
意图:
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
适用性:
许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时[H087] ,可以使用策略模式。
算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
一个类定义了多种行为, 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
#!/usr/bin/python
#coding:utf8
"""
Strategy
In most of other languages Strategy pattern is implemented via creating some base strategy interface/abstract class and
subclassing it with a number of concrete strategies (as we can see at http://en.wikipedia.org/wiki/Strategy_pattern),
however Python supports higher-order functions and allows us to have only one class and inject functions into it's
instances, as shown in this example.
"""
import types
class StrategyExample:
def __init__(self, func=None):
self.name = 'Strategy Example 0'
if func is not None:
self.execute = types.MethodType(func, self)
def execute(self):
print(self.name)
def execute_replacement1(self):
print(self.name + ' from execute 1')
def execute_replacement2(self):
print(self.name + ' from execute 2')
if __name__ == '__main__':
strat0 = StrategyExample()
strat1 = StrategyExample(execute_replacement1)
strat1.name = 'Strategy Example 1'
strat2 = StrategyExample(execute_replacement2)
strat2.name = 'Strategy Example 2'
strat0.execute()
strat1.execute()
strat2.execute()
23. Visitor(访问者)
意图:
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
适用性:
一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是Opdyke和Johnson所描述过的“重分解以一般化”的一个很好的例子[OJ93]。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
控制子类扩展。模板方法只在特定点调用“hook ”操作(参见效果一节),这样就只允许在这些点进行扩展。
#!/usr/bin/python
#coding:utf8
'''
Visitor
'''
class Node(object):
pass
class A(Node):
pass
class B(Node):
pass
class C(A, B):
pass
class Visitor(object):
def visit(self, node, *args, **kwargs):
meth = None
for cls in node.__class__.__mro__:
meth_name = 'visit_'+cls.__name__
meth = getattr(self, meth_name, None)
if meth:
break
if not meth:
meth = self.generic_visit
return meth(node, *args, **kwargs)
def generic_visit(self, node, *args, **kwargs):
print('generic_visit '+node.__class__.__name__)
def visit_B(self, node, *args, **kwargs):
print('visit_B '+node.__class__.__name__)
a = A()
b = B()
c = C()
visitor = Visitor()
visitor.visit(a)
visitor.visit(b)
visitor.visit(c)
以上只是大概介绍, 具体要分场景去实际使用哈