SOLID原則を理解するためにGo言語で書いてみた

# SOLID原則について

## SOLID原則とは

- SRP(単一責任の原則)
- OCP(オープン・クローズドの原則)
- LSP(リスコフの置換原則)
- ISP(インターフェース分離の原則)
- DIP(依存関係逆転の原則)

の頭文字を取ったものである。

## なぜSOLID原則？

SOLIDを適用する理由として、

- 変更に強くなる
- 理解しやすくなる
- 多くのソフトウェアシステムで利用できること

が挙げられます。
以下から、各原則の説明、コードについて触れていきます。

# SOLIDの各原則について

## SRP(単一責任の原則)

###説明

1つのモジュールやクラス、関数などを変更する際に、理由が複数あってはいけないという原則
理由としては以下のような事象が起きることを防ぐため

- 複数の人が一つのソースファイルを変更した際に、マージやコンフリクトが発生
- 変更した内容が予期しないところに影響を受ける


### コード

#### 悪い例

一つのインタフェースで従業員の管理、従業員のデータの保存と複数の責務を負っている。

```go

type EmployeeWorkManage interface {
	calculatePay()
	reportHours()
	saveEmployee()
}

```


####改善後

従業員の管理、従業員のデータの保存のインターフェースを分けて作成して、責務をそれぞれ1つになるようにする。

```go

type EmployeeWorkManage interface {
	calculatePay()
	reportHours()
}

type EmployeeDataManage interface {
	saveEmployee()
}

```



## OCP(オープン・クローズドの原則)

###説明

ソフトウェアの拡張に対して開いていて、修正に対して閉じてなければならないという原則
そうすることで変更の影響を受けずにシステムを拡張できる。



### コード

Animalというインターフェイスを作成して、それに依存した形で、Dog型やCat型のメソッドを作成することによって、mainの処理ではDog型やCat型を気にせずに、barkメソッドを実行できる。
また新しくBirdを作りたい場合でも、Animalのインターフェースに依存した作りにすることによって追加だけで済む。

```go

package main
import(
	"fmt"
)

type Animal interface {
	bark()
	eat()
}

type Dog struct {}

type Cat struct {}

func (d Dog) bark(){
	fmt.Println("ワンワン")
}

func (d Dog) eat(){
	fmt.Println("バクバク")
}

func (c Cat) bark(){
	fmt.Println("にゃー")
}

func (c Cat) eat(){
	fmt.Println("ムシャムシャ")
}

func main(){
	dog := Dog{}
	cat := Cat{}

	animals := []Animal{dog, cat}
	
	for _, animal := range animals {
		animal.bark()
	}

}
```

## LSP(リスコフの置換原則)

###説明

S型のオブジェクト :o1
T型のオブジェクト: o2
がある時に、Tを使って定義されたプログラムPに対して、o2の代わりに、o1を使ってもPの振る舞いが変わらない場合、SはTの派生型であるという原則

### コード

Goには継承がないため、リスコフの置換原則に違反することがなく意識されない（？）

## ISP(インターフェース分離の原則)

###説明

不要なインターフェースに依存することを強制してはいけないという原則
関連のあるインターフェースだけをグループ化、利用しないメソッドの依存を失くす。

### コード

#### 悪い例

以下のようなAnimalInterfaceから人間型を作ろうとすると、使用しないflyメソッドまで記述をしないといけなくなる。

```go

type AnimalInterface interface {
	fly()
	run()
	swim()
}
```

####改善後

鳥だったらBirdInterface、人間だったらHumanInterfaceとインターフェースを分けるようにする。
そうすることによって不要なメソッドを記述せずに済む。

```go
type BirdInterface interface {
	fly()
}

type HumanInterface interface {
	run()
	swim()
}

type Bird struct {}
type Human struct{}

func (b Bird) fly(){
	fmt.Println("飛びます！！")
}

func (h Human) run(){
	fmt.Println("走るぞ〜")
}

func (h Human) swim(){
	fmt.Println("泳ぎます〜")
}

func main(){
	bird := Bird{}
	human := Human{}

	bird.fly()
	human.run()
}

```


## DIP(依存関係逆転の原則)

###説明

頻繁に変更されている具象モジュール（関数の実装が書かれているモジュール）に依存してはいけない原則
変更しやすい具象への依存を避け、安定した抽象インターフェースに依存する。
Abstract Factoryパターンを利用して、抽象インターフェイスを参照するようにする。

### コード

Abstract Factoryに依存することによって、依存先が図のようなイメージになり、DBかテキストどちらかを気にせずに、ユーザのデータを取得できるようになる。

![Untitled Diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/515779/6c0b6b4a-c95a-8c78-6919-02db7d21bf5f.png)


```go

package main
import(
	"fmt"
)

type User struct {
	id int
	name string
}

type AbstractFactory interface {
	getData() User
}

type DbManage struct {}

func (db DbManage) getData() User {
	return User{1, "DB TARO"}
}

type TextManage struct {}

func (text TextManage) getData() User {
	return User{2, "TEXT JIRO"}
}

func getUserData(manageType string) User {
	var manageFactry AbstractFactory
	switch manageType {
	case "DB":
		manageFactry = DbManage{}
		return manageFactry.getData()
	case "TEXT":
		manageFactry = TextManage{}
		return manageFactry.getData()
	default:
		return User{3, "名無し"}
	}
}

func main(){
	user := getUserData("DB")
	fmt.Println(user.id, user.name)
}



```

# 参考

https://labs.septeni.co.jp/entry/2017/02/21/164127
https://maroyaka.hateblo.jp/entry/2017/05/22/165355
https://qiita.com/shunp/items/646c86bb3cc149f7cff9
https://golangvedu.wordpress.com/2017/01/31/golang-design-pattern-abstract-factory-and-factory-method/

# 最後に

なんとなくですが、SOLID原則について分かったような気がします。
質問やご指摘がございましたら、コメントに書いていただけると幸いです。