里氏替换&开放关闭

里氏替换

Let Φ(x) be a property provable about objects x of type T. Then Φ(y) should be true for objects y of type S where S is a subtype of T

本质上就是类设计中的继承,它强调类所实现的行为。参数的类型指定为基类,而实际传参的时候使用具体的子类。每次扩展新的行为,都通过创建一个新的子类来实现。在Go的设计中,继承是通过接口类型来实现的。

开放关闭

Software entities (classes, modules, function, etc) should be open for extension, but closed for modification.

A class is closed, since it may be complied, stored in a library, baselined and used by client classes. but it alse be open, since any new class may use it as parent, adding new features. when a descendant class is defined, there is no need to change the original or to disturb its clients.

原则上支持扩展,禁止修改,感觉是里氏替代的扩展。基类或者接口是对修改关闭的,而具体的实现是对修改开放的。

例子

参考代码:github.com/gin-gonic/gin/binding

声明Binding接口来当作基类,Binding对修改关闭。

// Binding describes the interface which needs to be implemented for binding the
// data present in the request such as JSON request body, query parameters or
// the form POST.
type Binding interface {
	Name() string
	Bind(*http.Request, interface{}) error
}

工厂模式创建子类,每个子类实现处理不同的请求类型。如果扩展新的contentType的话,创建新的子类对修改开放。

// Default returns the appropriate Binding instance based on the HTTP method
// and the content type.
func Default(method, contentType string) Binding {
	if method == "GET" {
		return Form
	}

	switch contentType {
	case MIMEJSON:
		return JSON
	case MIMEXML, MIMEXML2:
		return XML
	case MIMEPROTOBUF:
		return ProtoBuf
	case MIMEMSGPACK, MIMEMSGPACK2:
		return MsgPack
	default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
		return Form
	}
}

里氏替代原则实现处理:

// MustBindWith binds the passed struct pointer using the specified binding engine.
// It will abort the request with HTTP 400 if any error ocurrs.
// See the binding package.
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) {
	if err = c.ShouldBindWith(obj, b); err != nil {
		c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind)
	}

	return
}

总结

通过抽象基类来约束行为,通过实现基类来扩展具体的实现。最终达到修改对现有项目影响最小的目的。

参考文章:

  1. Open/Closed Principle
  2. Liskov Substitution Principle