让业务代码更优雅的方法——类型工厂
最近碰到一个需求,用结构化数据结构(如JSON)构造一个表达式。
已设计数据结构如下:
// Value represents a value with given type type Value struct { // type of this value: // number|string|boolean|object|array|expr Type string `json:"type"` Data json.RawMessage `json:"data"` // specific value for non-field content }
问题来了,Data字段根据Type值的内容,需要反序列化为不同的数据结构。
方法很简单:
func (v *Value) UnmarshalData() (interface{}, error) { var d interface{} switch v.Type { case "number": x := float64(0) d = &x case "string": x := "" d = &x case "object": x := &Object{} d = x case "array": x := &Array{} d = x case "expr": x := &Expr{} d = x case ... } err:=json.Unmarshal(v.Data, d) return d, err }
如果你有代码洁癖,你会发现这个switch-case非常刺眼。
设想,如果需要设计的类型有成百上千种,这个UnmarshalData函数会变得多么庞大和难以维护。
2. 解决方法
可以看到,以上switch-case所做的事情,不外乎是输入一个字符串,输出一个interface{}。
那么有么有可能用一个函数来代替呢,答案是肯定的:
// Factory impliments a factory that can create multi products by type name type Factory struct { mp map[string]reflect.Type name string } // newJsonFactory create a new factory func NewFactory(name string) *Factory { return &Factory{ mp: make(map[string]reflect.Type), name: name, } } // MustReg register the creator by name, it panic if name is duplicate func (f *Factory) MustReg(name string, v interface{}) { if _, ok := f.mp[name]; ok { panic(fmt.Errorf("duplicate reg of %s,%#v", v.TypeName(), v)) } t := reflect.TypeOf(v) for t.Kind() == reflect.Ptr { t = t.Elem() } f.mp[name] = t return nil } // Create make product by name func (f *Factory) Create(name string) (interface{}, error) { t, ok := f.mp[name] if !ok { return nil, fmt.Errorf("product [%s] cannot create from factory %s", name, f.name) } return reflect.New(t).Interface(), nil }
业务代码可以简化为:
// factory regist some flex json objects var factory = NewFactory("flexObjCreator") func init() { factory.MustReg("number", (*ValNumber)(nil)) factory.MustReg("string", (*ValString)(nil)) factory.MustReg("boolean", (*ValBoolean)(nil)) factory.MustReg("object", (*ValObject)(nil)) factory.MustReg("array", (*ValArray)(nil)) factory.MustReg("expr", (*Expr)(nil)) } func (v *Value) UnmarshalData() (interface{}, error) { d, err := factory.Create(v.Type) if err != nil { return nil, err } err := json.Unmarshal(v.Data, d) return d, err }
可以看到,业务代码已经简单到增加一个类型只需要增加一行注册函数调用。
更有甚者,这个init函数可以拆分到每个类型的实现文件中去分散注册。
这样扩展一个类型的时候,只需要新增一个value_xxx.go文件即可,不需要在任何外部文件增加任何一行代码,代码可读性,可维护性,可谓完美。