JavaプログラマがGo言語を触ってみた(Javaの継承をGo言語で実装してみる)

More than 1 year has passed since last update.

image

ども、keita69sawada です。

JavaプログラマがGo言語を触ってみた(とりあえず触ってみる編)に続きGo言語のお勉強。

Javaのクラス設計と同じようにUMLで設計したところ、Go言語では「継承(extends)」が無いので共通処理(抽象メソッド)をどうやって実現すべきかイメージできなかった。。。

じゃ、「簡単なJavaのクラス設計をGo言語で実装してみよう!」と

ウニウニしたのがこの記事です。


対象読者


  • Go言語のソースファイルの作成単位に悩んでいる人

  • Javaの継承(extends)を使った共通処理(抽象メソッド)をGo言語でどうやって実装するか悩んでいる人

  • Go言語の初心者


前提条件


  • Java言語の知識がある人


はじめに

 Javaの継承(extends)をつかった簡単なサンプルをGo言語に置き換えてみます。

「動物」を継承するクラス「イヌ」と「ネコ」に「鳴く」メソッドを実装してみます。


1. クラス図

 厳格なUMLではないですが、こんな感じでしょうか。

image


3. Go言語で同じものを実現しようとすると・・・


以下、悩みました(;´Д`)


  1. Go言語のソースファイルはどう分割すればいいの?(クラスの概念ないし…)


    • GitHubで競合しないようにソースファイルを細かく分けたい。

    • 動物の種類(サルとかキジとか)が増えたら、ソースファイルを増やす設計にしたい。



  2. Go言語では継承(extends)の概念がないので共通処理(抽象メソッド)をどうやって実現すればいいの?

これらの悩みを自分なりに考えてみました。


4. 【悩みその1】Go言語のソースファイルをどう分割すればいいの?(クラスの概念ないし…)

Javaのソースファイル構成とGo言語のソースファイル構成を比較して考えてみました。


4-1. Javaのソースファイル構成

Javaプログラマが上記のクラス図を見てソースファイル構成を決めると、大抵1クラス1ファイルにすると思います。


ファイル構成

└─src

├─animal // javaのパッケージ単位フォルダ
│ Animal.java // javaのクラス単位
│ Cat.java
│ Dog.java

└─main
Main.java


4-2. Go言語のソースファイル構成

 Javaのソースファイル構成と同じ構成です。今回はあえて同じにしています。

あえての理由は、"「悩みその1」まとめ"で。


ファイル構成

└─src

├─animal // goのパッケージ単位フォルダ
│ Animal.go // goにはクラスの概念がない
│ Cat.go
│ Dog.go

└─main
Main.go

 Go言語にはクラスの概念がないので、3つ(Animal.go, Cat.go, Dog.go)のソースファイルを分割する必要はありません。(ただし、パッケージは同じでないとダメ)

 例えば、次のように1ファイルにまとめるても文法的にはOK。(あくまで文法的にはです。。。)


ファイル構成

└─src

├─animal
│ Animal_Cat_Dog.go // 1つのソースファイルにまとめても文法的にOK

└─main
Main.go


4-3. 「悩みその1」まとめ

「Go言語のソースファイル分割をどうすればいいの?(クラスの概念ないし…)」に対する自分なりの答えは以下となりました。


  • Go言語のソースファイルはGitHubで管理しやすい(競合しない細かい)単位にする。

  • 似て非なる機能(今回の例だと、動物の種類(サルとかキジとか))が増えることがあらかじめわかっていれば、その機能単位でソースファイルを作成する。

  • Go言語には継承がないので、パッケージフォルダに基底となるソースファイル(今回の例だと”Animal.go”)を1つ作成する。

※ ベストなやり方を模索しているので皆さんコメントお願いします。(^O^)/


5. 【悩みその2】 Go言語では継承(extends)の概念がないので共通処理(抽象メソッド)をどうやって実現すればいいの?


5-1. 共通処理(抽象メソッド)

まずはJavaのソースを見てみましょう。

動物は鳴く(bark)という振舞いを抽象メソッドで実装してます。


Animal.java

package animal;

public abstract class Animal {
public abstract void bark();
}


そして、Go言語のソース。

Go言語は、抽象メソッドはありませんので、代わりにインターフェースで鳴く(bark)という振舞いを実装しています。

「動物という抽象的な概念(クラスに代わるもの)はない」です。。

※ ファイル名のAnimalはどこにも影響ありません。


Animal.go

package animal

type Barker interface {
Bark(string)
}


あと、先頭が大文字になるとpublicスコープになるみたいです。


5-2. 具象オブジェクト(Cat)

こちらもJavaソースから。

Catクラスはネコの鳴き声(voice)を持ち、

ネコが鳴く(bark)振舞いを持っています。


Cat.java

package animal;

public class Cat extends Animal {
private String voice = "にゃーにゃー";

public void bark() {
System.out.println(this.voice);
}
}


次にGo言語。

 Go言語はクラスの概念がないので、ネコを構造体(struct)で定義し、ネコ型のデータ型(type)として宣言します。しかし、データ型はJavaクラスのように値("voice=にゃーにゃー")を持つことができません。

 ここで登場するのが、Javaコンストラクタ風のメソッドNewCat()です。このメソッドでデータ型(Cat)のインスタンスを作成する時に値(”にゃーにゃー”)を持たせます。

 そして、Animal.goで定義したインターフェースで宣言したBark()を実装します。ここでポイントとなるのが、レシーバーです。

 レシーバーとは「データ型に対してメソッド定義されたもの」です。ここではデータ型(type Cat struct)のメソッド(Bark())ですね。


Cat.go

package animal

import (
"fmt"
)

type Cat struct { // ネコを構造体(struct)で定義
voice string
}

func NewCat() *Cat { // Javaコンストラクタ風のメソッド
return &Cat{"にゃーにゃー"}
}

func (c Cat) Bark(){ // レシーバー
fmt.Println(c.voice)
}



5-3. 具象オブジェクト(Dog)

Cat と同じなので省略。


5-4. Mainオブジェクト

最後にMainです。こちらもJavaから。

Cat,Dogの具象オブジェクトのインスタンスを作成し、

インターフェース(Animal)で定義したメソッド(bark)を呼び出しています。


Main.java

package main;

import animal.Cat;
import animal.Dog;
import animal.Animal;

public class Main {
public static void main(String[] args) {
Animal c = new Cat();
Animal d = new Dog();

c.bark();
d.bark();
}
}


次にGo言語。

 具象オブジェクト(Cat や Dog)からインタフェース(Baker)に変換するところも、基本的にJavaと同じで分かりやすいですね。

 

```go:Main.go

package main

import (

"animal"

)

func main(){

var c animal.Barker = animal.NewCat()

var d animal.Barker = animal.NewDog()

c.Bark()

d.Bark()

}

```


まとめ

 同じ設計をGo言語とJava言語で実装してみることで、Java言語の設計(クラス設計)をGo言語に適用することはできそうです。ただし、実装するときには言語仕様の違いがあるので、Java言語のクラス設計をGo言語に適用するには、今回のように実装パターンを覚える必要がありそうです。

 Java言語は継承(extends)や実装(implements)によりクラス間に制約を与え、それぞれの役割を明確にすることができるので、大人数(大規模)で実装するのに適しているかもしれません。一方、Go言語は制約がゆるいのでオブジェクト間の依存度が低いため、頻繁に改修されるようなアプリケーションに向いていると感じました。


参考URL