1. 序論
単一責任の原則とは?
Wikipediaによると
単一責任の原則 (たんいつせきにんのげんそく、英: single-responsibility principle) は、プログラミングに関する原則であり、モジュール、クラスまたは関数は、単一の機能について責任を持ち、その機能をカプセル化するべきであるという原則である。モジュール、クラスまたは関数が提供するサービスは、その責任と一致している必要がある。
単一責任の原則の説明としてよく用いられる例が十徳ナイフなので、十徳ナイフを用いて単一責任の原則の説明を行う。
十徳ナイフはナイフに缶切り、マイナスドライバーに栓抜きと、多機能に展開されている。これをソフトウェア設計の視点で見ると、十徳ナイフという1つのオブジェクトに対して責任(=役割)が複数存在することになる。これはソフトウェア設計上望ましくない。
単一責任の原則として見たときは、以下の画像のように、1つのオブジェクトに対して責任(=役割)が1つのみというのが望ましい。
なぜ十徳ナイフだと良くないの?
1. 理解の難しさ(可読性の低下)
複数の機能を一つのクラスやモジュールに詰め込むと、そのコードを理解するのが難しくなる。新しい開発者がそのコードを見たとき、どの機能がどのように実装され、互いにどのように影響し合っているのかを把握するのが困難になるため。
2. 保守の複雑化
「十徳ナイフ」型のクラスやモジュールでは、一つの機能に変更を加えることが他の機能に予期せぬ影響を及ぼす可能性がある。これはバグを引き起こすリスクを高め、保守作業をより複雑にするため。
3. 再利用性の低下
単一責任の原則に反する設計では、再利用性が低下します。例えば、ある機能を別のプロジェクトで再利用したい場合に、不要な依存関係や余計な機能が組み込まれているために再利用が困難になるため。
4. テストの難易度増加
複数の機能が一つに組み込まれていると、それぞれを個別にテストすることが困難になります。単一の変更が複数の機能に影響を与える可能性があり、テストの複雑さが増すため。テストを書くのが億劫になる理由の1つでもある。
5. 拡張性の問題
システムが成長し、新しい機能が必要になった場合、既存の「十徳ナイフ」型の設計に新しい機能を追加することは困難である。これは、新しい要件を満たすためには大規模なリファクタリングが必要になることを意味する。
ソースコードで学ぶ単一責任の原則
class User < ApplicationRecord
# ユーザーを保存した後、ウェルカムメールを送信
after_create :send_welcome_email
private
def send_welcome_email
# メール送信のロジック
# 例: ActionMailerを直接呼び出してメールを送信
ActionMailer::Base.mail(from: "noreply@example.com", to: self.email, subject: "Welcome!", body: "Welcome to our platform!").deliver_now
end
end
# app/models/user.rb
# Userモデルはユーザー情報の責任を持つ。
class User < ApplicationRecord
end
# app/mailers/user_mailer.rb
# UserMailerはメール送信の責任を持つ。RailsのActionMailerを使用して、メール関連のロジックをカプセル化する。
class UserMailer < ApplicationMailer
def welcome_email(user)
@user = user
mail(to: @user.email, subject: 'Welcome to Our App')
end
end
# app/controllers/users_controller.rb
# UsersControllerでは、UserモデルやUserMailerの橋渡しをしている。
class UsersController < ApplicationController
def create
@user = User.new(user_params)
if @user.save
UserMailer.welcome_email(@user).deliver_later
redirect_to @user, notice: 'User was successfully created.'
else
render :new
end
end
private
def user_params
params.require(:user).permit(:name, :email)
end
end