当实体 e1要调用 e2.func1的时候,一般情况下直接在e1的调用处new一个e2,并调用e2的函数。这样可能会引起一些依赖。 在设计松耦合的程序时,可以使用好莱坞风格,即将e2的函数通过e1的接口注册进e1的实体中,e1会随后在合适的时候调用。 例如:
type E1 struct {
willCall func()
}
func (e *E1) Reg(f func()) {
e.willCall = f // 框架在合适的时候注入e1的依赖
}
func (e *E1) Run(){
e.willCall() // e调用的是抽象的函数,而非具体
}
下面实现一个词频统计器,参考《编程风格-好代码的逻辑》进行实现。 这个词频统计器,包含三个部分: 1. stopwords检查器,对一些助词如is, to等不进行统计 2. data处理器:将源数据转为内部的小写词表 3. 统计器: 传入word,统计它的词频,并可以打印统计结果
值得注意的是,它们之间并没有任何显示的new依赖,而是通过抽象的函数进行占位。在调用的时候,framework会进行依赖注入。 在main函数中,演示了依赖注入的整个过程。
package main
import (
"fmt"
"strings"
)
var originData = `The target field within a path is name for the target. This field MUST only ever be present on prefix paths in the corresponding request and response messages. This field is optional for clients. When set in the prefix in a request, GetRequest, SetRequest or SubscribeRequest, the field MUST be reflected in the prefix of the corresponding GetResponse, SetResponse or SubscribeResponse by a server. This field is used to allow a name to be associated with all the data for a given stream if requested by a client. If a client does not set this field in the prefix of a request, it MUST NOT be set in the prefix of the corresponding response messages. The value for target is tied to the context of a client RPC and not persisted or shared among multiple clients.`
var originStopwords = []string{"be", "is", "by", "and", "in", "of", "are", "to", "if", "on", "for", "the"}
type FrameWork struct {
LoadHandlers []func()
WorkHandlers []func()
EndHandlers []func()
}
func (f *FrameWork) Reg(fn func(), ttype string) {
switch ttype {
case "load":
f.LoadHandlers = append(f.LoadHandlers, fn)
case "work":
f.WorkHandlers = append(f.WorkHandlers, fn)
case "end":
f.EndHandlers = append(f.EndHandlers, fn)
}
}
func (f *FrameWork) Run() {
for _, h := range f.LoadHandlers {
h()
}
for _, h := range f.WorkHandlers {
h()
}
for _, h := range f.EndHandlers {
h()
}
}
type StopWordFilter struct {
stopwords []string
}
func (s *StopWordFilter) Load() {
s.stopwords = originStopwords
}
func (s *StopWordFilter) IsStopWord(w string) bool {
for _, tp := range s.stopwords {
if tp == w {
return true
}
}
return false
}
type DataHandler struct {
filters []func(w string) bool
wordHandler []func(w string)
data []string
}
func (d *DataHandler) RegFilter(f func(w string) bool) {
d.filters = append(d.filters, f)
}
func (d *DataHandler) RegWordHandler(f func(w string)) {
d.wordHandler = append(d.wordHandler, f)
}
func (d *DataHandler) Load() {
tpdata := strings.Split(originData, " ")
for _, word := range tpdata {
word = strings.Replace(word, ".", "", -1)
word = strings.Replace(word, "'", "", -1)
word = strings.Replace(word, ",", "", -1)
word = strings.Replace(word, " ", "", -1)
word = strings.ToLower(word)
d.data = append(d.data, word)
}
}
func (d *DataHandler) Run() {
isFilter := func(word string) bool {
for _, filter := range d.filters {
if filter(word) == true {
return true
}
}
return false
}
for _, word := range d.data {
if isFilter(word) == false {
for _, h := range d.wordHandler {
h(word)
}
}
}
}
type WordsCounter struct {
counts map[string]int
}
func (w *WordsCounter) Load() {
w.counts = make(map[string]int)
}
func (w *WordsCounter) Count(word string) {
_, ok := w.counts[word]
if ok {
w.counts[word]++
} else {
w.counts[word] = 1
}
}
func (w *WordsCounter) PrintAll() {
for key := range w.counts {
fmt.Printf("word:%v, count:%v\n", key, w.counts[key])
}
}
func main() {
framework := new(FrameWork)
dataHandler := new(DataHandler)
stopwordsFilter := new(StopWordFilter)
counter := new(WordsCounter)
framework.Reg(dataHandler.Load, "load")
framework.Reg(stopwordsFilter.Load, "load")
framework.Reg(counter.Load, "load")
dataHandler.RegFilter(stopwordsFilter.IsStopWord)
dataHandler.RegWordHandler(counter.Count)
framework.Reg(dataHandler.Run, "work")
framework.Reg(counter.PrintAll, "end")
framework.Run()
}