Character Specifications for A Word in Golang

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

序言

兩個月前,筆者有幸參加了一次Code Retreat活動,主要負責點評Golang小組的代碼,整個過程很有收穫,尤其到了活動的最後,重量級大咖 劉光聰 分享了他C++版和Scala版的作品,讓人大呼過癮,於是就有了一個實現Golang版作品的想法。

恰巧筆者當時正在啟動撰寫Golang單元測試相關的系列文章,所以這個事情就暫時擱淺了,直到完成了已計劃的四篇文章。畢竟兩個月過去了,Code Retreat活動中需求演化的一些細節有些遺忘,於是查看簡書,找到了相關的文章,即 劉光聰 同學的經典文章《應用正交設計與組合式設計》,認真學習後感覺很有價值,立即開啟了Golang版的實現之旅。

需求一:判斷某個單詞是否包含數字

這個需求比較簡單,代碼實現如下:

func HasDigit(word string) bool {    for _, c := range word {        if unicode.IsDigit(c) {            return true        }    }    return false}

需求二:判斷某個單詞是否包含大寫字母

有了需求一的基礎後,可以通過copy-paste快速實現需求二:

func HasDigit(word string) bool {    for _, c := range word {        if unicode.IsDigit(c) {            return true        }    }    return false}func HasUpper(word string) bool {    for _, c := range str {        if unicode.IsUpper(c) {            return true        }    }    return false}

很明顯,HasDigit函數和HasUpper函數除過if的條件判斷外,其餘代碼都一樣,所以我們使用抽象這個強大的屠龍刀來消除重複:

  1. 定義一個介面CharSpec,作為所有字元謂詞的抽象,方法Satisfy用來判斷謂詞是否為真
  2. 針對需求一定義具有原子語義的謂詞IsDigit
  3. 針對需求二定義具有原子語義的謂詞IsUpper

謂詞相關代碼實現如下:

type CharSpec interface {    Satisfy(c rune) bool}type IsDigit struct {}func (i IsDigit) Satisfy(c rune) bool {    return unicode.IsDigit(c)}type IsUpper struct {}func (i IsUpper) Satisfy(c rune) bool {    return unicode.IsUpper(c)}

要完成需求,還必須將謂詞注入給單詞的Has語義函數,而Exists具有Has語義,同時表達力很強:

func Exists(word string, spec CharSpec) bool {    for _, c := range word {        if spec.Satisfy(c) {            return true        }    }    return false}

通過Exists判斷某個單詞word是否包含數字:

isDigit := IsDigit{}ok := Exists(word, isDigit)...

通過Exists判斷某個單詞word是否包含大寫字母:

isUpper := IsUpper{}ok := Exists(word, isUpper)...

其實需求二的故事還沒完:)

對於普通的程式員來說,能完成上面的代碼已經很好了,而對於經驗豐富的程式員來說,在需求一剛完成後就發現了新的變化方向,即單詞的Has語義和字元的Is語義是兩個變化方向,所以在需求二開始前就通過重構分離了變化方向:

type IsDigit struct {}func (i IsDigit) Satisfy(c rune) bool {    return unicode.IsDigit(c)}func Exists(word string, spec IsDigit) bool {    for _, c := range word {        if spec.Satisfy(c) {            return true        }    }    return false}

通過Exists判斷某個單詞word是否包含數字:

isDigit := IsDigit{}ok := Exists(word, isDigit)...

在需求二出來後,謂詞被第一顆子彈擊中,我們根據Uncle Bob的建議,應用開放封閉原則,於是也就寫出了普通程式員在需求二時消除重複後的代碼。

殊途同歸,這並不是巧合,而是有理論依據。

我們一起回顧一下 袁英傑先生 提出的正交設計四原則:

  1. 一個變化導致多處修改:消除重複
  2. 多個變化導致一處修改:分離不同的變化方向
  3. 不依賴不必要的依賴:縮小依賴範圍
  4. 不依賴不穩定的依賴:向著穩定的方向依賴

這四個原則的提出是為了使得實現簡單設計四原則中的第二條“消除重複”的目標而具有更明確的實踐指導性。我們應用正交設計四原則,可以將系統分解得到很多單一職責的小類,然後再將它們根據需要而靈活的組合起來。

細細品味正交設計四原則,你就會發現:第一條是被動策略,而後三條是主動策略。這就是說,第一條是一種事後補救的策略,而後三條是一種事前預防的策略,目標都是為了消除重複。

從上面的分析可以看出,普通的程式員更擅長使用被動策略,而經驗豐富的程式員更擅長使用主動策略。Anyway,他們殊途同歸,都消除了重複。

需求三:判斷某個單詞是否包含_

不管是包含底線還是中劃線,都有原子語義Equals,我們將代碼快速實現:

type Equals struct {    c rune}func (e Equals) Satisfy(c rune) bool {    return c == e.c}

通過Exists判斷某個單詞word是否包含_:

isUnderline := Equals{'_'}ok := Exists(word, isUnderline)...

需求四:判斷某個單詞是否不包含_

字母是底線的謂詞是Equals,那麼字母不是底線的謂詞就是給Equals前增加一個修飾語義IsNot,IsNot修飾謂詞後是一個新的謂詞,代碼實現如下:

type IsNot struct {    spec CharSpec}func (i IsNot) Satisfy(c rune) bool {    return !i.spec.Satisfy(c)}

單詞不包含底線,就不是Exists語義了,而是ForAll語義,代碼實現如下:

func ForAll(word string, spec CharSpec) bool {    for _, c := range str {        if !spec.Satisfy(c) {            return false        }    }    return true}

通過ForAll判斷某個單詞word是否不包含_:

isNotUnderline := IsNot{Equals{'_'}}ok := ForAll(word, isNotUnderline)...

功能雖然實現了,但是我們發現Exists函數和ForAll函數有很多代碼是重複的,使用重構基本手法Extract Method:

func expect(word string, spec CharSpec, ok bool) bool {    for _, c := range word {        if spec.Satisfy(c) == ok {            return ok        }    }    return !ok}func Exists(word string, spec CharSpec) bool {    return expect(word, spec, true)}func ForAll(word string, spec CharSpec) bool {    return expect(word, spec, false)}

需求五:判斷某個單詞是否包含_或者*

字母是x或y的謂詞具有組合語義AnyOf,其中x為Equals{'_'},y為Equals{'*'},代碼實現如下:

type AnyOf struct {    specs []CharSpec}func (a AnyOf) Satisfy(c rune) bool {    for _, spec := range a.specs {        if spec.Satisfy(c) {            return true        }    }    return false}

通過Exists判斷某個單詞word是否包含_或*:

isUnderlineOrStar := AnyOf{[]CharSpec{Equals{'_'}, Equals{'*'}}}ok := Exists(word, isUnderlineOrStar)...

需求六:判斷某個單詞是否包含空白符,但除去空格

空白符包括空格、定位字元和分行符號等,具體見下面代碼:

func IsSpace(r rune) bool {    // This property isn't the same as Z; special-case it.    if uint32(r) <= MaxLatin1 {        switch r {        case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0:            return true        }        return false    }    return isExcludingLatin(White_Space, r)}

字母是空白符的謂詞還沒有實現,我們定義具有原子語義的謂詞IsSpace:

type IsSpace struct {}func (i IsSpace) Satisfy(c rune) bool {    return unicode.IsSpace(c)}

字母是x和y的謂詞具有組合語義AllOf,其中x為IsSpace,y為IsNot{Equals{' '}},代碼實現如下:

type AllOf struct {    specs []CharSpec}func (a AllOf) Satisfy(c rune) bool {    for _, spec := range a.specs {        if !spec.Satisfy(c) {            return false        }    }    return true}

通過Exists判斷某個單詞word是否包含空白符,但除去空格:

isSpaceAndNotBlank := AllOf{[]CharSpec{IsSpace{}, IsNot{Equals{' '}}}}ok := Exists(word, isSpaceAndNotBlank)...

需求七:判斷某個單詞是否包含字母x,且不區分大小寫

不區分大小寫是一種具有修飾語義的謂詞IgnoreCase,代碼實現如下:

type IgnoreCase struct {    spec CharSpec}func (i IgnoreCase) Satisfy(c rune) bool {    return i.spec.Satisfy(unicode.ToLower(c))}

通過Exists判斷某個單詞word是否包含字母x,且不區分大小寫:

isXIgnoreCase := IgnoreCase{Equals{'x'}}ok := Exists(word, isXIgnoreCase)...

小結

在需求的實現過程中,我們不斷應用正交設計四原則,從而得到了很多小類或小函數,再通過組合來完成一個個既定需求,不但領域表達力強,而且非常靈活,容易應對變化。

字母的謂詞是本文的重點,有以下三類

分類 謂詞
原子語義 IsDigit
IsUpper
Equals
IsSpace
修飾語義 IsNot
IgnoreCase
組合語義 AnyOf
AllOf

它的領域模型如下所示:


char-spec.png
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.