灰姑娘(GO版)

故事背景(简化版)

  • 辛德瑞拉有个继母和2个姐姐,每天被她们欺负
  • 有一天,城里举行一场舞会。参加舞会需要礼服和鞋子。姐姐们都有,而辛德瑞拉没有。
  • 仙女用魔法给辛德瑞拉变出了礼服和鞋子(水晶鞋),并告诉辛德瑞拉魔法会在午夜12点后失效
  • 辛德瑞拉在城里见到王子
  • 午夜12点,辛德瑞拉不得已要马上离开,在仓皇间留下了一只水晶鞋。
  • 王子拿着水晶鞋寻找辛德瑞拉
  • 只有辛德瑞拉才能穿上水晶鞋,姐姐们穿上都掉下来
  • 灰姑娘和王子在一起,幸福地生活

登场人物设定

go语言不像其它语言存在class。使用构造体来定义登场人物

package main

import "fmt"

// Sex -- 性别
type Sex int

//
const (
    _ Sex = iota
    Woman
    Man
)

// Actor -- 登场角色
type Actor struct {
    Name  string
    Age   int
    Sex   Sex
    Cos   Costume
    Shoes *Shoes
}

// NewActor -- 创建角色
func NewActor(n string, age int, s Sex) *Actor {
    return &Actor{
        Name: n,
        Age:  age,
        Sex:  s,
    }
}

// Say -- 角色-说
func (a *Actor) Say(s string) {
    fmt.Printf("%v: %v\n", a.Name, s)
}

// SetCostume -- 角色-穿服装
func (a *Actor) SetCostume(c Costume) {
    a.Cos = c
}

// SetShoes -- 角色-穿鞋
func (a *Actor) SetShoes(s *Shoes) {
    a.Shoes = s
}

Actor构造体使用NewActor来创建角色实例。
使用NewActor这个函数就可以创建辛德瑞拉,王子,继母等角色了

常量中生成连续整数

const (
    _ Sex = iota // _ Sex = 0
    Woman // Woman Sex = 1
    Man // Man Sex = 2
)

使用iota可以生成连续整数,iota只能在const里使用

服装构造体定义

参加舞会需要礼服。男的燕尾服,女的连衣裙

package main

// Costume -- 服装接口
type Costume interface {
    Wear(a *Actor) bool
}

// Dress -- 连衣裙
type Dress struct {
    Owner *Actor
}

// NewDress -- 生成连衣裙
func NewDress(a *Actor) Costume {
    return &Dress{
        Owner: a,
    }
}

// Wear -- 只有女性且所有者才能穿上
func (d *Dress) Wear(a *Actor) bool {
    return a == d.Owner && a.Sex == Woman
}

// Tailcoat -- 燕尾服
type Tailcoat struct {
    Owner *Actor
}

// NewTailcoat -- 生成燕尾服
func NewTailcoat(a *Actor) Costume {
    return &Tailcoat{
        Owner: a,
    }
}

// Wear -- 只有男性且所有者才能穿上
func (t *Tailcoat) Wear(a *Actor) bool {
    return a == t.Owner && a.Sex == Man
}

定义了连衣裙燕尾服构造体了,还实现了Costume服装接口。这样不管是连衣裙还是燕尾服,角色是否匹配,就可以通过Costume服装接口Wear方法来判断了

鞋子构造体定义

故事里需要水晶鞋。

package main

// Shoes -- 鞋子
type Shoes struct {
    Owner *Actor
}

// NewShoes -- 生成鞋子
func NewShoes(a *Actor) *Shoes {
    return &Shoes{
        Owner: a,
    }
}

// Wear -- 鞋子只能持有者能穿
func (s *Shoes) Wear(a *Actor) bool {
    return a == s.Owner
}

这里并没有实现Costume服装接口Wear方法

和继母,两个姐姐一起生活的辛德瑞拉

辛德瑞拉有个继母和2个姐姐,每天被她们欺负

    setMother := NewActor("继母", 52, Woman)
    sisterA := NewActor("姐姐A", 23, Woman)
    sisterB := NewActor("姐姐B", 20, Woman)
    cinderella := NewActor("辛德瑞拉", 18, Woman)

    setMother.Say("今天欺负一下辛德瑞拉~~~")
    sisterA.Say("今天欺负一下辛德瑞拉~~~")
    sisterB.Say("今天欺负一下辛德瑞拉~~~")
    cinderella.Say("orz...")

运行结果

继母: 今天欺负一下辛德瑞拉~~~
姐姐A: 今天欺负一下辛德瑞拉~~~
姐姐B: 今天欺负一下辛德瑞拉~~~
辛德瑞拉: orz...

城里举行舞会

舞会定义

package main

import (
    "fmt"
    "sync"
)

// Ball -- 舞会
type Ball struct {
    m          sync.Mutex
    Entries    []*Actor
    Clock      int //时间
    FinishedAt int
}

// NewBall ...
func NewBall(startedAt, finishedAt int) *Ball {
    return &Ball{
        Clock:      startedAt,
        FinishedAt: finishedAt,
    }
}

// Start ...
func (b *Ball) Start() {
    fmt.Printf("舞会开始")
}

// Dancing ...
func (b *Ball) Dancing() {
    b.m.Lock()
    defer b.m.Unlock()
    fmt.Printf("现在%d点\n", b.Clock)
    for _, a := range b.Entries {
        fmt.Printf("%v 在跳舞\n", a.Name)
    }
    b.Clock++
}

// Finish ...
func (b *Ball) Finish() {
    fmt.Println("舞会结束")
}

// IsFinish ...
func (b *Ball) IsFinish() bool {
    return b.Clock >= b.FinishedAt
}

// Entry ...
func (b *Ball) Entry(a *Actor) {
    if a.Cos != nil {
        b.Entries = append(b.Entries, a)
        fmt.Printf("%v 参加了舞会", a.Name)
    } else {
        fmt.Println("没有服装不能参加舞会")
        fmt.Printf("%v 不能参加舞会", a.Name)
    }
}

// Exit ...
func (b *Ball) Exit(a *Actor) {
    b.m.Lock()
    defer b.m.Unlock()
    var entries []*Actor
    for _, e := range b.Entries {
        if e != a {
            entries = append(entries, e)
        }
    }

    b.Entries = entries
    fmt.Printf("%v 离开舞会回家了\n", a.Name)
}

参加舞会必须有服装

    if a.Cos != nil {
        b.Entries = append(b.Entries, a)
        fmt.Printf("%v 参加了舞会", a.Name)
    } else {
        fmt.Println("没有服装不能参加舞会")
        fmt.Printf("%v 不能参加舞会", a.Name)
    }

通过Actor构造体的Costume接口来检查能否参加

可能发生重入

type Ball struct {
    m          sync.Mutex
    ...
}

// 省略
    b.m.Lock()
    defer b.m.Unlock()

ExitDancing同时执行的时候,可能会使程序崩溃。
使用sync.Mutex来上锁防止
可以使用-race参数来确认go run -race ./

举行舞会

ball := NewBall(19, 27)

参加舞会前的准备

继母家的服装屋

package main

// DressRoom ...
type DressRoom struct {
    Dresses []*Dress
}

// NewDressRoom ...
func NewDressRoom() *DressRoom {
    return &DressRoom{}
}

// Store -- 保管连衣裙
func (d *DressRoom) Store(actors ...*Actor) {
    for _, a := range actors {
        cos := NewDress(a)
        if dress, ok := cos.(*Dress); ok {
            d.Dresses = append(d.Dresses, dress)
        }
    }
}

// GetDress -- 获取连衣裙
func (d *DressRoom) GetDress(a *Actor) {
    for _, dress := range d.Dresses {
        if dress.Wear(a) {
            a.SetCostume(dress)
        }
    }
}

可变个数参数

Store(actors ...*Actor) 

...3个点支持不限定个数参数

类型声明

Actor构造体里Cos Costume匹配到实体类型为Dress,保存到服装屋

        if dress, ok := cos.(*Dress); ok {
            d.Dresses = append(d.Dresses, dress)
        }

继母和姐姐们的准备

    //舞会服装准备
    dressRoom := NewDressRoom()
    dressRoom.Store(setMother, sisterA, sisterB)

去参加舞会

继母和姐姐们去参加舞会,而辛德瑞拉没有礼服不能参加

    //继母有连衣裙
    dressRoom.GetDress(setMother)

    //继母参加舞会
    ball.Entry(setMother)

    //姐姐A,B有连衣裙,参加舞会
    dressRoom.GetDress(sisterA)
    ball.Entry(sisterA)
    dressRoom.GetDress(sisterB)
    ball.Entry(sisterB)

    //辛德瑞拉没有连衣裙

    dressRoom.GetDress(cinderella)
    ball.Entry(cinderella)

运行结果

继母 参加了舞会
姐姐A 参加了舞会
姐姐B 参加了舞会
没有服装不能参加舞会
辛德瑞拉 不能参加舞会

魔法变出了礼服和水晶鞋

魔法生成道具

package main

import "fmt"

// Magic ...
type Magic struct {
    Target *Actor
    Broken chan int
}

// NewMagic ...
func NewMagic(a *Actor) *Magic {
    return &Magic{
        Target: a,
        Broken: make(chan int, 1),
    }
}

// GenerateDress ...
func (m *Magic) GenerateDress() Costume {
    fmt.Printf("%v 获得了魔法道具连衣裙\n", m.Target.Name)
    return NewDress(m.Target)
}

// GenerateGlassShoes ...
func (m *Magic) GenerateGlassShoes() *Shoes {
    fmt.Printf("%v 获得了魔法道具水晶鞋\n", m.Target.Name)
    return NewShoes(m.Target)
}

辛德瑞拉穿上了魔法道具

    magic := NewMagic(cinderella)
    cinderella.SetCostume(magic.GenerateDress())
    cinderella.SetShoes(magic.GenerateGlassShoes())

魔法会在0点失效,警告辛德瑞拉需要回家了

func (m *Magic) Limit(limit chan int) {
    <-limit //从limit接收值
    fmt.Println("快到晚上0点了")
    fmt.Println("魔法要被解除了")
    m.Broken <- 1 //往m.Broken发送值
}

辛德瑞拉去参加舞会

    limit := make(chan int, 1)
    go magic.Limit(limit)

    ball.Entry(cinderella)

运行结果

辛德瑞拉 获得了魔法道具连衣裙
辛德瑞拉 获得了魔法道具水晶鞋
辛德瑞拉 参加了舞会

舞会上一见钟情的王子

王子身穿燕尾服登场

    prince := NewActor("王子", 18, Man)
    tailcoat := NewTailcoat(prince)
    prince.SetCostume(tailcoat)
    ball.Entry(prince)

运行结果

王子 参加了舞会

舞会进行中

大家都在跳舞,而辛德瑞拉需要在24点回家

    finished := make(chan int, 1)
    go func() {
        for !ball.IsFinished() {
            <-time.After(1 * time.Second)
            ball.Dancing()

            if ball.Clock == 24 {
                limit <- 1
            }
        }
        ball.Finish()
        finished <- 1
    }()

使用time.After的管道每1秒接收信号,执行Dancing方法
当24点时,需要给limit管道发送信号,警告辛德瑞拉魔法要失效,需要回家
finished管道用来接收舞会结束信号

魔法失效,辛德瑞拉遗失了水晶鞋

    <-magic.Broken

    ball.Exit(cinderella)
    falledShoes := cinderella.Shoes
    cinderella.Shoes = nil

王子捡到了水晶鞋

    foundShoes := falledShoes

    //舞会结束
    <-finished

运行结果

舞会开始
现在19点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
辛德瑞拉 在跳舞
王子 在跳舞
现在20点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
辛德瑞拉 在跳舞
王子 在跳舞
现在21点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
辛德瑞拉 在跳舞
王子 在跳舞
现在22点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
辛德瑞拉 在跳舞
王子 在跳舞
现在23点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
辛德瑞拉 在跳舞
王子 在跳舞
快到晚上0点了
魔法要被解除了
辛德瑞拉 离开舞会回家了
现在24点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
王子 在跳舞
现在25点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
王子 在跳舞
现在26点
继母 在跳舞
姐姐A 在跳舞
姐姐B 在跳舞
王子 在跳舞
舞会结束

王子拿鞋寻找辛德瑞拉

从舞会参加者里寻找

    fmt.Println("王子开始寻找辛德瑞拉")

    for _, a := range ball.Entries {
        if a.Sex == Woman {
            if foundShoes.Wear(a) {
                fmt.Printf("找到了,是%v 的水晶鞋\n", a.Name)
            } else {
                fmt.Printf("%v: 不是%v 的水晶鞋\n", prince.Name, a.Name)
            }
        }
    }

从参加舞会的女性中寻找, 辛德瑞拉中途离开,不在名单ball.Entries

只有辛德瑞拉能穿起鞋子不掉下来

    if foundShoes.Wear(cinderella) {
        fmt.Printf("找到了,是%v 的水晶鞋\n", cinderella.Name)
    }

运行结果

王子开始寻找辛德瑞拉
王子: 不是继母 的水晶鞋
王子: 不是姐姐A 的水晶鞋
王子: 不是姐姐B 的水晶鞋
找到了,是辛德瑞拉 的水晶鞋

结局

灰姑娘和王子在一起,幸福地生活

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注