C#で単一責任原則(SRP)を実装する方法をわかりやすく解説
「クラスが変更されるべき理由は、たったひとつであるべきだ」――これがSOLID設計原則のひとつである単一責任原則(Single Responsibility Principle / SRP)の核心です。本記事では、この原則の意味と、C#での具体的な実装方法をコード例とともに解説します。
単一責任原則とは?
定義: ここでいう「責任」とは、クラスを変更する理由そのものを指します。
この原則によれば、あるクラスに変更理由が2つ存在する場合、その機能は2つのクラスに分割すべきです。各クラスは1つの責任のみを担当し、将来変更が必要になったときは、その責任を持つクラスだけを修正すればよいことになります。
逆に、複数の責任を持つクラスに変更を加えると、その修正が別の責任に関わる機能へ意図しない影響を与える可能性があります。これが、バグや保守性の低下につながる大きな要因となるのです。
複数の責任がもたらす問題
- 1つの変更が他の機能に波及し、予期しないバグを引き起こす
- テスト範囲が広がり、テストが書きにくくなる
- コードの再利用性が下がる
- 変更のたびに影響範囲の調査が必要になり、保守コストが増大する
C#での実装例
ここでは「招待メールを送信する」機能を例に、原則適用前後のコードを比較してみましょう。
適用前:単一責任原則違反のコード
以下のコードでは、SendInviteメソッドが「名前の検証」「メールアドレスの検証」「メール送信」という3つの異なる責任を1つのクラスで担っています。
using System;
using System.Net.Mail;
namespace SolidPrinciples.Single.Responsibility.Principle.Before {
class Program{
public static void SendInvite(string email,string firstName,string lastname){
if(String.IsNullOrWhiteSpace(firstName)|| String.IsNullOrWhiteSpace(lastname)){
throw new Exception("Name is not valid");
}
if (!email.Contains("@") || !email.Contains(".")){
throw new Exception("Email is not Valid!");
}
SmtpClient client = new SmtpClient();
client.Send(new MailMessage("Test@gmail.com", email) { Subject="Please Join the Party!"})
}
}
}
この状態では、たとえばメールアドレスの検証ルールを変更したい場合でも、招待送信ロジックを含むクラス全体を修正することになります。これは明らかに好ましくありません。
適用後:単一責任原則に準拠したコード
責任ごとにクラスを分離すると、次のようにリファクタリングできます。名前の検証はUserNameService、メールアドレスの検証はEmailServiceがそれぞれ専用に担当します。
using System;
using System.Net.Mail;
namespace SolidPrinciples.Single.Responsibility.Principle.After{
internal class Program{
public static void SendInvite(string email, string firstName, string lastname){
UserNameService.Validate(firstName, lastname);
EmailService.validate(email);
SmtpClient client = new SmtpClient();
client.Send(new MailMessage("Test@gmail.com", email) { Subject = "Please Join the Party!" });
}
}
public static class UserNameService{
public static void Validate(string firstname, string lastName){
if (string.IsNullOrWhiteSpace(firstname) || string.IsNullOrWhiteSpace(lastName)){
throw new Exception("Name is not valid");
}
}
}
public static class EmailService{
public static void validate(string email){
if (!email.Contains("@") || !email.Contains(".")){
throw new Exception("Email is not Valid!");
}
}
}
}
リファクタリングのポイント
- 責任の切り出し: 検証ロジックを静的サービスクラスとして独立させ、呼び出し側のコードは簡潔になりました。
- 独立性の向上: 各検証処理は個別にテスト・再利用できるため、ユニットテストの作成も容易です。
- 変更への強さ: 検証ルールの変更が必要な場合、該当するサービスクラスだけを修正すればよく、他の機能への影響を最小限に抑えられます。
まとめ
単一責任原則は、「1クラス = 1つの変更理由」というシンプルな指針ながら、ソフトウェアの保守性・テスト容易性・拡張性を大きく向上させる強力な原則です。C#に限らずあらゆるオブジェクト指向言語で活用できるため、クラス設計を行う際は「このクラスが変更される理由は何か?」を常に自問する習慣をつけるとよいでしょう。
-
Python 3で辞書を使って文字列をフォーマットする方法
Pythonでは、辞書(dict)を活用して文字列への値の埋め込み(補間)を行うことができます。この構文では、%と変換文字の間の括弧内に、辞書のキー名を指定します。例えば、キーcostに格納された浮動小数点数を$xxxx.xxの形式で表示したい場合は、表示したい位置に$%(cost).2fと記述します。辞書を使った文字列フォーマットの基本例以下は、辞書を使った文字列フォーマットで、文字列と数値を同時に補間・整形する例です。>>> print(%(language)s has %(number)03d quote types. % {language: Python, numb
-
Pythonで文字列をJSONに変換する方法|json.loads()の使い方を解説
PythonでJSON文字列を辞書(dict)に変換する基本Pythonでは、標準ライブラリの json モジュールを使うことで、JSON形式の文字列を簡単に扱うことができます。文字列として渡されたJSONデータをPythonの辞書型に変換するには、json.loads() メソッドを使用します。json.loads() は、有効なJSON文字列を受け取り、それを解析して辞書オブジェクトを返します。返された辞書には、すべての要素にキーを使ってアクセスできます。json.loads()の使用例>>> import json >>> s = {success: