Go设计模式之开闭设计原则

面向对象设计原则

在面向对象设计中,需要按照以下六个原则进行设计,代码才具有可读性/可扩展性等,实现高内聚低耦合。

开闭原则

一个软件实体如类,模块和函数应该对扩展开放,对修改关闭。

  • 用抽象构建框架,用实现扩展细节
  • 不以改动原有类的方式来实现新需求,而是应该以实现事先抽象出来的接口(或具体类继承抽象类)的方式来实现。

发现问题

如下场景,设计一个告警模块,根据不同阈值条件,发送不同的应急通知措施。

// 模块功能:当接口请求个数超过某个阈值发出告警
// 存储告警规则,可自由设置
type AlertRule struct {}
func (a *AlertRule) getMathedRule(api string) int64{
	return 0
}
// 告警通知类,支持短信/微信等各种渠道
type Notification struct {}
func (n *Notification) notify(level string) {

}

// 监控接口
type Alert struct {
	rule *AlertRule
	notification *Notification
}
// 构造函数创建实例,如果单实例参考之前文档单实例创建方式
func NewAlert(rule *AlertRule, notification *Notification) *Alert {
	return &Alert{
		rule:rule,
		notification:notification,
	}
}

func (a *Alert) check(api string, requestCnt, errCnt, durationOfSeconds int64) {
	tps := requestCnt/durationOfSeconds
	if tps > a.rule.getMathedRule(api) {
		a.notification.notify("URGENCY")
	}
	if errCnt > a.rule.getMathedRule(api) {
		a.notification.notify("SEVERE")
	}
}

以上代码实现非常简单,但是当需要增加一个新功能,比如当每秒钟超时请求个数超过某个阈值,需要触发告警通知,针对以上代码设计需要修改两处:1,修改check接口增加timeout入参,2,check内部增加超时逻辑代码,

// 接口处增加timeout入参
func (a *Alert) check(api string, requestCnt, errCnt, timeout, durationOfSeconds int64) {
	tps := requestCnt/durationOfSeconds
	if tps > a.rule.getMathedRule(api) {
		a.notification.notify("URGENCY")
	}
	if errCnt > a.rule.getMathedRule(api) {
		a.notification.notify("SEVERE")
	}
        // 此处增加超时逻辑
}

开闭原则实现

type Alertx struct {
	alertHandlers []AlertHandler   // 注册存储不同告警功能模块
}
type ApiStatInfo struct {
	api string
	rstcnt int64
	errcnt int64
	durOfSecs int64
}
// 设计接口
type AlertHandler interface {
	check(info ApiStatInfo)
}
// 构造函数
func NewAlertx() *Alertx{
	return &Alertx{
		alertHandlers:make([]AlertHandler,0),
	}
}

func (a *Alertx) addAlerHandler(handler AlertHandler) {
	a.alertHandlers = append(a.alertHandlers,handler)
}

func (a *Alertx) check(info ApiStatInfo) {
	for _,handle := range a.alertHandlers {
		handle.check(info)
	}
}

// 实现AlertHandler接口,按照TPS规则
type TpsAlertHandler struct {
	rule *AlertRule
	notification *Notification
}
func NewTpsAlertHandler(rule *AlertRule, notification *Notification) *TpsAlertHandler{
	return &TpsAlertHandler{
		rule:rule,
		notification:notification,
	}
}
func (t *TpsAlertHandler) check(info ApiStatInfo) {
	tps := info.rstcnt/info.durOfSecs
	if tps > t.rule.getMathedRule(info.api) {
		t.notification.notify("URGENCY")
	}
	fmt.Println("TPS")
}
// 实现AlertHandler接口,按照Err规则实现
type ErrAlertHandler struct {
	rule *AlertRule
	notification *Notification
}
func NewErrAlertHandler(rule *AlertRule, notification *Notification) *ErrAlertHandler{
	return &ErrAlertHandler{
		rule:rule,
		notification:notification,
	}
}
// 实现AlertHandler接口
func (e *ErrAlertHandler) check(info ApiStatInfo) {
	if info.errcnt > e.rule.getMathedRule(info.api) {
		e.notification.notify("SEVER")
	}
	fmt.Println("Err")
}

那么新增timeOut规则,只需要实现AlertHandler接口,并注册到Alertx。

// 那么新增timeOut规则,只需要实现AlertHandler接口,并注册到Alertx
type TimeOutAlertHandler struct {
	rule *AlertRule
	notification *Notification
}
func NewTimeAlertHandler(rule *AlertRule, notification *Notification) *TimeOutAlertHandler{
	return &TimeOutAlertHandler{
		rule:rule,
		notification:notification,
	}
}
func (t *TimeOutAlertHandler) check(info ApiStatInfo) {
	fmt.Println("TimeOut!")
}

测试用例

func TestAlertx(t *testing.T) {
	alter := NewAlertx()
	rule := new(AlertRule)
	apiInfo := ApiStatInfo{
		rstcnt:1000,
		errcnt:10,
		durOfSecs:50,
	}
	notification := new(Notification)
	alter.addAlerHandler(NewTpsAlertHandler(rule,notification))
	alter.addAlerHandler(NewErrAlertHandler(rule,notification))
	alter.addAlerHandler(NewTimeAlertHandler(rule,notification))
	alter.check(apiInfo)
}


完整代码实现可以访问个人Github主页查看实例

编辑于 2020-10-03 22:27