结构型模式描述如何将类或者对象结合在一起形成更大的结构。
外观模式(Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式是一种对象结构型模式。
外观模式包含如下角色:
样例代码来源。
子系统对象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,其它代码如果需要使用细节功能时可以直接调用。
根据“单一职责原则”,在软件中将一个系统划分为若干个子系统有利于降低整个系统的复杂性,一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小,而达到该目标的途径之一就是引入一个外观对象,它为子系统的访问提供了一个简单而单一的入口。
更详细的介绍看图解设计模式。
适配器模式(Adapter Pattern) :将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
适配器模式包含如下角色:
样例代码来源。
目标抽象类:
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接口。
适配器模式包含四个角色:
在类适配器模式中,适配器类实现了目标抽象类接口并继承了适配者类,并在目标抽象类的实现方法中调用所继承的适配者类的方法;
在对象适配器模式中,适配器类继承了目标抽象类并定义了一个适配者类的对象实例,在所继承的目标抽象类方法中调用适配者类的相应业务方法。
适配器模式的主要优点是将目标类和被适配类解耦,增加了类的透明性和复用性,同时系统的灵活性和扩展性都非常好,更换适配器或者增加新的适配器都非常方便,符合“开闭原则”;
适配器模式适用情况包括:
更详细的介绍看图解设计模式。
装饰模式(Decorator Pattern) :
装饰模式包含如下角色:
type a interface
)a
接口的实现)type b interface
)a
和b
接口的实现,将a组合到结构体中)修饰类与原始类继承同样的父类,这样可以对原始类嵌套多个修饰类。
样例代码来源。
抽象构件:(父类)
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语言借助于匿名组合和非入侵式接口可以很方便实现装饰模式。
使用匿名组合,在装饰器中不必显式定义转调原对象方法。
装饰模式用于动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。它是一种对象结构型模式。
装饰模式包含四个角色:
使用装饰模式来实现扩展比继承更加灵活,它以对客户透明的方式动态地给一个对象附加更多的责任。
装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。
装饰模式的主要优点在于可以提供比继承更多的灵活性,可以通过一种动态的方式来扩展一个对象的功能,并通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,而且具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类;
主要缺点在于使用装饰模式进行系统设计时将产生很多小对象,而且装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。
装饰模式适用情况包括:
装饰模式可分为透明装饰模式和半透明装饰模式:
更详细的介绍看图解设计模式。