设计模式

创建型模式 阅读更多

0.1. 单例模式 0.1.1. 定义 0.1.2. 样例 sync.Once内部实现 0.1.3. 分析 优点 缺点 0.2. 简单工厂模式 0.2.1. 定义 0.2.2. 样例 0.2.3. 分析 0.3. 工厂方法模式 0.3.1. 定义 0.3.2. 样例 0.3.3. 分析 0.4. 抽象工厂模式 0.4.1. 定义 0.4.2. 样例 0.4.3. 分析 创建型模式对类的实例化过程进行了抽象,能够将对象的创建与对象的使用过程分离。 0.1. 单例模式 对于系统中的某些类来说,只有一个实例很重要: 一个系统中存在多个打印任务,但是只能有一个正在工作的任务 一个系统只能有一个窗口管理器或文件系统 一个系统只能有一个计时工具或ID(序号)生成器 一个系统中只能有一份统一的配置文件 如何保证一个类只有一个实例并且这个实例易于被访问呢? 定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象 更好的解决办法是让类自身负责保存它的唯一实例。这个类保证没有其他实例被创建,并且提供一个访问该实例的方法 0.1.1. 定义 单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。 单例模式的要点有三个: 某个类只能有一个实例 它必须自行创建这个实例 它必须自行向整个系统提供这个实例 单例模式是一种对象创建型模式。单例模式又名单件模式或单态模式。 单例模式只包含一个角色:Singleton(单例) 0.1.2. 样例 sync.Once是让函数方法只被调用执行一次的实现,最常应用于单例模式,例如: 初始化系统配置 保持数据库唯一连接 这样就可以满足单例模式的要求。 package singleton import "sync" //Singleton 是单例模式类 type Singleton struct{} var ( singleton *Singleton once sync.Once ) //GetInstance 用于获取单例模式对象 func GetInstance() *Singleton { once.Do(func() { singleton = &Singleton{} }) return singleton } sync.Once内部实现 type Once struct { // done indicates whether the action has been performed. // It is first in the struct because it is used in the hot path. // The hot path is inlined at every call site. // Placing done first allows more compact instructions on some architectures (amd64/x86), // and fewer instructions (to calculate offset) on other architectures. done uint32 // 调用标识符,Once对象初始化时,done值默认为0,Do方法被调用后变为1 m Mutex // 初始化竞态控制,在第一次调用Once.Do()方法时,会通过m加锁, // 保证在第一个Do()方法中的参数f()函数还未执行完毕时, // 其他此时调用Do()方法会被阻塞(不返回也不执行) } func (o *Once) Do(f func()) { // Do()方法的入参是一个无参数输入与返回的函数 // Note: Here is an incorrect implementation of Do: // // if atomic.CompareAndSwapUint32(&o.done, 0, 1) { // f() // } // // Do guarantees that when it returns, f has finished. // This implementation would not implement that guarantee: // given two simultaneous calls, the winner of the cas would // call f, and the second would return immediately, without // waiting for the first's call to f to complete. // This is why the slow path falls back to a mutex, and why // the atomic.StoreUint32 must be delayed until after f returns. // 下面这个if中原子读取o.done的值并判断,是为了保证其他调用会先阻塞,而不是直接退出 // 如果其他调用直接退出了,但是单例还没有初始化,就会有异常了 if atomic.LoadUint32(&o.done) == 0 { // 当o.done值为0时,执行doSlow()方法 // Outlined slow-path to allow inlining of the fast-path. o.doSlow(f) } // 当o.done值为1则退出Do()方法 } func (o *Once) doSlow(f func()) { o.m.Lock() // 加锁 defer o.m.Unlock() // 方法退出时释放锁 if o.done == 0 { // 再次检查o.done值 defer atomic.StoreUint32(&o.done, 1) // 原子操作将o.done值置为1 f() // 执行传入的函数 } } 注意:由于o.m.Lock()处的代码限定,once.Do()内部调用Do()方法时,会造成死锁的发生。 0.1.3. 分析 优点 提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它,并为设计及开发团队提供了共享的概念。 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。 允许可变数目的实例。我们可以基于单例模式进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例。 缺点 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。 单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致对象状态的丢失。 0.2. 简单工厂模式 0.2.1. 定义 简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。 简单工厂模式包含如下角色: Factory:工厂角色,工厂角色负责实现创建所有实例的内部逻辑 Product:抽象产品角色,抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口 ConcreteProduct:具体产品角色,具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。 0.2.2. 样例 样例代码来源。 工厂角色: package simplefactory //NewAPI return Api instance by type func NewAPI(t int) API { if t == 1 { return &hiAPI{} } else if t == 2 { return &helloAPI{} } return nil } 抽象产品角色: package simplefactory //API is interface type API interface { Say(name string) string } 具体产品角色: package simplefactory import "fmt" //hiAPI is one of API implement type hiAPI struct{} //Say hi to name func (*hiAPI) Say(name string) string { return fmt.Sprintf("Hi, %s", name) } //HelloAPI is another API implement type helloAPI struct{} //Say hello to name func (*helloAPI) Say(name string) string { return fmt.Sprintf("Hello, %s", name) } go 语言没有构造函数一说,所以一般会定义NewXXX函数来初始化相关类。 NewXXX 函数返回接口时就是简单工厂模式,也就是说Golang的一般推荐做法就是简单工厂。 在这个simplefactory包中只有API接口和NewAPI函数为包外可见,封装了实现细节。 0.2.3. 分析 简单工厂模式又称为静态工厂方法模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。 简单工厂模式包含三个角色: 工厂角色:负责实现创建所有实例的内部逻辑; 抽象产品角色:是所创建的所有对象的父类,负责描述所有实例所共有的公共接口; 具体产品角色:是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。 简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。 优点:在于实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责, 缺点:在于工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码将会非常复杂。 简单工厂模式适用情况包括:工厂类负责创建的对象比较少;客户端只知道传入工厂类的参数,对于如何创建对象不关心。 更详细的介绍看图解设计模式。 0.3. 工厂方法模式 0.3.1. 定义 工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。 工厂方法模式包含如下角色: Product:抽象产品 ConcreteProduct:具体产品 Factory:抽象工厂 ConcreteFactory:具体工厂 0.3.2. 样例 样例代码来源。 抽象产品: package factorymethod //Operator 是被封装的实际类接口 type Operator interface { SetA(int) SetB(int) Result() int } 具体产品: package factoryMethod type OperatorBase struct { a, b int } func (o *OperatorBase) SetA(i int) { o.a = i } func (o *OperatorBase) SetB(i int) { o.b = i } //PlusOperator Operator 的实际加法实现 type PlusOperator struct { *OperatorBase } //Result 获取结果 func (p PlusOperator) Result() int { return p.a + p.b } //MinusOperator Operator 的实际减法实现 type MinusOperator struct { *OperatorBase } //Result 获取结果 func (m MinusOperator) Result() int { return m.a - m.b } 抽象工厂: package factoryMethod //OperatorFactory 是工厂接口 type OperatorFactory interface { Create() Operator } 具体工厂: package factoryMethod //PlusOperatorFactory 是 PlusOperator 的工厂类 type PlusOperatorFactory struct{} func (PlusOperatorFactory) Create() Operator { return &PlusOperator{ OperatorBase: &OperatorBase{}, } } //MinusOperatorFactory 是 MinusOperator 的工厂类 type MinusOperatorFactory struct{} func (MinusOperatorFactory) Create() Operator { return &MinusOperator{ OperatorBase: &OperatorBase{}, } } 工厂方法模式使用子类的方式延迟生成对象到子类中实现。 Go中不存在继承 所以使用匿名组合来实现 0.3.3. 分析 工厂方法模式又称为工厂模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。 工厂方法模式包含四个角色: 抽象产品:定义产品的接口,是工厂方法模式所创建对象的超类型,即产品对象的共同父类或接口; 具体产品:实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,它们之间往往一一对应; 抽象工厂:声明了工厂方法,用于返回一个产品,它是工厂方法模式的核心,任何在模式中创建对象的工厂类都必须实现该接口; 具体工厂:抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户调用,返回一个具体产品类的实例。 工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责哪一个产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。 优点:增加新的产品类时无须修改现有系统,并封装了产品对象的创建细节,系统具有良好的灵活性和可扩展性; 缺点:增加新产品的同时需要增加新的工厂,导致系统类的个数成对增加,在一定程度上增加了系统的复杂性。 工厂方法模式适用情况包括: 一个类不知道它所需要的对象的类; 一个类通过其子类来指定创建哪个对象; 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定。 更详细的介绍看图解设计模式。 0.4. 抽象工厂模式 0.4.1. 定义 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。 抽象工厂模式包含如下角色: AbstractFactory:抽象工厂 ConcreteFactory:具体工厂 AbstractProduct:抽象产品 Product:具体产品 0.4.2. 样例 样例代码来源。 抽象工厂: package abstractFactory // DAOFactory DAO抽象模式工厂接口 type DAOFactory interface { CreateOrderMainDAO() OrderMainDAO CreateOrderDetailDAO() OrderDetailDAO } 具体工厂: package abstractFactory type RDBDAOFactory struct { } func (R *RDBDAOFactory) CreateOrderMainDAO() OrderMainDAO { return &RDBMainDAO{} } func (R *RDBDAOFactory) CreateOrderDetailDAO() OrderDetailDAO { return &RDBDetailDAO{} } type XMLDAOFactory struct { } func (X *XMLDAOFactory) CreateOrderMainDAO() OrderMainDAO { return &XMLMainDAO{} } func (X *XMLDAOFactory) CreateOrderDetailDAO() OrderDetailDAO { return &XMLDetailDAO{} } 抽象产品: package abstractFactory // OrderMainDAO 为订单主记录 type OrderMainDAO interface { SaveOrderMain() } // OrderDetailDAO 为订单详情纪录 type OrderDetailDAO interface { SaveOrderDetail() } 具体产品: package abstractFactory import "fmt" // RDBMainDAP 为关系型数据库的OrderMainDAO实现 type RDBMainDAO struct { } // SaveOrderMain ... func (R *RDBMainDAO) SaveOrderMain() { fmt.Print("rdb main save\n") } // RDBDetailDAO 为关系型数据库的OrderDetailDAO实现 type RDBDetailDAO struct { } // SaveOrderDetail ... func (R *RDBDetailDAO) SaveOrderDetail() { fmt.Print("rdb detail save\n") } // XMLMainDAO XML存储 type XMLMainDAO struct{} // SaveOrderMain ... func (*XMLMainDAO) SaveOrderMain() { fmt.Print("xml main save\n") } // XMLDetailDAO XML存储 type XMLDetailDAO struct{} // SaveOrderDetail ... func (*XMLDetailDAO) SaveOrderDetail() { fmt.Print("xml detail save") } 抽象工厂模式用于生成产品族的工厂,所生成的对象是有关联的。 如果抽象工厂退化成生成的对象无关联则成为工厂函数模式。 比如本例子中使用RDB和XML存储订单信息,抽象工厂分别能生成相关的主订单信息和订单详情信息。 如果业务逻辑中需要替换使用的时候只需要改动工厂函数相关的类就能替换使用不同的存储方式了。 0.4.3. 分析 抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。 抽象工厂模式包含四个角色: 抽象工厂:用于声明生成抽象产品的方法; 具体工厂:实现了抽象工厂声明的生成抽象产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中; 抽象产品:为每种产品声明接口,在抽象产品中定义了产品的抽象业务方法; 具体产品:定义具体工厂生产的具体产品对象,实现抽象产品接口中定义的业务方法。 抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构。 优点:隔离了具体类的生成,使得客户并不需要知道什么被创建,而且每次可以通过具体工厂类创建一个产品族中的多个对象,增加或者替换产品族比较方便,增加新的具体工厂和产品族很方便; 缺点:增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。 抽象工厂模式适用情况包括: 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节; 系统中有多于一个的产品族,而每次只使用其中某一产品族; 属于同一个产品族的产品将在一起使用; 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。 更详细的介绍看图解设计模式。

结构型模式 阅读更多

0.1. 外观模式 0.1.1. 定义 0.1.2. 样例 0.1.3. 分析 0.2. 适配器模式 0.2.1. 定义 0.2.2. 样例 0.2.3. 分析 0.3. 装饰模式 0.3.1. 定义 0.3.3. 样例 0.3.4. 分析 结构型模式描述如何将类或者对象结合在一起形成更大的结构。 0.1. 外观模式 0.1.1. 定义 外观模式(Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式是一种对象结构型模式。 外观模式包含如下角色: Facade: 外观对象 SubSystem:子系统对象 0.1.2. 样例 样例代码来源。 子系统对象A: package facade type AModuleAPI interface { TestA() string } type aModuleImpl struct { } func NewAModuleImpl() AModuleAPI { return &aModuleImpl{} } func (a *aModuleImpl) TestA() string { return "A module running" } 子系统对象B: package facade type BModuleAPI interface { TestB() string } type bModuleImpl struct { } func NewBModuleImpl() BModuleAPI { return &bModuleImpl{} } func (b *bModuleImpl) TestB() string { return "B Module running" } 外观对象: package facade import "fmt" type API interface { Test() string } type apiImpl struct { a AModuleAPI b BModuleAPI } func NewApiImpl(a AModuleAPI, b BModuleAPI) *apiImpl { return &apiImpl{ a: NewAModuleImpl(), b: NewBModuleImpl(), } } func (a *apiImpl) Test() string { aRet := a.a.TestA() bRet := a.b.TestB() return fmt.Sprintf("%s\n%s", aRet, bRet) } API为facade模块的外观接口,大部分代码使用此接口简化对facade类的访问。 facade模块同时暴露了a和b 两个Module 的NewXXX和interface,其它代码如果需要使用细节功能时可以直接调用。 0.1.3. 分析 根据“单一职责原则”,在软件中将一个系统划分为若干个子系统有利于降低整个系统的复杂性,一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小,而达到该目标的途径之一就是引入一个外观对象,它为子系统的访问提供了一个简单而单一的入口。 外观模式:也是“迪米特法则”的体现,通过引入一个新的外观类可以降低原有系统的复杂度,同时降低客户类与子系统类的耦合度。 外观模式:要求一个子系统的外部与其内部的通信通过一个统一的外观对象进行,外观类将客户端与子系统的内部复杂性分隔开,使得客户端只需要与外观对象打交道,而不需要与子系统内部的很多对象打交道。 外观模式:目的在于降低系统的复杂程度。 外观模式:从很大程度上提高了客户端使用的便捷性,使得客户端无须关心子系统的工作细节,通过外观对象即可调用相关功能。 更详细的介绍看图解设计模式。 0.2. 适配器模式 0.2.1. 定义 适配器模式(Adapter Pattern) :将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。 适配器模式包含如下角色: Target:目标抽象类 Adapter:适配器类 Adaptee:被适配类 Client:客户类 0.2.2. 样例 样例代码来源。 目标抽象类: package adapter type Target interface { Request() string } 被适配类: package adapter type Adaptee interface { SpecificRequest() string } type adapteeImpl struct { } func NewAdaptee() Adaptee { return &adapteeImpl{} } func (a *adapteeImpl) SpecificRequest() string { return "adaptee method" } 适配器类: package adapter type adapter struct { Adaptee } func NewAdapter(adaptee Adaptee) Target { return &adapter{Adaptee: adaptee} } func (a *adapter) Request() string { return a.SpecificRequest() } 实际使用中Adaptee一般为接口,并且使用工厂函数生成实例。 在Adapter中匿名组合Adaptee接口,所以Adapter类也拥有SpecificRequest实例方法,又因为Go语言中非入侵式接口特征,其实Adapter也适配Adaptee接口。 0.2.3. 分析 适配器模式:用于将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器。 适配器模式:既可以作为类结构型模式,也可以作为对象结构型模式。 适配器模式包含四个角色: 目标抽象类:定义客户要用的特定领域的接口; 适配器类:可以调用另一个接口,作为一个转换器,对适配者和抽象目标类进行适配,它是适配器模式的核心; 被适配类:是被适配的角色,它定义了一个已经存在的接口,这个接口需要适配; 在客户类中针对目标抽象类进行编程,调用在目标抽象类中定义的业务方法。 在类适配器模式中,适配器类实现了目标抽象类接口并继承了适配者类,并在目标抽象类的实现方法中调用所继承的适配者类的方法; 在对象适配器模式中,适配器类继承了目标抽象类并定义了一个适配者类的对象实例,在所继承的目标抽象类方法中调用适配者类的相应业务方法。 适配器模式的主要优点是将目标类和被适配类解耦,增加了类的透明性和复用性,同时系统的灵活性和扩展性都非常好,更换适配器或者增加新的适配器都非常方便,符合“开闭原则”; 适配器模式适用情况包括: 系统需要使用现有的类,而这些类的接口不符合系统的需要; 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类一起工作。 更详细的介绍看图解设计模式。 0.3. 装饰模式 0.3.1. 定义 装饰模式(Decorator Pattern) : 动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。 其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合,装饰模式是对功能的增强,这是它应用场景的一个重要特点。 根据翻译的不同,装饰模式也有人称之为“油漆工模式”,它是一种对象结构型模式。 装饰模式包含如下角色: Component: 抽象构件(type a interface) ConcreteComponent: 具体构件 (a接口的实现) Decorator: 抽象装饰类(type b interface) ConcreteDecorator: 具体装饰类 (a和b接口的实现,将a组合到结构体中) 修饰类与原始类继承同样的父类,这样可以对原始类嵌套多个修饰类。 0.3.3. 样例 样例代码来源。 抽象构件:(父类) package decorator type Component interface { Calc() int } 具体构件:(原始类) package decorator type ConcreteComponent struct { } func (c *ConcreteComponent) Calc() int { return 0 } 具体装饰类:(修饰类的实现) package decorator type MullDecorator struct { Component num int } func WarpMullDecorator(c Component, num int) Component { return &MullDecorator{ Component: c, num: num} } func (d *MullDecorator) Calc() int { return d.Component.Calc() * d.num } type AddDecorator struct { Component num int } func WarpAddDecorator(c Component, num int) Component { return &AddDecorator{ Component: c, num: num, } } func (d *AddDecorator) Calc() int { return d.Component.Calc() + d.num } 装饰模式使用对象组合的方式动态改变或增加对象行为。 Go语言借助于匿名组合和非入侵式接口可以很方便实现装饰模式。 使用匿名组合,在装饰器中不必显式定义转调原对象方法。 0.3.4. 分析 装饰模式用于动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。它是一种对象结构型模式。 装饰模式包含四个角色: 抽象构件:定义了对象的接口,可以给这些对象动态增加职责(方法); 具体构件:定义了具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法); 抽象装饰类:抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现; 具体装饰类:抽象装饰类的子类,负责向构件添加新的职责。 使用装饰模式来实现扩展比继承更加灵活,它以对客户透明的方式动态地给一个对象附加更多的责任。 装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。 装饰模式的主要优点在于可以提供比继承更多的灵活性,可以通过一种动态的方式来扩展一个对象的功能,并通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,而且具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类; 主要缺点在于使用装饰模式进行系统设计时将产生很多小对象,而且装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。 装饰模式适用情况包括: 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责; 需要动态地给一个对象增加功能,这些功能也可以动态地被撤销; 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。 装饰模式可分为透明装饰模式和半透明装饰模式: 在透明装饰模式中,要求客户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该声明具体构件类型和具体装饰类型,而应该全部声明为抽象构件类型; 半透明装饰模式中,允许用户在客户端声明具体装饰者类型的对象,调用在具体装饰者中新增的方法。 更详细的介绍看图解设计模式。