基本思想:将某个模块的启动逻辑也封装在该模块内。需要的方法至少有:注册方法、启动方法和优雅退出。

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

​ 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
}