メソッドチェーン
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 81/107。
メソッドチェーンとは、メソッドがレシーバーを返すことで、1つの文の中で複数のメソッド呼び出しを連結できるようにする技法です。すでにBuilderパターンでこれを見たことがありますが、これは多くの場面で役立つ独立した概念です。
重要なのは、各メソッドから構造体へのポインターを返すことです。
type Query struct {
table string
columns []string
where string
limit int
}
func (q *Query) Select(cols ...string) *Query {
q.columns = cols
return q
}
func (q *Query) From(table string) *Query {
q.table = table
return q
}
func (q *Query) Where(condition string) *Query {
q.where = condition
return q
}
func (q *Query) Limit(n int) *Query {
q.limit = n
return q
}これで、これらの呼び出しをfluently chainできます。
query := &Query{}
query.Select("name", "email").From("users").Where("active = true").Limit(10)これは、個別の文と比べると、ほとんど自然言語のように読めます。メソッドチェーンは、他の値を返す必要なく、操作によってオブジェクトを設定または変更する場合に最も効果を発揮します。これは、クエリビルダー、設定API、テストアサーションライブラリでよく使用されます。
各メソッドはポインタレシーバーを使用し、チェーンが正しく機能するように *StructType を返す必要があることを覚えておいてください。
チャレンジ
簡単メソッドチェーンを使用して属性とコンテンツを持つHTMLタグを構築する、流暢なHTML Element builderを作成しましょう!これはメソッドチェーンの実践的な応用です。HTML要素を構築する際に、ほぼ自然言語のように読めるAPIを作成します。
コードを2つのファイルに整理します。
element.go:チェーン可能なメソッドを備えたHTML Element builderをDefineします。タグ名、属性のmap(stringからstring)、およびinner contentを格納する
ElementstructをCreateします。それぞれが*Elementを返す、次のチェーン可能なメソッドをBuildします。NewElement(tag string)- 指定されたタグ名とinitialized(empty)の属性mapを持つ新しいElementをcreatesするconstructorAttr(key, value string)- elementに属性をaddsするID(id string)- "id" attributeを設定するconvenience methodClass(class string)- "class" attributeを設定するconvenience methodContent(text string)- elementのinner contentを設定する
HTML stringを返す
Render()methodをAddします。属性はkeyのalphabetical orderでappearする必要があります。Formatは次のとおりです。<tag attr1="value1" attr2="value2">content</tag>属性がない場合は、
<tag>content</tag>をそのままrenderします。contentがない場合は、<tag attr="value"></tag>をrenderします。main.go:メソッドチェーンを使用してHTML elementsをBuildします。タグ名を読み取り、続いて実行するoperationsのcountを読み取ります。各operationは次のいずれかです。
idに続くID valueclassに続くclass valueattrに続くattribute nameとvalue(2行)contentに続くcontent text
すべてのoperationsをfluently chainし、その後renderされたHTMLをprintします。
次の入力が提供されます。
- 1行目:Tag name
- 2行目:Operationsの数
- それ以降の行:Operation typeとvalue(s)
たとえば、次の入力givenの場合:
div
3
id
container
class
main-content
content
Hello World出力は次のようになります。
<div class="main-content" id="container">Hello World</div>また、次の入力givenの場合:
a
3
attr
href
https://example.com
attr
target
_blank
content
Click here出力は次のようになります。
<a href="https://example.com" target="_blank">Click here</a>また、次の入力givenの場合:
input
2
attr
type
text
attr
placeholder
Enter name出力は次のようになります。
<input placeholder="Enter name" type="text"></input>また、次の入力givenの場合:
p
1
content
Simple paragraph出力は次のようになります。
<p>Simple paragraph</p>メソッドチェーンによって、読みやすい形でelementをstep by stepにBuildできることに注目してください。各methodはelementを変更してそれを返すため、次のmethod callを直接chainできます。
自分で試してみよう
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// タグ名を読み取る
scanner.Scan()
tagName := scanner.Text()
// 操作の数を読み取る
scanner.Scan()
numOps, _ := strconv.Atoi(scanner.Text())
// 要素を作成する
element := NewElement(tagName)
// 各操作を処理する
for i := 0; i < numOps; i++ {
scanner.Scan()
operation := scanner.Text()
// TODO: 操作の種類に基づいて、適切なメソッドをチェーンする
// 操作は次のいずれか: "id", "class", "attr", または "content"
// - "id" の場合: 次の行を ID 値として読み取る
// - "class" の場合: 次の行をクラス値として読み取る
// - "attr" の場合: 次の2行を読み取る(属性名、次に値)
// - "content" の場合: 次の行をコンテンツテキストとして読み取る
// メソッドチェーンを使用して要素を流暢に構築する
_ = operation // 実装時にこの行を削除する
}
// レンダリングされたHTMLを出力する
fmt.Println(element.Render())
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー自分で練習してみよう: Goオンラインコンパイラ