LoginSignup
3
2

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-11-22

SOLID原則について

SOLID原則とは

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

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

なぜSOLID原則?

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

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

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

SOLIDの各原則について

SRP(単一責任の原則)

説明

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

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

コード

悪い例

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


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

改善後

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


type EmployeeWorkManage interface {
    calculatePay()
    reportHours()
}

type EmployeeDataManage interface {
    saveEmployee()
}

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

説明

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

コード

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


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メソッドまで記述をしないといけなくなる。


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

改善後

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

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


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原則について分かったような気がします。
質問やご指摘がございましたら、コメントに書いていただけると幸いです。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2