表驱动模式+匿名函数优化大量if判断

1.表驱动模式

表驱动模式是一种常见的编程模式,从表里面查找信息而不使用逻辑语句(if和case)。核心操作是将输入因素作为直接或者间接的索引,到数组或者map里找到直接的结果或者对应的处理过程。

  • 表驱动模式示例

普通写法:

func isLeapYear(year int) bool {
    return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}

func getDaysInMonth(year, month int) (int, error) {
    var (
        days int
        err  error
    )
    switch month {
    case 1:
        days = 31
    case 2:
        days = 28
        if isLeapYear(year) {
            days = 29
        }
    case 3:
        days = 31
    case 4:
        days = 30
    case 5:
        days = 31
    case 6:
        days = 30
    case 7:
        days = 31
    case 8:
        days = 31
    case 9:
        days = 30
    case 10:
        days = 31
    case 11:
        days = 30
    case 12:
        days = 31
    default:
        err = errors.New("invalid month")
    }
    return days, err
}

表驱动模式写法

func getDaysInMonth(year, month int) (int, error) {
    if month < 1 || month > 12 {
        return 0, errors.New("invalid month")
    }
    var daysInMonth = [12]int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
    if isLeapYear(year) {
        daysInMonth[1] = 29
    }
    return daysInMonth[month-1], nil
}

由上面的代码可以看出,表驱动模式的代码更加简洁,逻辑也非常清晰,最关键的是加入有新的逻辑链加入的时候,扩展更加方便简单

2. 匿名函数

匿名函数(anonymous function),是指没有指定名称的函数。它们通常用于需要函数作为参数或返回值的地方,能够简化代码并提高灵活性。

在Go中,匿名函数可以作为值存储在变量中,或者作为参数传递给其他函数。这在表驱动模式中非常有用,因为可以将处理逻辑直接定义为匿名函数,并放入映射表中。

普通写法:

type Calculator struct{}

func (c *Calculator) Calculate(op string, a, b int) (int, error) {
    var (
        result int
        err    error
    )
    switch op {
    case "add":
        result = a + b
    case "sub":
        result = a - b
    case "mul":
        result = a * b
    case "div":
        if b == 0 {
            panic("division by zero")
        }
        result = a / b
    default:
        err = fmt.Errorf("unsupported operation: %s", op)
    }
    return result, err
}

表驱动+匿名函数写法

type Handler func(int, int) int

type Calculator struct {
    handlers map[string]Handler
}

func NewCalculator() *Calculator {
    return &Calculator{
        // 此步骤中的注册handler可以移到register方法中实现
        handlers: map[string]Handler{
            "add": func(a, b int) int {
                return a + b
            },
            "sub": func(a, b int) int {
                return a - b
            },
            "mul": mul(),
            "div": div,
        },
    }
}

func (c *Calculator) Calculate(op string, a, b int) (int, error) {
    handler, ok := c.handlers[op]
    if !ok {
        return 0, fmt.Errorf("unsupported operation: %s", op)
    }
    return handler(a, b), nil
}

// 闭包写法
func mul() Handler {
    return func(a, b int) int {
        return a * b
    }
}

func div(a, b int) int {
    if b == 0 {
        panic("division by zero")
    }
    return a / b
}

比较上述两种写法,如果需要新加一种计算方法比如取模运算(mod)

  1. 普通写法需要更改主方法Calculate中的代码,再增加一个case分支来实现:
type Calculator struct{}

func (c *Calculator) Calculate(op string, a, b int) (int, error) {
    var (
        result int
        err    error
    )
    switch op {
    case "add":
        result = a + b
    case "sub":
        result = a - b
    case "mul":
        result = a * b
    case "div":
        if b == 0 {
            panic("division by zero")
        }
        result = a / b
    case "mod":
        result = a % b
    default:
        err = fmt.Errorf("unsupported operation: %s", op)
    }
    return result, err
}
  1. 表驱动+匿名函数写法只需要在注册计算方法的代码中新增一个取模的计算方法即可:
type Handler func(int, int) int

type Calculator struct {
    handlers map[string]Handler
}

func NewCalculator() *Calculator {
    return &Calculator{
        handlers: map[string]Handler{
            "add": func(a, b int) int {
                return a + b
            },
            "sub": func(a, b int) int {
                return a - b
            },
            "mul": mul(),
            "div": div,
            "mod": mod,
        },
    }
}

func (c *Calculator) Calculate(op string, a, b int) (int, error) {
    handler, ok := c.handlers[op]
    if !ok {
        return 0, fmt.Errorf("unsupported operation: %s", op)
    }
    return handler(a, b), nil
}

// 闭包写法
func mul() Handler {
    return func(a, b int) int {
        return a * b
    }
}

func div(a, b int) int {
    if b == 0 {
        panic("division by zero")
    }
    return a / b
}

func mod(a, b int) int {
    return a % b
}

总结:

由上两种写法可以看出表驱动模式+匿名函数的写法在有新的逻辑加入的时候,扩展更加方便,逻辑更加清晰,特别是判断的分支中逻辑处理比较复杂的情况下,这种优势更加明显。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容