Go語言字串的鏈式處理——操作與資料分離的設計技巧

2020-07-16 10:05:09
使用 SQL 語言從資料庫中獲取資料時,可以對原始資料進行排序(sort by)、分組(group by)和去重(distinct)等操作,SQL 將資料的操作與遍歷過程作為兩個部分進行隔離,這樣操作和遍歷過程就可以各自獨立地進行設計,這就是常見的資料與操作分離的設計。

對資料的操作進行多步驟的處理被稱為鏈式處理,本例中使用多個字串作為資料集合,然後對每個字串進行一系列的處理,使用者可以通過系統函數或者自定義函數對鏈式處理中的每個環節進行自定義。

首先給出本節完整程式碼:
package main

import (
    "fmt"
    "strings"
)

// 字串處理常式,傳入字串切片和處理鏈
func StringProccess(list []string, chain []func(string) string) {

    // 遍歷每一個字串
    for index, str := range list {

        // 第一個需要處理的字串
        result := str

        // 遍歷每一個處理鏈
        for _, proc := range chain {

            // 輸入一個字串進行處理,返回資料作為下一個處理鏈的輸入。
            result = proc(result)
        }

        // 將結果放回切片
        list[index] = result
    }
}

// 自定義的移除字首的處理常式
func removePrefix(str string) string {

    return strings.TrimPrefix(str, "go")
}

func main() {

    // 待處理的字串列表
    list := []string{
        "go scanner",
        "go parser",
        "go compiler",
        "go printer",
        "go formater",
    }

    // 處理常式鏈
    chain := []func(string) string{
        removePrefix,
        strings.TrimSpace,
        strings.ToUpper,
    }

    // 處理字串
    StringProccess(list, chain)

    // 輸出處理好的字串
    for _, str := range list {
        fmt.Println(str)
    }

}

1) 字串處理常式

字串處理常式(StringProccess)需要外部提供資料來源,一個字串切片(list[]string),另外還要提供一個鏈式處理常式的切片(chain[]func(string)string),鏈式處理切片中的一個處理常式的定義如下:

func(string)string

這種處理常式能夠接受一個字串輸入,處理後輸出。

strings 包中將字串變為小寫就是一種處理常式的形式,strings.ToLower() 函數能夠將傳入的字串的每一個字元變為小寫,strings.ToLower 定義如下:

func ToLower(s string) string

字串處理常式(StringProccess)內部遍歷每一個資料來源提供的字串,每個字串都需要經過一系列鏈式處理常式處理後被重新放回切片,參見下面程式碼。

字串的鏈式處理:
// 字串處理常式, 傳入字串切片和處理鏈
func StringProccess(list []string, chain []func(string) string) {

    // 遍歷每一個字串
    for index, str := range list {

        // 第一個需要處理的字串
        result := str

        // 遍歷每一個處理鏈
        for _, proc := range chain {

            // 輸入一個字串進行處理, 返回資料作為下一個處理鏈的輸入
            result = proc(result)
        }

        // 將結果放回切片
        list[index] = result
    }
}
程式碼說明如下:
  • 第 2 行,傳入字串切片 list 作為資料來源,一系列的處理常式作為 chain 處理鏈。
  • 第 5 行,遍歷字串切片的每個字串,依次對每個字串進行處理。
  • 第 8 行,將當前字串儲存到 result 變數中,作為第一個處理常式的引數。
  • 第 11 行,遍歷每一個處理常式,將字串按順序經過這些處理常式處理。
  • 第 14 行,result 變數即是每個處理常式的輸入變數,處理後的變數又會重新儲存到 result 變數中。
  • 第 18 行,將處理完的字串儲存回切片中。

2) 自定義的處理常式

處理常式可以是系統提供的處理常式,如將字串變大寫或小寫,也可以使用自定義函數,本例中的字串處理的邏輯是使用一個自定義的函數實現移除指定 go 字首的過程,參見下面程式碼:
// 自定義的移除字首的處理常式
func removePrefix(str string) string {
    return strings.TrimPrefix(str, "go")
}
此函數使用了 strings.TrimPrefix() 函數實現移除字串的指定字首,處理後,移除字首的字串結果將通過 removePrefix() 函數的返回值返回。

3) 字串處理主流程

字串處理的主流程包含以下幾個步驟:
  1. 準備要處理的字串列表。
  2. 準備字串處理鏈。
  3. 處理字串列表。
  4. 列印輸出後的字串列表。

詳細流程參考下面的程式碼:
func main() {

    // 待處理的字串列表
    list := []string{
        "go scanner",
        "go parser",
        "go compiler",
        "go printer",
        "go formater",
    }

    // 處理常式鏈
    chain := []func(string) string{
        removePrefix,
        strings.TrimSpace,
        strings.ToUpper,
    }

    // 處理字串
    StringProccess(list, chain)

    // 輸出處理好的字串
    for _, str := range list {
        fmt.Println(str)
    }
}
程式碼說明如下:
  • 第 4 行,定義字串切片,字串包含 go 字首及空格。
  • 第 13 行,準備處理每個字串的處理鏈,處理的順序與函數在切片中的位置一致,removePrefix() 為自定義的函數,功能是移除 go 字首,移除字首的字串左邊有一個空格,使用 strings.TrimSpace 移除,這個函數的定義剛好符合處理常式的格式 func(string)string,strings.ToUpper 用於將字串轉為大寫。
  • 第 20 行,傳入字串切片和字串處理鏈,通過 StringProcess() 函數對字串進行處理。
  • 第 23 行,遍歷字串切片的每一個字串,列印處理好的字串結果。

提示

鏈式處理器是一種常見的程式設計設計,Netty 是使用 Java 語言編寫的一款非同步事件驅動的網路應用程式框架,支援快速開發可維護的高效能的面向協定的伺服器和用戶端,Netty 中就有類似的鏈式處理器的設計。

Netty 可以使用類似的處理鏈對封包進行收發編碼及處理,Netty 的開發者可以分為 3 種:第一種是 Netty 底層開發者;第二種是每個處理環節的開發者;第三種是業務實現者。在實際開發環節中,後兩種開發者往往是同一批開發者,鏈式處理的開發思想將資料和操作拆分、解耦,讓開發者可以根據自己的技術優勢和需求,進行系統開發,同時將自己的開發成果共用給其他的開發者。