【Go实现】实践GoF的23种设计模式:SOLID原则( 六 )


# src/main/resources/pipelines/pipeline_0.yamlname: pipeline_0 # pipeline名称type: single_thread # pipeline类型input: # input插件定义(数据从哪里来)name: input_0 # input插件名称type: memory_mq # input插件类型context: # input插件的初始化上下文topic: access_log.topicfilter: # filter插件定义(需要经过哪些加工)- name: filter_0 # 加工流程filter_0定义,类型为log_to_jsontype: log_to_json- name: filter_1 # 加工流程filter_1定义,类型为add_timestamptype: add_timestamp- name: filter_2 # 加工流程filter_2定义,类型为json_to_monitor_eventtype: json_to_monitor_eventoutput: # output插件定义(加工后存储到哪里)name: output_0 # output插件名称type: memory_db # output插件类型context: # output插件的初始化上下文tableName: monitor_event_0
首先我们定义一个接口来表示“配置”这一抽象:
// demo/monitor/plugin/plugin.gopacket plugin// Config 插件配置抽象接口type Config interface {Load(conf string) error}
另外,上述配置中的input、、子项,可以认为是input.、.、.插件的配置项,由插件的配置项组合在一起,因此我们定义了如下几个的实现类:
【【Go实现】实践GoF的23种设计模式:SOLID原则】// demo/monitor/config/config.gopackage configtype item struct {Namestring`json:"name" yaml:"name"`PluginType string`json:"type" yaml:"type"`Ctxplugin.Context `json:"context" yaml:"context"`loadConffunc(conf string, item interface{}) error // 区分yaml和json的加载方式}// 输入插件配置type Input itemfunc (i *Input) Load(conf string) error {return i.loadConf(conf, i)}// 过滤插件配置type Filter itemfunc (f *Filter) Load(conf string) error {return f.loadConf(conf, f)}// 输出插件配置type Output itemfunc (o *Output) Load(conf string) error {return o.loadConf(conf, o)}// Pipeline插件配置type Pipeline struct {item`yaml:",inline"` // yaml嵌套时需要加上,inlineInputInput`json:"input" yaml:"input"`Filters []Filter`json:"filters" yaml:"filters,flow"`OutputOutput`json:"output" yaml:"output"`}func (p *Pipeline) Load(conf string) error {return p.loadConf(conf, p)}
因为涉及到从配置到对象的实例化过程,自然会想到使用***工厂方法模式***来创建对象 。另外因为.、input.、.和.都实现了接口,我们也很容易想到定义一个接口来表示“插件工厂”这一抽象,具体的插件工厂再实现该接口:
// 插件工厂接口,根据配置实例化插件type PluginFactory interface {Create(config plugin.Config) plugin.Plugin}// input插件工厂type InputPluginFactory struct {}func (i *InputPluginFactory) Create(config plugin.Config) plugin.Plugin {conf := config.(*config.Input)... // input插件实例化过程}// filter插件工厂type FilterPluginFactory struct {}func (f *FilterPluginFactory) Create(config plugin.Config) plugin.Plugin {conf := config.(*config.Filter)... // filter插件实例化过程}// output插件工厂type OutputPluginFactory struct {}func (o *OutputPluginFactory) Create(config plugin.Config) plugin.Plugin {conf := config.(*config.Output)... // output插件实例化过程}// pipeline插件工厂type PipelinePluginFactory struct {}func (p *PipelinePluginFactory) Create(config plugin.Config) plugin.Plugin {conf := config.(*config.Pipeline)... // pipeline插件实例化过程}
最后,通过来创建对象:
conf := &config.Pipeline{...}pipeline := NewPipelinePluginFactory().Create(conf)...
到目前为止,上述的设计看起来是合理的,运行也没有问题 。
但是,细心的读者可能会发现,每个插件工厂子类的方法的第一行代码都是一个转型语句,比如的是conf := .(*.) 。所以,上一段代码能够正常运行的前提是:传入.方法的入参必须是*.。如果客户端程序传入*.Input的实例,.方法将会抛出转型失败的异常 。