目录

定义

为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口、这个接口使得这一子系统更加容易使用。

其本质:封装交互,简化调用。

应用场景

代码生成器中有:模板生成器,逻辑生成器,数据生成器等。那么客户端就应该有相应的代码生成处理模块。

package main

import (
	"fmt"
	"sync"
)

//配置类
type Config struct {
	IsGenView, IsGenDAO, IsGenService bool
}

func (c *Config) setIsGenView(b bool) {
	c.IsGenView = b
}

func (c *Config) setIsGenDAO(b bool) {
	c.IsGenDAO = b
}

func (c *Config) setIsGenService(b bool) {
	c.IsGenService = b
}

func (c *Config) getIsGenView() bool {
	return c.IsGenView
}

func (c *Config) getIsGenDAO() bool {
	return c.IsGenDAO
}

func (c *Config) getIsGenService() bool {
	return c.IsGenService
}

// ConfigManager 是一个配置管理器,使用单例模式实现
type ConfigManager struct {
	config Config
}

var configManager *ConfigManager
var once sync.Once

func GetInstance() *ConfigManager {
	once.Do(func() {
		configManager = &ConfigManager{config: Config{
			IsGenView:    true,
			IsGenDAO:     true,
			IsGenService: true,
		}}
	})
	return configManager
}

//各个子系统模块
type View struct{}
type DAO struct{}
type Service struct{}

func (v *View) Generate() {
	if GetInstance().config.IsGenView {
		fmt.Println("View files has generate.")
	}
}

func (d *DAO) Generate() {
	if GetInstance().config.IsGenDAO {
		fmt.Println("DAO files has generate.")
	}
}

func (receiver *Service) Generate() {
	if GetInstance().config.IsGenService {
		fmt.Println("Service files has generate.")
	}
}

func main() {
	view := &View{}
	dao := &DAO{}
	svc := &Service{}
	view.Generate()
	dao.Generate()
	svc.Generate()
}

以上代码,在客户端使用时候需要与子系统中多个模块交互,对于客户端而言,是一个麻烦,不能将简单的使用生成代码功能。而且如果某个子模块发生了变化,还可能引起客户端也要随着变化。

外观模式结构

Facade

Facade: 定义子系统的多个模块对外的高层接口,通常需要调用内部多个模块,从而把客户的请求代理给适当的子系统对象

子模块接受Facade 对象的委派真正实现功能,各个模块之间可能有交互。 Facade 对象知道各个模块,但是各个子模块不应该知道 Facade 对象

示例代码

package main

import (
	"fmt"
	"sync"
)

//配置类
type Config struct {
	IsGenView, IsGenDAO, IsGenService bool
}


// ConfigManager 是一个配置管理器,使用单例模式实现
type ConfigManager struct {
	config Config
}

var configManager *ConfigManager
var once sync.Once

func GetInstance() *ConfigManager {
	once.Do(func() {
		configManager = &ConfigManager{config: Config{
			IsGenView:    true,
			IsGenDAO:     true,
			IsGenService: true,
		}}
	})
	return configManager
}

//各个子系统模块
type View struct{}
type DAO struct{}
type Service struct{}

func (v *View) Generate() {
	if GetInstance().config.IsGenView {
		fmt.Println("View files has generate.")
	}
}

func (d *DAO) Generate() {
	if GetInstance().config.IsGenDAO {
		fmt.Println("DAO files has generate.")
	}
}

func (receiver *Service) Generate() {
	if GetInstance().config.IsGenService {
		fmt.Println("Service files has generate.")
	}
}

// 定义Facade
type Facade struct{}

// 将客户端需要实现的代码在Facade结构体中实现
func (f *Facade) Generate() {
	view := &View{}
	dao := &DAO{}
	svc := &Service{}
	view.Generate()
	dao.Generate()
	svc.Generate()
}

func main() {
	//使用Facade对象
	facade := &Facade{}
	facade.Generate()
}

外观模式的目的

其不是给子系统添加新的功能,而是为了让外部减少与子系统内部多个模块的交互,松散耦合,从而让外部能够更简单的使用子系统。 因为外观模式是当作子系统对外的接口出现的,虽然也可以在Facade中定义一些子系统中没有的功能,但是不建议这么做。外观应该是包装以有的功能,它主要负责组合已有功能来实现客户需要,而不是添加新的实现。

外观模式提供子系统中缺省的实现,即提供子系统中默认的实现。

Facade 方法只是实现一个功能的组合调用,其本身并不进行功能处理。

优缺点

  • 优点

    1. 松散耦合
    2. 简单易用
    3. 更好地划分访问的层次
  • 缺点

    过多或者不太合理Facade 也容易让人迷惑,到底调用Facade 好呢,还是直接调用模块好呢。

参考 - 研磨设计模式(外观模式)