一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭。 简单地说:就是当别人要修改软件功能的时候,使得他不能修改我们原有代码,只能新增代码实现软件功能修改的目的。
作为程序员先上代码,比如我们想实现一个权限校验的功能。我们可能这样写:
type PermissionChecker struct {
}
func (c *PermissionChecker) HasPermission(ctx *gin.Context, name string) bool {
var permissions []string
switch ctx.GetString("authType") {
case "jwt":
permissions = c.extractPermissionsFromJwt(ctx.Request.Header)
case "basic":
permissions = c.getPermissionsForBasicAuth(ctx.Request.Header)
case "applicationKey":
permissions = c.getPermissionsForApplicationKey(ctx.Query("applicationKey"))
}
}
上面的方法,基于用于的认证方式 jwt、basic 或者 applicationKey ,然后分别去做权限的校验。感觉也没有啥问题,常规操作。
但如果我们此时想扩展一下,新增一个校验,就必须改动这里的代码。于是利用开闭原则,我们改一下代码:
type PermissionChecker struct {
providers []PermissionProvider
}
func (c *PermissionChecker) HasPermission(ctx *gin.Context, name string) bool {
var permissions []string
for _, provider := range c.providers {
if ctx.GetString("authType") != provider.Type() {
continue
}
permissions = provider.GetPermissions(ctx)
break
}
}
我们将所有的验证方法放到一个切片里面存储,然后在统一的地方遍历选择合适的认证方式。
这样改动后,如果新增一个校验方式,我们只需要注册到 providers 这个切片就可以了。从而避免修改原有的代码。
总之,开闭原则就允许我们在不修改之前代码的前提下安全的扩展我们的程序。