外观模式是一种结构类型的代码设计模式之一,其目的是提供一个简单的接口或门面(客户端视角的“外观”),使复杂的子系统或类更易于使用。它通过封装子系统中的一组接口,并提供一个统一的接口,简化了对子系统的操作。
模式实例代码
使用Go语言实现外观模式的示例代码:
package main
import "fmt"
// SubsystemA 是子系统A
type SubsystemA struct{}
func (s *SubsystemA) MethodA() {
fmt.Println("SubsystemA: MethodA")
}
// SubsystemB 是子系统B
type SubsystemB struct{}
func (s *SubsystemB) MethodB() {
fmt.Println("SubsystemB: MethodB")
}
// Facade 是外观
type Facade struct {
subsystemA *SubsystemA
subsystemB *SubsystemB
}
func NewFacade() *Facade {
return &Facade{
subsystemA: &SubsystemA{},
subsystemB: &SubsystemB{},
}
}
// Execute 执行操作
func (f *Facade) Execute() {
f.subsystemA.MethodA()
f.subsystemB.MethodB()
}
func main() {
facade := NewFacade()
facade.Execute()
}
上述代码中,SubsystemA和SubsystemB是两个子系统,它们分别实现了不同的方法。Facade是外观,它封装了SubsystemA和SubsystemB,提供了一个统一的接口Execute()来执行操作。通过外观模式,客户端只需与外观对象交互,而无需直接与子系统进行交互,从而简化了操作的复杂性。在示例代码中,客户端只需要创建外观对象并调用Execute()方法即可完成操作。
外观模式代码扩展性
考虑到代码的未来的扩展性,外观对象本身通常是保持不变的,不应该随着子系统的变化而频繁修改。外观模式充当了客户端和子系统之间的中介,隐藏了子系统的复杂性,客户端只需与外观对象进行交互。
如果后续需要扩展或修改子系统中的某个部分,可以通过以下方式来实现:
- 添加新的子系统:如果需要添加新的功能或子系统,可以创建一个新的子系统,并在外观对象中添加相应的方法,将新的子系统集成到外观中。这样客户端仍然只需要与外观对象进行交互,而不需要关心新的子系统的具体实现。
- 修改子系统的行为:如果需要修改子系统的行为,可以直接在子系统内部进行修改,而不会影响外观对象或客户端的代码。外观对象仅仅是对子系统的调用进行封装,修改子系统的行为不会改变外观对象的接口。
- 继承和重写:可以通过继承外观对象的方式来扩展或修改外观对象的行为。通过创建一个新的子类,并重写需要修改的方法,可以实现对外观对象行为的定制。这种方式可以支持对外观对象的扩展和灵活性。
总之,通过保持外观对象的稳定性和封装性,可以更容易地扩展和修改子系统,同时确保客户端的代码不需要修改。
比如未来需要添加新的子系统C时,可以按照以下步骤对外观模式进行扩展:
首先,在现有的代码基础上创建一个新的子系统C:
// 新增子系统C
type SubsystemC struct{}
func (s *SubsystemC) MethodC() {
fmt.Println("SubsystemC: MethodC")
}
然后,在外观对象中添加对子系统C的集成和方法调用:
// Facade 是外观
type Facade struct {
subsystemA *SubsystemA
subsystemB *SubsystemB
subsystemC *SubsystemC //扩展
}
func NewFacade() *Facade {
return &Facade{
subsystemA: &SubsystemA{},
subsystemB: &SubsystemB{},
subsystemC: &SubsystemC{}, //扩展
}
}
// Execute 执行操作
func (f *Facade) Execute() {
f.subsystemA.MethodA()
f.subsystemB.MethodB()
f.subsystemC.MethodC() //扩展
}
现在,外观对象就包含了子系统C,并在Execute()
方法中调用了SubsystemC
的方法。
客户端使用代码保持不变,仍只需创建外观对象并调用Execute()
方法,即可同时调用子系统A、B和C的相应方法,完成操作:
// 客户端操作保持不变
func main() {
facade := NewFacade()
facade.Execute()
}
与中介者模式的不同
外观模式和中介者模式的确有一些相似之处,但它们的主要目的和实现方式有所不同。
外观模式的主要目的是简化客户端与复杂子系统之间的交互,提供一个统一的接口或门面,隐藏子系统的复杂性。外观模式通过封装子系统,使得客户端可以更方便地使用子系统的功能,但客户端仍然直接与外观对象进行交互。
中介者模式的主要目的是减少对象之间的直接耦合,将对象间的交互通过一个中介对象进行协调。中介者模式通过将对象间的通信集中到一个中介对象中,从而降低了对象间的依赖关系。对象不再直接相互引用,而是通过中介者对象来进行通信。
以下是一个使用Go语言实现外观模式和中介者模式进行对比的示例代码:
// 外观模式示例
// SubsystemA 是子系统A
type SubsystemA struct{}
func (s *SubsystemA) MethodA() {
fmt.Println("SubsystemA: MethodA")
}
// SubsystemB 是子系统B
type SubsystemB struct{}
func (s *SubsystemB) MethodB() {
fmt.Println("SubsystemB: MethodB")
}
// Facade 是外观
type Facade struct {
subsystemA *SubsystemA
subsystemB *SubsystemB
}
func NewFacade() *Facade {
return &Facade{
subsystemA: &SubsystemA{},
subsystemB: &SubsystemB{},
}
}
func (f *Facade) Execute() {
f.subsystemA.MethodA()
f.subsystemB.MethodB()
}
// 中介者模式示例
// Mediator 是中介者
type Mediator struct {
colleagueA *ColleagueA
colleagueB *ColleagueB
}
func NewMediator() *Mediator {
return &Mediator{}
}
func (m *Mediator) SetColleagueA(colleagueA *ColleagueA) {
m.colleagueA = colleagueA
}
func (m *Mediator) SetColleagueB(colleagueB *ColleagueB) {
m.colleagueB = colleagueB
}
func (m *Mediator) CoordinateA() {
// 协调逻辑
fmt.Println("Mediator: Coordinate A")
m.colleagueB.ActionB()
}
func (m *Mediator) CoordinateB() {
// 协调逻辑
fmt.Println("Mediator: Coordinate B")
m.colleagueA.ActionA()
}
// ColleagueA 是同事A
type ColleagueA struct {
mediator *Mediator
}
func NewColleagueA(mediator *Mediator) *ColleagueA {
return &ColleagueA{mediator: mediator}
}
func (c *ColleagueA) ActionA() {
fmt.Println("ColleagueA: Action A")
c.mediator.CoordinateA()
}
// ColleagueB 是同事B
type ColleagueB struct {
mediator *Mediator
}
func NewColleagueB(mediator *Mediator) *ColleagueB {
return &ColleagueB{mediator: mediator}
}
func (c *ColleagueB) ActionB() {
fmt.Println("ColleagueB: Action B")
c.mediator.CoordinateB()
}
func main() {
// 外观模式示例
facade := NewFacade()
facade.Execute()
// 中介者模式示例
mediator := NewMediator()
colleagueA := NewColleagueA(mediator)
colleagueB := NewColleagueB(mediator)
mediator.SetColleagueA(colleagueA)
mediator.SetColleagueB(colleagueB)
colleagueA.ActionA()
}
在示例中,外观模式通过封装子系统A和B提供了统一的接口外观对象(Facade),客户端只需与外观对象进行交互。
而中介者模式中,中介者对象(Mediator)被用来协调同事对象(ColleagueA和ColleagueB)之间的交互,同事对象不直接耦合,而是通过中介者进行通信。
总结起来,外观模式主要简化了客户端与复杂子系统之间的接口,而中介者模式是用来减少对象间直接耦合。
总结
设计模式中的外观模式旨在简化客户端与复杂子系统之间的交互,通过提供一个统一的接口或门面来隐藏子系统的复杂性。外观模式封装了子系统的一组接口,使客户端更容易使用子系统的功能。
外观对象本身在未来的扩展中需要保持稳定,如果需要扩展或修改子系统的功能,可以通过添加新的子系统、修改子系统的行为或通过继承和重写外观对象来实现。
与外观模式相比,中介者模式的主要目的是减少对象之间的直接耦合。中介者模式通过引入一个中介者对象,将对象间的通信集中到该对象中进行协调,从而降低了对象间的依赖关系。中介者模式中,对象不再直接相互引用,而是通过中介者对象来进行通信。总的来说,外观模式和中介者模式在目的和实现方式上存在区别:外观模式简化了客户端与复杂子系统之间的交互,中介者模式减少对象间的直接耦合。外观模式通过封装子系统提供统一的接口,而中介者模式通过引入中介者对象协调对象间的交互。