美文网首页
经典题FizzBuzzWhizz Go版本

经典题FizzBuzzWhizz Go版本

作者: Wu杰语 | 来源:发表于2021-10-29 15:44 被阅读0次

FizzBuzzWhizz是一个比较经典的题目,见https://blog.csdn.net/lanxuezaipiao/article/details/24989879,使用一串ifelse就可以实现,但是为了可扩展,从领域角度,用DSL写法的Go版本代码如下:

代码

package bizz

import "strconv"

type Action interface {
    Act(i int) string
}

type ToFizz struct {
}

func (act *ToFizz) Act(i int) string {
    return "Fizz"
}

type ToBuzz struct {

}

func (b *ToBuzz) Act(i int) string {
    return "Buzz"
}

type ToWhizz struct {

}

func (w *ToWhizz) Act(i int) string {
    return "Whizz"
}

type ToString struct {

}

func (t *ToString) Act(i int) string {
    return strconv.Itoa(i);
}
package bizz

import (
    "strconv"
    "strings"
)

type Prediction interface {
    Pred(i int) bool
}

type Times struct {
    Value int
}

func (t *Times) Pred(i int) bool {
    return i % t.Value == 0
}

type Contain struct {
    Value int
}

func (c *Contain) Pred(i int) bool {
    return strings.Contains(strconv.Itoa(c.Value), strconv.Itoa(i));
}

type AlwaysTrue struct {
    Value int
}

func (t AlwaysTrue) Pred(i int) bool {
    return true;
}
package bizz

type Rule interface {
    Apply(i int) (interface{}, bool)
}

type AtomRule struct {
    P Prediction
    A Action
}

func (r *AtomRule) Apply(i int) (interface{}, bool) {
    if r.P.Pred(i) {
        return r.A.Act(i), true
    } else {
        return nil, false
    }
}

type OrRule struct {
    r1 Rule
    r2 Rule
}

func (r *OrRule) Apply(i int) (interface{}, bool) {
    if v, ok := r.r1.Apply(i); ok {
        return v, ok
    }
    if v, ok := r.r2.Apply(i); ok {
        return v, ok
    }
    return nil, false
}

type Or3Rule struct {
    R1 Rule
    R2 Rule
    R3 Rule
}

func (r *Or3Rule) Apply(i int) (interface{}, bool) {
    or := OrRule{r.R1, r.R2}
    if v, ok := or.Apply(i); ok {
        return v, ok
    }
    if v, ok := r.R3.Apply(i); ok {
        return v, ok
    }
    return nil, false
}

type Or4Rule struct {
    R1 Rule
    R2 Rule
    R3 Rule
    R4 Rule
}

func (r *Or4Rule) Apply(i int) (interface{}, bool) {
    or3 := Or3Rule{r.R1, r.R2, r.R3}
    if v, ok := or3.Apply(i); ok {
        return v, ok
    }
    if v, ok := r.R4.Apply(i); ok {
        return v, ok
    }
    return nil, false
}

type AndRule struct {
    R1 Rule
    R2 Rule
}

func (r *AndRule) Apply(i int) (interface{}, bool) {
    var value1, value2 string
    if v1, ok := r.R1.Apply(i); ok {
        value1 = v1.(string)
    } else {
        return nil, false
    }

    if v2, ok := r.R2.Apply(i); ok {
        value2 = v2.(string)
    } else {
        return nil, false
    }

    return value1 + value2, true
}

type And3Rule struct {
    R1 Rule
    R2 Rule
    R3 Rule
}

func (r *And3Rule) Apply(i int) (interface{}, bool) {
    and := AndRule{r.R1, r.R2}
    var value1, value2 string
    if v1, ok := and.Apply(i); ok {
        value1 = v1.(string)
    } else {
        return nil, false
    }

    if v2, ok := r.R3.Apply(i); ok {
        value2 = v2.(string)
    } else {
        return nil, false
    }

    return value1 + value2, true
}
package main

import (
    "bizz/src/bizz"
    "fmt"
)

func main() {
    var rule1_3 bizz.Rule = &bizz.AtomRule{&bizz.Times{3}, &bizz.ToFizz{}}
    var rule1_5 bizz.Rule = &bizz.AtomRule{&bizz.Times{5}, &bizz.ToBuzz{}}
    var rule1_7 bizz.Rule = &bizz.AtomRule{&bizz.Times{7}, &bizz.ToWhizz{}}

    r1 := &bizz.Or3Rule{rule1_3, rule1_5, rule1_7}
    r2 := &bizz.Or4Rule{&bizz.And3Rule{rule1_3, rule1_5, rule1_7},
                    &bizz.AndRule{rule1_3, rule1_5},
                    &bizz.AndRule{rule1_3, rule1_7},
                    &bizz.AndRule{rule1_5, rule1_7}}
    r3 := &bizz.AtomRule{&bizz.Contain{3}, &bizz.ToFizz{}}
    r4 := &bizz.AtomRule{&bizz.AlwaysTrue{}, &bizz.ToString{}}
    r := &bizz.Or4Rule{r3, r2, r1, r4}

    for i := 1; i < 200; i++ {
        if v, ok := r.Apply(i); ok {
            fmt.Printf("Predict %d value is %s\n", i, v)
        } else {
            fmt.Printf("Predict %d get wrong\n", i)
        }
    }
}

领域表达式就是上述main函数中的一串rule表达式,当增加新的规则,如不涉及领域规则扩展,可以直接编写rule表达式就可以了。

go经验总结

  • go mod包管理,引入自定义类的时候,import的是“mod名称/目录”, 具体使用的时候需要使用 pakagename.type的方式使用
  • 自定义类对外需需要暴露的接口定义、结构定义、成员定义一律要大写,否则访问不到。
  • go语言也支持多态,主要要注意如何写,具体参见https://zhuanlan.zhihu.com/p/165621566文章,上述代码中Rule、Prediction、Action是多态的实现基础。

小结

FizzBuzzWhizz的代码搞这么复杂,貌似是把问题复杂化,脱离了场景确实是这样的。但是当有具体应用场景的时候,通过构建DSL,把领域表达出来,把通用语言编程特定领域语言,何尝不是对问题领域的提炼和对语义的提升,这个代码的意图就在此处。

相关文章

网友评论

      本文标题:经典题FizzBuzzWhizz Go版本

      本文链接:https://www.haomeiwen.com/subject/zkbialtx.html