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
}

これで、これらの呼び出しをfluently chainできます。

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

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

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

challenge icon

チャレンジ

簡単

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

コードを2つのファイルに整理します。

  • element.go:チェーン可能なメソッドを備えたHTML Element builderをDefineします。

    タグ名、属性のmap(stringからstring)、およびinner contentを格納するElement structをCreateします。それぞれが*Elementを返す、次のチェーン可能なメソッドをBuildします。

    • NewElement(tag string) - 指定されたタグ名とinitialized(empty)の属性mapを持つ新しいElementをcreatesするconstructor
    • Attr(key, value string) - elementに属性をaddsする
    • ID(id string) - "id" attributeを設定するconvenience method
    • Class(class string) - "class" attributeを設定するconvenience method
    • Content(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 value
    • classに続くclass value
    • attrに続く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())
}
quiz icon腕試し

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

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

自分で練習してみよう: Goオンラインコンパイラ