基本思想:将某个模块的启动逻辑也封装在该模块内。需要的方法至少有:注册方法、启动方法和优雅退出。
type Government interface {
Register(Department) error
Start() error
GracefulShutdown() error
}
type Department interface {
Name() string
Start(ch chan struct{}) error
Secretary
}
type Secretary interface {
Send(mail Mail) error
Recv() (mail Mail, err error)
}
type Mail interface {
SourceDepartment() string
DestinationDepartment() string
Content() interface{}
}
Department
Department代指每一个模块,每个模块都要实现Deparment的Name()方法和Start()方法,通过为每个模块指定Start()方法可以将每个模块的启动逻辑封装在该模块内,使得模块之间耦合更少。
Government
Government是所有模块的控制中心,所有模块在启动时向Government进行注册,Government的启动方法会调用每个模块自己的启动方法,Goverment的GracefulShutdown方法会给每个模块的关闭channel发送一条消息,让他们都停止执行。
Secretary
Secretary用于模块之间通信,每个模块都需要一个Secretary负责在Department之间收发信件。模块间的通信都必须通过Secretary进行,减少模块调用的耦合。
mail是一个封装了模块通信消息的接口,可以根据实际需要添加自己的方法。
下面是一个实现的例子
department_interface.go
package department
const (
MailBoxCapacity int = 128
)
var (
DepartmentMailbox map[string]chan Mail
)
type Government interface {
Register(department Department) error
Start() []error
GracefulShutdown() error
}
type Department interface {
Name() string
Start(ch chan struct{}) error
}
type Secretary interface {
Send(mail Mail) error
Recv() (mail Mail, err error)
}
type Mail interface {
SourceDepartment() string
DestinationDepartment() string
Content() interface{}
TimeStamp() int64
SetSourceDepartment(department string)
SetDestinationDepartment(department string)
SetContent(content interface{})
SetTimeStamp(timestamp int64)
}
department.go
package modules
import (
"fmt"
"os"
"os/signal"
"syscall"
)
type government struct {
modules []Department
closeChannels map[string]chan struct{}
}
func NewGovernment() Government {
DepartmentMailbox = make(map[string]chan Mail)
return &government{
closeChannels: make(map[string]chan struct{}),
}
}
func (gmt *government) Register(department Department) error {
if _, ok := gmt.closeChannels[department.Name()]; ok {
return fmt.Errorf("department already registered")
}
gmt.modules = append(gmt.modules, department)
gmt.closeChannels[department.Name()] = make(chan struct{}, 1)
DepartmentMailbox[department.Name()] = make(chan Mail, MailBoxCapacity)
return nil
}
func (gmt *government) Start() []error {
var (
errors []error
)
for _, department := range gmt.modules {
err := department.Start(gmt.closeChannels[department.Name()])
if err != nil {
errors = append(errors, err)
}
}
return errors
}
func (gmt *government) GracefulShutdown() error {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)
<-c
for _, closeChannel := range gmt.closeChannels {
closeChannel <- struct{}{}
}
return nil
}
type secretary struct {
departmentName string
}
func NewSecretary(departmentName string) Secretary {
return &secretary{
departmentName: departmentName,
}
}
func (s *secretary) Send(mail Mail) error {
if ch, ok := DepartmentMailbox[mail.DestinationDepartment()]; ok {
ch <- mail
return nil
}
return fmt.Errorf("no such department in government")
}
func (s *secretary) Recv() (mail Mail, err error) {
if ch, ok := DepartmentMailbox[s.departmentName]; ok {
mail = <-ch
if mail.DestinationDepartment() != s.departmentName {
return nil, fmt.Errorf("the mail receiver mismatch the department name")
}
return mail, nil
}
return nil, fmt.Errorf("no such department in government")
}
type mail struct {
sourceDepartment string
destinationDepartment string
timestamp int64
content interface{}
}
func NewMail(sourceDepartment string, destinationDepartment string, timestamp int64, content interface{}) Mail {
return &mail{
sourceDepartment: sourceDepartment,
destinationDepartment: destinationDepartment,
timestamp: timestamp,
content: content,
}
}
func (m *mail) SourceDepartment() string {
return m.sourceDepartment
}
func (m *mail) DestinationDepartment() string {
return m.destinationDepartment
}
func (m *mail) Content() interface{} {
return m.content
}
func (m *mail) TimeStamp() int64 {
return m.timestamp
}
func (m *mail) SetSourceDepartment(sourceDepartment string) {
m.sourceDepartment = sourceDepartment
}
func (m *mail) SetDestinationDepartment(destinationDepartment string) {
m.destinationDepartment = destinationDepartment
}
func (m *mail) SetContent(content interface{}) {
m.content = content
}
func (m *mail) SetTimeStamp(timestamp int64) {
m.timestamp = timestamp
}