Menu
Coddy logo textTech

メソッドチェーン

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
}

これで、これらの呼び出しを流暢に連結させることができます:

query := &Query{}
query.Select("name", "email").From("users").Where("active = true").Limit(10)

これは、個別のステートメントと比較して、ほとんど自然言語のように読めます。メソッドチェーンは、他の値を返す必要がなく、操作がオブジェクトの設定や変更を行う場合に最も効果的です。クエリビルダー、設定API、テストアサーションライブラリなどでよく使用されます。

チェーンが正しく機能するためには、各メソッドがポインタレシーバを使用し、*StructType を返す必要があることに注意してください。

challenge icon

チャレンジ

簡単

メソッドチェーンを使用して、属性とコンテンツを持つHTMLタグを構築する、流暢なHTML要素ビルダーを作成しましょう!これはメソッドチェーンの実用的な応用例です。HTML要素を構築する際に、まるで自然言語のように読めるAPIを作成します。

コードは2つのファイルに分けて構成します:

  • element.go: チェーン可能なメソッドを持つHTML要素ビルダーを定義します。

    タグ名、属性のマップ(文字列から文字列)、および内部コンテンツを格納する Element 構造体を作成します。それぞれが *Element を返す、以下のチェーン可能なメソッドを構築してください:

    • NewElement(tag string) - 指定されたタグ名と初期化された(空の)属性マップを持つ新しい Element を作成するコンストラクタ
    • Attr(key, value string) - 要素に属性を追加する
    • ID(id string) - "id" 属性を設定する便利なメソッド
    • Class(class string) - "class" 属性を設定する便利なメソッド
    • Content(text string) - 要素の内部コンテンツを設定する

    HTML文字列を返す Render() メソッドを追加します。属性はキーのアルファベット順に表示される必要があります。形式は以下の通りです:

    <tag attr1="value1" attr2="value2">content</tag>

    属性がない場合は、単に <tag>content</tag> とレンダリングします。コンテンツがない場合は、 <tag attr="value"></tag> とレンダリングします。

  • main.go: メソッドチェーンを使用してHTML要素を構築します。

    タグ名を読み取り、次に実行する操作の数を読み取ります。各操作は以下のいずれかです:

    • id とそれに続く ID の値
    • class とそれに続くクラスの値
    • attr とそれに続く属性名および値(2行)
    • content とそれに続くコンテンツのテキスト

    すべての操作を流暢にチェーンさせ、レンダリングされたHTMLを出力します。

以下の入力が提供されます:

  • 1行目:タグ名
  • 2行目:操作の数
  • 続く行:操作のタイプと値

例えば、以下の場合:

div
3
id
container
class
main-content
content
Hello World

出力は以下のようになります:

<div class="main-content" id="container">Hello World</div>

また、以下の場合:

a
3
attr
href
https://example.com
attr
target
_blank
content
Click here

出力は以下のようになります:

<a href="https://example.com" target="_blank">Click here</a>

また、以下の場合:

input
2
attr
type
text
attr
placeholder
Enter name

出力は以下のようになります:

<input placeholder="Enter name" type="text"></input>

また、以下の場合:

p
1
content
Simple paragraph

出力は以下のようになります:

<p>Simple paragraph</p>

メソッドチェーンによって、読みやすい方法でステップバイステップで要素を構築できることに注目してください。各メソッドは要素を変更してそれを返すため、次のメソッド呼び出しを直接つなげることができます。

チートシート

メソッドチェーンは、メソッドがレシーバ(通常は *StructType)を返すことで、複数のメソッド呼び出しを単一のステートメントで連結できるようにする手法です。

各メソッドはポインタレシーバを使用し、*StructType を返す必要があります:

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
}

メソッド呼び出しを流暢に連結します:

query := &Query{}
query.Select("name", "email").From("users").Where("active = true").Limit(10)

メソッドチェーンは、他の値を返す必要がなく、オブジェクトの設定や変更を行う操作に最適です。クエリビルダー、設定API、テストアサーションライブラリなどで一般的によく使用されます。

自分で試してみよう

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())
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン