HayatoOkamura0000
@HayatoOkamura0000

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

【Go初心者】関数とメソッドの使い分けについての質問

解決したいこと

Goにおける関数とメソッドの理解を深めて、それぞれを正しい場面で適切に使用できるようになりたいです。

疑問点

簡単なTodoアプリを作成しています。
ユーザー情報を作成するときメソッドを使用し、取得する時は関数を使用していますが、使い分けている理由は下記の解釈であっておりますか?

ユーザー作成

func (u *User) CreateUser() (err error) {
	cmd := `insert into users (
		uuid,
		name,
		email,
		password,
		create_at
		) values (?, ?, ?, ?, ?)`

		_, err = Db.Exec(cmd, createUUID(), u.Name, u.Email, Encrypt(u.PassWord), time.Now())

		if err != nil {
			log.Fatalln(err)
		}
		return err
}

↑ 作成の際は構造体UserのNameとEmailの値が必要なので、メソッドにすることで効率的に値を取得できる(u.Name, u.Emailの部分)

ユーザー取得

func GetUser(id int) (user User, err error) {
	user = User{}
	cmd := `select id, uuid, name, email, password, create_at from users where id = ?`
	err = Db.QueryRow(cmd, id).Scan(
		&user.ID,
		&user.UUID,
		&user.Name,
		&user.Email,
		&user.PassWord,
		&user.CreateAt,
	)

	return user, err
}

↑ 取得の際はUserの値が必要ではないので関数で良い

結論

構造体の値が必要なときはメソッド、不要な時は関数

0

2Answer

プログラミングの基本についての質問なのかなと、思ったので回答してみます。Go言語についての突っ込んだ回答を期待されているのでしたら申し訳ございません。

ひとつの考え方として「オブジェクトの役割としてその処理をもたせるべきかどうか」と考えるので、Goの構造体もそれに当てはまるのではないかと思います。
(少なくとも「構造体の値が必要かどうか」という観点だと「複数の構造体の値が必要なときはどうする?」ということにもなるので、別の観点が必要になりそうです)

記載のコードの例をとっても、オブジェクトの情報とそのデータベース操作をどのように設計するかについて様々な考え方があります。
例えば、オブジェクトに対応するテーブルの操作も持たせるActiveRecordパターンや、その役割を分離するDataMapperパターン、さらにデータの取得と更新を分けるCQRS。
これらは、プロジェクトやシステム対象の性質などさまざまな要素を含めて考えるので、唯一の正解はありません。

まとめですが、「メソッドにするか関数にするか」については「オブジェクト(構造体)の役割や処理の目的を考えて決める」という回答で、どちらが良いという唯一の正解はありません。
様々な設計手法や考え方を学び、実際の状況に合わせて検討する部分ですね。

1Like

Comments

  1. ご回答いただき誠にありがとうございます。
    普段フロントエンドの業務をしておりバックエンドに関する知識が浅かったので、基本となる考え方を教えていただき感謝しております。
    設計手法の話なのでどちらを使うかは人それぞれであり、ある種宗教的な部分になるということですね。
    学習を進めて適切な設計ができるよう精進いたします。

  2. 参考になったのであればよかったです。
    好みや感じ方は人それぞれですので宗教的な部分も無くはないと思いますが、基本はメリット/デメリットを理解して適切に使用することです。個人のプロジェクトであれば好みで選択してもいいですが、仕事の場合はそういうわけにもいきませんので。
    頑張ってください!

まず最初に「ユーザー情報」と「構造体User」が別のものだと気づかずに混乱しました。
そして「構造体UserのメソッドならCreateUserは何をCreateするの?」となりました。
メソッドか関数かより、こういった名称のあいまいさの方が深刻に思えます。
「データベース上のユーザー情報」であることが明確になるメソッド名や変数名を使わないと、コードを読んだり書いたりする人が混乱するので危険です。


本題ですが、あまり言及されることがないオブジェクト指向の基本に「インスタンス(Goの場合は構造体)は主語、メソッド名は動詞」というのがあります。
おそらく英語圏の人には自然なことすぎて説明が必要だと思わないのでしょう。

そう考えるとUser.CreateUserは「UserがUserを作成する」とへんてこな表現になっていることに気づけます。
だったらたとえばUser.CreateDbUserとするのも間違いとは言えないですが、主語がUserなのだからUserがDbに対してCreateをするならユーザー情報であろうことはわかります。
それならUser.CreateDbEntryなんかの方が重複性がなくやろうとしてることがわかりやすいのではないかなぁと。
同じ考え方でたとえばToDo構造体の内容をDbに登録するときもToDo.CreateDbEntryとなるので、一貫性が出てわかりやすくかっこいいですよね。


一方の「ユーザー取得」ですが、コードを見ればやっていることは「構造体Userの作成」です。
だからGoの流儀に従うならファクトリ関数として実装した方が自然でしょう。
「ひとつの整数値を引数とするファクトリ関数LoadUser(LoadUserByIdなどでもいいかも)は、データベースから引数に一致するidのユーザー情報を探し、それを元に構造体Userを作成する」
と仕様定義すれば、他の情報をデータベースから取り出す場合にも一貫したアプローチを適用しやすくなるのではないかと思います。

1Like

Comments

  1. ご回答いただき誠にありがとうございます。

    細部までご指摘いただきありがとうございます。
    Udemyの教材を参考に簡易的なTodoアプリの作成をしていたので名称まで気を配ることができませんでしたが、ご指摘いただいた通り意味の重複や曖昧な表現が目立つと感じました。

    この質問を機に今後は「主語、動詞」の関係などオブジェクト指向の基本を強く意識して学習したいと思います。

    詳細なご指摘をいただき重ねて感謝申し上げます。

Your answer might help someone💌