Go言語 ~基礎編~ EncapsulationとEmbedding

go

今回は、EncapsulationとEmbeddingを学びましょう。

ここでは、EncapsulationはStructのカプセル化、Embeddingは、Structの拡張を指しています。

学習記事まとめ

Structの作成

Structについて少し復習しておきましょう。

Structは、Go言語の構造体の一つで、様々なtypeのデータをまとめて管理することができます。

KOUKI
KOUKI

私の感覚だとクラスみたいなものです。

サンプルコードを下記に記載します。

DateのStructを実装してみました。

実行してみます。

Setterメソッド

先ほどの例では、Date Structに設定したパラメータ(Year, Month, Day)に対して、正しい値を入力しました。

しかし、実際には存在しえない値(無効な値)を入力したらどうなるのでしょうか?

Date Structに無効な値を入力して実行しましたが、普通に処理されてしまいましたね。しかし、これでは困るわけです。

アプリ開発の観点では、Structに対してユーザーが自由に値をセットすることは望ましくありません。

Structに値を設定する時は、Setterメソッドを定義することが定石です。

サンプルコードを以下に記載します。

SetYearメソッドを実装しました。これを実行したらどうなるでしょうか?

出力結果が、「0」になりましたね。Go言語はデフォルトでは値渡しの言語であるため、引数に渡された値(2019)は、オリジナルからコピーしたものが渡されます。

これを解決するには、メソッドのパラメータをPointer Receiverにする必要があります。

変更点は1箇所だけで、(d Date)に「*」を付与しました。

実行してみます。

値をセットできましたね。

同じようにSetMonth, SetDayを作ってみましょう。

簡単ですね。しかし、これではまだ無効なデータを入力できてしまいます。

バリデーションチェック

不正の値を検出するためにSetterメソッドにバリデーションチェックを追加しましょう。

バリデーションチェックとは、インプットされた値に対してその値が有効な入力値であるか検査することです。

KOUKI
KOUKI

アプリ開発において、バリデーションチェックはよく実装します。例えば、フォームの入力文字数チェックなどもバリデーションチェックですね。

では、最初にSetYearに対してバリデーションチェックを追加します。

Yearのバリデーション範囲は、1 ~ 2100の範囲にしました。この範囲外の場合は、errorを返すように実装しています。

同様にSetMonth, SetDayにもバリデーションを追加してみましょう。

Dayは、月ごとに最大日数が違いますが、ここでは簡単のために1~31までの数値をバリデーションチェックしています。

しかし、これらのSetterメソッドはまだ”甘い“です。

なぜなら、値を直接書き換えることが可能だからです。

入力情報は、全てSetterメソッドを経由させる必要があります。

KOUKI
KOUKI

どうすれば、直接値を書き換えずに済むか考えてみてください。

パッケージの分割

これまでは、mainパッケージ内に処理を書いていましたが、Date StructとSetterメソッドを別パッケージに分割しましょう。

そして、Structを外部非公開(unexport)状態にして、Setterメソッドを外部公開(export)状態にします。

このようにすれば、先ほどの問題を解決することができます。

最初に次の構成になるよう、WorkSpaceを作成してください。

date.goには、Date StructとSetterメソッドをコピーします。

次に、Date Structを外部から直接アクセスできないように変更します。

変更は簡単で、Struct の各フィールドを全て小文字にするだけです

Go言語では、外部ファイルにフィールドや変数を定義したとき、先頭の文字が小文字の場合は、外部から読み取ることができなくなります。Javaで例えるとprivateを設定したようなものです。

プログラムを実行してみましょう。

OKですね。これでDate Structのパラメータを変更したい場合は、Setterメソッドを通じて変更するしかなくなりました。

このように、プライベート用の変数、関数、メソッドは、同一パッケージ内の公開用関数やメソッドを通じてアクセスします。

Getterメソッド

プライベートに設定した変数は外部ファイルから読み取れないようにできました。

しかし、Date Structのフィールドをプライベートに設定してしまうと外部から値を取得することができなくなります。

このような場合は、Getterメソッドを定義します

Setterメソッドが、定義したStructのフィールドに値を設定するメソッドであるなら、GetterメソッドはStructのフィールドから値を取得するメソッドです。

サンプルコードを以下に記載します。

Go言語の慣習的に、Getterメソッド名とStructのフィールド名は一致させた方が良いらしいです。例えば、GetterメソッドのYearとDate Structのyearですね。

もちろんGetterメソッド名の先頭は大文字にしてください。外部参照ができなくなるので。

あとは、GetterメソッドのReceiverをPointer Receiverで宣言してください。

これで、Getterメソッドの追加は完了です。main関数を次のように書き換えて、プログラムを実行してみましょう。

Go言語のカプセル化(Encapsulation)について

外部からデータを隠蔽するプログラム手法の一つにカプセル化(Encapsulation)があります。

カプセル化は、Go言語特有のものではなく、プログラミングの一般的な概念であり、不正な値から大切なデータを守るプロテクターとして、とても有効です。

他のプログラミング言語では、クラス単位でデータをカプセル化しますが、Go言語にはクラスが存在しないので、パッケージ単位でカプセル化を行います。

また、他のプログラミング言語では、定義されたデータに対して必ずSetterを設定す べきとする言語もありますが、Go言語に関しては必ずしも必要ではありません。バリデーションチェックなど必要に応じてカプセル化を行えば、OKです。
※外部ファイルへのexportを許可してOK

Embedding

Embeddingとは、「拡張する」ことを指します。Structに宣言したフィールドを拡張するときによく使われますね。

例えば、次のサンプルコードのことを指します。

Address Structのフィールドを他のStructのフィールドに定義することで、フィールドの拡張をしています。クラスの継承に少し似ていますね。

さて、calendarフォルダ配下に、event.goファイルを作成してください。

このファイルには、Event StructにDate StructをEmbeddingしたプログラムを実装します。

これを一旦、main関数から呼び出してみましょう。恐らくエラーになります。

予想通りエラーになりましたね。Date Structのmonthは外部ファイルへ非公開になっている為、エラーになります。

外部公開用のメソッドはStructのフィールドにもなり得る!?

先ほどは、Event StructにDate StructをEmbeddingしたプログラムの例を見せました。

しかし、いざプログラムを実行したところ、Date Structのフィールドがunexport状態だった為、コンパルエラーになってしまいましたね。

この状態を回避するには、外部公開用のメソッドを定義すればOKです。

Go言語の面白いところは、この外部公開用のメソッドをStructのフィールドとして宣言できてしまう点です。

サンプルコードをみてください。

EmbeddedTypeフィールドが今回アクセスしたいフィールドですね。

定義したEmbeddedTypeをMyType Structにフィールドとしてセットします。そして、EmbeddedTypeをメソッドとして定義したExportedMethod関数を用意しました。

これをどのように使うかですが、下記のmain関数を見てください。

こんな感じで使います。

MyType Structは、EmbeddedTypeフィールドを持っているため、EmbeddedTypeのメソッドも使用可能になるわけですね。

KOUKI
KOUKI

ややこしいですよね?わかります。

非公開にしておいたメソッド(unexportedMethod)については、使用することはできません。

先ほどエラーになったEvent Structもこれと同じことをすれば良いのです。

簡単ですね^^

やってみよう

Event StructからDate Structのフィールド値を出力することができました。しかし、Event StructのTitleの値を直接書き換えることができてしまう問題が残っています。

このEventのTitleフィールドをカプセル化してみましょう。

要件を書いておきますね。

・ Title値の書き換えやアクセスはSetter/Getterメソッドから行うこと
・ 最大文字数を30文字以内とするバリデーションチェックを入れること

では、event.goファイルを書き換えてみましょう。

utf8パッケージのRuneCountInString関数を使えば、文字数チェックは簡単にできます。

main関数から呼び出してみましょう。

これで、Event Structからは、Titleフィールド + Date Structのフィールドの書き換えや取得が可能になりました。

Embeddingは、奥が深そうですね。

まとめ

KOUKI
KOUKI

最後にこの章で学んだことをまとめておきましょう。

まとめ
・ Go言語では、クラスがない為、パッケージ単位でカプセル化を行う
・ Structフィールドを外部非公開にすることで、データの保護を行う
・ 外部公開用の関数やメソッドにて、非公開フィールド/メソッド/関数にアクセスする
・ フィールド等に値をセットするときは、バリデーションチェックを入れる
・ 外部非公開フィールドに値をセットするメソッドをSetter Methodと呼ぶ
・ Setter Methodでは、Pointer Receiverを使う
・ Setter Method名はSetXと定義し、Xは値のセット先のフィールド名とする
・ 外部非公開フィールドの値を取得するメソッドをGetter Methodと呼ぶ
・ Getter Method名はXと定義し、Xは値の取得先のフィールド名とする
・ 定義したメソッドは、他のStrcutのフィールドにEmbeddingできる

次回

次回は、Interfaceを学びましょう!

参考書籍

コメントを残す