Go言語 – 5.構造体

2019-01-29

Go言語 構造体

今回は「構造体 (structure) 」について説明します。
Go言語の構造体は、基本的にはC言語の構造体と同じですが、このほかにも「メソッド (method) 」の定義や構造体の「埋め込み」といった機能があります。
Go言語の場合、クラスや継承といったオブジェクト指向機能はサポートされていません。
メソッドや埋め込み、インターフェースを使うと、Go 言語でもオブジェクト指向プログラミングが可能になります。

構造体とは

Go言語のデータ構造をざっくり分類すると、
「基本型」「配列型」「関数型」「参照型」「ポインタ型」
といったものに分けられます。
「構造体」は、こうした多様なデータ構造を1つの「型」として取り扱うことができるものです。
=> 「複数の型の値を1つにまとめられるもの」
ということです。
Go言語で何かを作ろうとしたとき、
この構造体と関数を色々と使って、開発することが多くなるのではないかと思います。

構造体の定義

構造体は下記のように type と struct を使用して定義します。

<例>

構造体の初期化

構造体の初期化方法は複数存在します。

  • 変数定義後にフィールドを設定する方法
  • {} で順番にフィールドの値を渡す方法
  • フィールド名を  で指定する方法
  • コンストラクタ関数を使用した方法

①変数定義後にフィールドを設定する方法

②{}で順番にフィールドの値を渡す方法

③フィールド名をで指定する方法

④コンストラクタ関数を使用した方法

構造体の実装パターン

  • エクスポートによるアクセス許可
  • インターフェースによるポリモフィズム
  • 構造体によるポリモフィズム
  • 構造体によるサブクラス・レスポンシビリティ
  • 構造体による移譲
  • 関数による移譲

①エクスポートによるアクセス許可

Go言語ではパッケージ外へのアクセス許可は、名前の先頭を大文字にすることで行います(エクスポート)。
これを利用することでシングルトンのようなインスタンス生成の制御を行うことが可能です。

省略書式:=を使うことで構造体自体をエクスポートしなくても利用できます。

②インターフェースによるポリモフィズム

Go言語には型の継承機能が用意されていませんが、インターフェースを用いることでポリモフィズムを実現できます。

構造体の埋込により擬似的な継承ができると説明しているものがありますが、
これはあくまで透過的に構造体を利用できるだけで、”型”としてポリモフィズムを
実現できるわけではないことに注意して下さい。

③構造体によるポリモフィズム

ポリモフィズムを実現しつつ、構造体に共通の実装やメンバを定義したい場合があります。
この場合は、インターフェースを実装した構造体を用意して、それを埋め込む方法を取ります。

ただし、この場合、利用側が初期化時に埋込構造体を意識する必要があるため、
前述のコンストラクタ関数を用意するとよいでしょう。

④構造体によるサブクラス・レスポンシビリティ

そのため、TemplateMethodパターンのように子クラスに処理の実装を任せる機能をつくる場合、埋め込まれた構造体のメソッドに対してレシーバとなる、もとの構造体の参照を渡す必要があります。

このレシーバを自ら指定する方法は、継承を利用できない言語でClient-specified selfというパターンという名前で使われているようです。
ただし、構造体間の依存関係が高くなるため、どうしても継承関係が必要な場合を除き、後述の構造体による移譲、または関数による移譲を検討するほうがよいでしょう。

⑤構造体による移譲

Go言語では、処理の実装を適切な責務を持つ構造体に任せる移譲は簡単に実現できます。
それには構造体をメンバとして保持し、必要に応じて、その構造体のメソッドを呼ぶようにします。

また、Strategyパターンのようにインターフェースに対して移譲するような設計にしておくと依存性をより下げることができるでしょう。

⑥関数による移譲

Go言語では、関数をファーストクラスの型として扱えるため、移譲する処理を関数として渡す手法もとることができます。 Go言語の基本パッケージを見ていると、移譲処理を外部から注入する機能を提供する場合、関数型を渡すような設計になっているのが多いように思われます。