改めて、きっちりGoの基本的な書き方を学んでみよう。とはいえ、本でありがちな変数宣言やfunctionの定義なんて学んでも、正直実践にほとんど活かせない。なので、今回は実践的に見てみたい。
実際のコード進行では、頻繁にfunctionとmethodを作る。functionは特定の処理を持たせたもの。例えば、2つの数字を足し算する機能をfunctionとして作れば、後付けでユーザーが入力した数字を足し算することができる。一方、methodはオブジェクトにふるまいを持たせるためのもの。applicationという構造体を作り、これに付与される機能を追加するのがmethodで、オブジェクトを呼び出してmethodを実行することができる。
中々概念的で分かりずらいが、functionとmethodの違いは、何らかのオブジェクトにくっつけるか否か、という違いと見れば分かり易いかもしれない。分かり易い説明をしているブログがあったのでリンクを貼っておく。
頭の中で言語化しきれないな、と思ったときによくこの方のブログを見るが、分かり易い説明をしている記事がとても多いのでおススメ。ただ、functionとmethodの違いについては中々説明に難儀しているように感じた。
具体的に見てみよう。
functionとは何か?
以下のadd関数はfunctionである。
func add(a int, b int) int {
return a + b
}
関数(function)とは、「何かを入れると何かをやって何かを返してくれる」プログラムの部品のこと。この「何かを入れると」の部分を引数という。即ち、プログラムや関数に渡す値が引数。この例では(a,b int)が引数。「その結果として出てくる値」が戻り値。この例では、a=1, n=2を入れれば「3」が帰ってくる。この場合は3が戻り値、1,2が引数となる。戻り値の型は一行目で ) int {の部分でint型であることを明記している。一行目だけ見ても、aとbをfunction内で入力するんだな、その結果としてint型の戻り値が出力されるんだな、ということが分かる。
さて、ここまでは本でも出てくることが多いが、一歩ステップアップして実際の関数を見てみよう。以下のopenDB関数はデータベースに接続するためのコードの一行目である。
func openDB(dsn string) (*sql.DB, error) {
dsn(data source name)は操作したい対象のデータベース名。*sql.DBはSQLデータベースへの接続を示している。さて、ここから何を読み取れるだろうか?
func openDB //openDB functionを作成する
(dsn string) //操作対象となるデータベースを関数内で入力(文字列型)すると、
(*sql.DB, error) //SQLデータベースに接続するか、error型の結果を出す。
openDB関数はその名の通りデータベースに接続するための関数である。では、データベースに接続するためにはどんな手順が必要だろうか。まずどのデータベースに接続するかを知らせる必要があるだろう。最終的な結果はデータベースに接続した姿になっていてほしい。となれば、データベース情報は引数で、データベースに接続した姿が戻り値になるはずだ。データベースへの接続情報を入れて、openDBの中の処理を行うと、その結果としてSQLデータベースに接続できる。
methodとは何か?
methodはfunctionのように単体では機能しない。必ず、構造体にふるまいとして付与される。
type Person struct {
FirstName string
LastName string
}
尚、構造体(type 〇〇 struct)に含まれる変数はフィールドと呼ばれる。この場合、FirstNameフィールドとLastNameフィールドをPerson構造体は持っている。
そしてここからが本題。以下のFullNameはPerson構造体に付与したmethodである。functionとの見分け方は、func (p Person)のように関数名の前に()がついているか否かである。
func (p Person) FullName() string {
return p.FirstName + " " + p.LastName
}
このmethodの機能は単純で、FirstNameとLastNameを入力したらFullNameが出力されますよ、というメソッドだ。Go言語では、funcの後に書かれた()内にレシーバーを指定することで、そのメソッドがどの型に関連付けられたものか分かる。(p Person)がレシーバー。Person構造体に関するメソッドであり、pが付けられた変数がPerson構造体のフィールドであることを示している。つまり正確に言えば、FullNameメソッドはPerson構造体のFirstNameフィールドとLastNameフィールドを結合したFullNameを返す。
さて、これでPerson構造体のふるまいが定義できた。なのでPerson構造体を呼び出してFirstNameとLastNameを入力し、FullNameメソッドを呼び出せば、結合されてフルネームを取得できる。
p := Person{
FirstName: "John",
LastName: "Doe",
}
fmt.Println(p.FullName())
これがオブジェクト指向というものである。メソッドはオブジェクトの操作を定義するものであり、Person構造体に付与された機能としてFullName methodが存在する。
少しステップアップして実際のメソッドを見てみよう。以下のapplication構造体は、DSNフィールド(データベース接続情報)を持っている。
type application struct {
DSN string
}
このapplication構造体を使ってconnectToDBメソッドを作る。
func (app *application) connectToDB() (*sql.DB, error) {
connection, err := openDB(app.DSN)
return connection, nil
}
(app *application)がレシーバーであり、レシーバーを指定することで上記のapplication構造体と関連するものだとプログラムが認識する。また、メソッド内で「app.の付く変数(app.DSN)はapplication構造体のフィールドである」ことを示している。ConnectToDBメソッドは引数を持たず、*sql.DBを戻り値で返す。メソッド内の処理は固定されており、外部からの入力を必要としない。つまり、何も入力されずにconnectToDBの処理を行うと、その結果としてSQLデータベースに接続できる。引数なし、戻り値ありのメソッドは、特定の処理を行い、その結果を他の部分で利用するために使用される。ConnectToDBでは、openDB関数を使用して、appのDSNフィールドに指定したSQLのDBに接続される。大切なのは、これによってapplicatioin構造体にconnectToDBのふるまいが定義された、ということ。
var app application
var app applicationと定義されているので、application構造体はappで呼び出せばよくて、つまりapp変数にconnectToDBのふるまいが定義されたと言える。
conn, err := app.connectToDB()
このようにappを呼び出すことで、SQLデータベースに接続することができる。
引数あり、戻り値なしのmethodとは?
上述の具体例では引数なし、戻り値ありのmethodについて紹介してきた。メソッド内の処理は固定されており、外部からの入力を必要としない。特定の処理を行い、その結果を他の部分で利用するために使用される。
一方で、methodでは以下のような使い方もよくする。引数に(w http.ResponseWriter, r *http.Request)を設定する使い方である。これはルーティングとセットで使う。
func (app *application) Allpractices(w http.ResponseWriter, r *http.Request) {
practices, err := app.DB.Allpractices()
_ = app.writeJSON(w, http.StatusOK, practices)
}
一行目から、このmethodはapplication構造体のふるまいを規定するものであり、method内ではapp.をつけてapplication構造体のdsnフィールドに値を設定する。引数が(w http.ResponseWriter, r *http.Request)の2つであり、つまり、wとrをmethod内で入力するんだな、ということが分かる。http.ResponseWriterとhttp.Requestについては文末で解説する。
このmethodでは戻り値が設定されていない。即ち、与えられた引数に基づいて特定の処理を行われるが、その結果を戻り値として返さない。メソッド内で行われる処理は、引数に渡されたデータに基づいて実行され、通常は外部の状態を変更するために使用される。今回は、この関数がデータベースの閲覧のみで状態の更新が無いから、戻り値は必要ないので設定しない。
すなわち、このmethodは、HTTPリクエストの内容を処理解析し、HTTPレスポンスを生成して書き込む。その結果としての戻り値は出力しない。この場合は、データベースからの情報取得がmethodとしての機能であり、この機能をapplication構造体に付与している。
http.ResponseWriterとhttp.Requestって何?
webブラウザはwebサーバーに対して何らかの操作を行う。例えば、特定のURLへのアクセスやファイルのダウンロードがある。この要求がHTTPリクエストと表現されてwebサーバーへ送られる。webサーバーはこのHTTPリクエストをhttp.Requestで情報を取得して処理する。特定のページの表示やデータベースからの情報取得などが”処理”に含まれる。このHTTPリクエストに対する処理結果に基づいて、webブラウザにレスポンスを返すのがhttp.ResponseWriter。レスポンス内容はHTMLページやJSONデータなどがある。このプロセスを通じて、webブラウザは動いている。
Goでは、net/httpにhttp.ResponseWriterとhttp.Requestが標準で搭載されている。http.RequestでHTTPリクエストの情報(HTTPメソッドやボディ等)を解析する。その後、http.ResponseWriterでHTTPサーバーを作成。HTTPサーバーはHTTPリクエストを受信・処理し、webブラウザに対してHTTPレスポンスを返す。HTTPリクエストに対して生成されるHTTPレスポンスのデータを書き込む。
コメント