🙌

単一責任の原則について

2025/02/26に公開

単一責任の原則について

SOLIDの5原則の1つである単一責任の原則について、調べた/実装した内容を記載します。

1. はじめに

SOLIDの5原則の1つとなります。

※SOLIDの原則については以下となります。
[S:単一責任の原則(Single Responsibility) ←ここ
O:オープン・クローズドの原則(Open-Closed)
L:リスコフの置換原則(Liskov Substitution)
I:インターフェイス分離の原則(Interface Segregation)
D:依存性逆転の原則(Dependency Inversion)

2. 単一責任の原則とは

1) 単一責任の原則とは

Copilot先生に聞いた結果は以下です。

単一責任の原則(Single Responsibility Principle, SRP)は、SOLID原則の一つで、
クラスやモジュールは一つの責任を持つべきという考え方です。
具体的には、クラスやモジュールが変更される理由は一つだけであるべきということです。

そのまんまですね。

2) 責任とは

「クラスやモジュールは一つの責任を持つべき」と記載がありますが、責任とは何でしょうか?
これもCopilot先生に聞いてみました。

責任とは、ある行動や結果に対して自分が負うべき義務や義理のことを指します。

という回答が返ってきました。

3. 単一責任の原則を適応してないケース

単一責任の原則を守らず、コードを書いた場合、どうなるのでしょうか。
以下のコードは、単一責任の原則を無視した場合のコードとなります。

//ユーザマスタ検索用
func SearchUsermstRepository(usercd string) (*SearchUsermst, error) {
	var searchdata SearchUsermst
	query := "select usercd,userpw,userm,,comanycd,deptcd from usermst where usercd = $1"

	row := db.QueryRow(query, usercd)
	err := row.Scan(&searchdata.usercd, &searchdata.userpw, &searchdata.usernm, &searchdata.companycd, &searchdata.deptcd)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil // データが見つからない場合
		}
		return nil, err
	}
	return &searchdata, nil
}

上記のusermst検索用モジュールを、マスタメンテサービスと認証サービスで利用しています。

//ユーザマスタメンテナンス ユーザ検索処理
package services

import (
	"errors"
	"net_http/Single/models"
)

func SearchUsermstService(usercd string) (models.SearchUsermst,error){

	searchuser,err := models.SearchUsermstRepository(usercd)

	if err != nil{
		return searchuser,err
	}
	return searchuser,nil
}
package services

import "net_http/Single/models"

func AuthUserService(usercd string, pw string) bool {

	//models内のSearchUsermstRepositoryからusermstのデータを取得する。
	//本来、以下のモジュールは、usermstメンテナンス機能の検索処理となります。
	//それなのに、認証処理でも利用されており、1つのモジュールで複数の仕事をしています。
	//これでは複数責任となっており、単一責任になっていません。
	authuser, err := models.SearchUsermstRepository(usercd)

	if err == nil {
		return false
	}
	if authuser.Userpw != pw {
		return false
	}
	return true
}

これの何が悪いのでしょうか?
現状のままでは特に問題はありません。
ただし、Search関数が、ある日突然、機能改修が行われ、取得項目のうち、userpwの項目を検索項目から外した場合、どうなるでしょうか。
当然、ユーザ認証機能で必要なuserpwの項目がなくなっているので、認証処理は、エラーとなってしまいます。

※マスタメンテ機能の検索機能で取得できる項目にパスワードを入れておけば、セキュリティ上のリスクとなるので、削除される可能性は高いですよね。

4. 単一責任の原則を適応した場合

上記のコードに単一責任の原則を適応したらどうなるのでしょうか。
先ず、認証処理用のRepositoryを新規に作成します。

package models

import "database/sql"

type SearchAuthuser struct {
	Usercd    string
	Userpw    string
}

func SearchAuthUserRepository(usercd string) (*SearchAuthuser, error) {
	var searchdata SearchAuthuser
	query := "select usercd,userpw,userm,,comanycd,deptcd from usermst where usercd = $1"

	row := db.QueryRow(query, usercd)
	err := row.Scan(&searchdata.Usercd, &searchdata.Userpw)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil // データが見つからない場合
		}
		return nil, err
	}
	return &searchdata, nil
}
package services

import "net_http/Single/models"

func AuthUserService(usercd string, pw string) bool {

	//models内のSearchUsermstRepositoryからusermstのデータを取得する。
    //usermstメンテナンス用のRepositoryは利用していないため、単一責任となります。
	authuser, err := models.SearchAuthUserRepository(usercd)

	if err == nil {
		return false
	}
	if authuser.Userpw != pw {
		return false
	}
	return true
}

authuserRepository.SearchAuthUserRepository()モジュールをauthuserService.goで呼び出すようにすれば、SearchUsermstRepositoryの責務が分割され、単一責務となります。

X. 参考にしたURL

https://qiita.com/MinoDriven/items/76307b1b066467cbfd6a

Discussion