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,把领域表达出来,把通用语言编程特定领域语言,何尝不是对问题领域的提炼和对语义的提升,这个代码的意图就在此处。
网友评论