go言語でオブジェクト指向っぽく書いてみた


## はじめに
この記事は，[SLP KBIT Advent Calendar 2018](https://qiita.com/advent-calendar/2018/slp_kbit) の12日目の記事です．

先日，クラスについて学習する機会がありました．クラスのほんの一部しか触っていませんが，とても便利なものだと気づかされたので，Go言語でも実践したいと思いました．しかし，Go言語にはそもそもクラスというものが存在しません．そこで，実際に作ってみることにしました．クラスやオブジェクト指向について完璧に理解できたかと言われたらそうではないので，間違い等あればご指摘ください．

Go言語を使う理由は，特にありません．ただ使いたかっただけです．

## 開発環境
OS: Windows 10
言語: go 1.11

## クラスとは
構造体に手足が生えて己の意思で動けるようになったものです．(#あくまでイメージ)

## 作ったもの
何かを作りながらやりたいので，簡単なゲームを作りながらやってみます．作るものはじゃんけんです．自分がじゃんけんをするのはめんどくさいので，じゃんけんをするユニットを2つ作ります．10回勝負して，勝った回数を表示するようなゲームを作ろうと思います．0をグー，1をチョキ，2をパーとして作っていきます．

### 構造体の定義

```Go:unit
type unit struct {
	name     string
	stone    int
	scissors int
	paper    int
	janken   int
	winCount int
}
```
ユニットの構造体を定義します．プロパティとしては，ユニットの名前と，グーとチョキとパーをどの割合で出すかのパラメータと，現在出している手と，勝った回数を用意しておきます．
### 初期化時に必要なメソッド
```Go:newUnit
func (unit *unit) newUnit(name string, stone, scissors, paper int) {
	unit.name = name
	unit.stone = stone
	unit.scissors = scissors
	unit.paper = paper
	unit.janken = -1
	unit.winCount = 0
}
```
newUnitは，Javaでいうコンストラクタです．新しいユニットを作るときに使います．
### その他のメソッド群

```Go:choiseJanken
func (unit *unit) choiseJanken() {
	time.Sleep(1 * time.Nanosecond)
	rand.Seed(time.Now().UnixNano())
	randomNumber := rand.Intn(unit.stone + unit.scissors + unit.paper)
	switch {
	case randomNumber < unit.stone:
		unit.janken = 0
	case randomNumber < unit.stone+unit.scissors:
		unit.janken = 1
	default:
		unit.janken = 2
	}
}
```
choiseJankenは，グーとチョキとパーをどの割合で出すかによって手を変えるようにしました．こうすることで，ユニット毎に違った個体を作ることができます．乱数を生成する前に1ナノ秒スリープしているのは，短期間で複数の乱数を生成すると同じ値になってしまうからです．少し時間をずらすことによって乱数が正しく生成されるようになりました．

```Go:incrementWinCount
func (unit *unit) incrementWinCount() {
	unit.winCount++
}

```


```Go:getName
func (unit unit) getName() string {
	return unit.name
}
```

```Go:getWinCount
func (unit unit) getWinCount() int {
	return unit.winCount
}
```

```Go:getJanken
func (unit unit) getJanken() int {
	return unit.janken
}
```

```Go:getJankenString
func (unit unit) getJankenString() string {
	switch unit.getJanken() {
	case 0:
		return "グー"
	case 1:
		return "チョキ"
	}
	return "パー"
}
```

incrementWinCountは勝利をしたときにカウントするメソッド，getNameはunitの名前を返却するメソッド，getWinCountは勝利数を返却するメソッド，getJankenはじゃんけんで出した手の数字を返却するメソッド，getJnakenStringはじゃんけんで出した手の数字に対応する文字列を返却するメソッドです．ここで注意したいのは，getがついているメソッドには，レシーバとしてunit型の変数を値渡しで渡しているのに対して，getがついていないメソッドは参照渡しで渡されているということです．つまり，メソッドによって値を書き換える権利があるものとないものに分けられているということを表しています．これは，プライベートとパブリックに近づけたかったからです．

### main関数
```Go:main
func main() {

	var unit1, unit2 unit
	unit1.newUnit("taro", 5, 3, 1)
	unit2.newUnit("jiro", 1, 1, 2)

	for i := 0; i < 10; i++ {
		unit1.choiseJanken()
		unit2.choiseJanken()
		fmt.Println(unit1.getName(), ": ", unit1.getJankenString())
		fmt.Println(unit2.getName(), ": ", unit2.getJankenString())
		switch unit1.getJanken() {
		case unit2.getJanken():
			fmt.Println("Draw")
		case (unit2.getJanken() + 1) % 3:
			fmt.Println("Winner: ", unit2.getName())
			unit2.incrementWinCount()
		default:
			fmt.Println("Winner: ", unit1.getName())
			unit1.incrementWinCount()
		}
		fmt.Println()
	}

	fmt.Println("result")
	fmt.Println(unit1.getName(), ": ", unit1.getWinCount())
	fmt.Println(unit2.getName(), ": ", unit2.getWinCount())

}
```


## 感想
コードを見てもクラスで固まっているわけではないので，クラスっぽくはないのですが，動きとしては，クラスに寄せることができたかなと思います．(クラスごとにフォルダを分ければもっとそれっぽくなりそう)
オブジェクトの視点でコードを書いていくの楽しい!!と思いました．

## 参考
http://www.geocities.jp/m_hiroi/golang/index.html