1. 本記事のポイント
- PHPでクラスを分割し、処理の責務を整理する基本方針を解説
- 単一責任の原則(SRP)をベースに、設計時の判断基準を提示
- 実務でのよくある分割パターンを、動作するコード例で紹介
2. PHPにおける「クラス分割」とは?
PHPでの開発において、処理が肥大化してくると1つのクラスが多くの役割を担ってしまい、保守性や再利用性が下がるケースがあります。これを防ぐための手法が「クラス分割」です。
クラス分割とは、1つのクラスに集中していた責務(処理内容)を、関連する処理単位ごとに分けて複数のクラスに整理することを指します。特に「単一責任の原則(Single Responsibility Principle)」は、クラス設計の出発点としてよく用いられます。
例えば「ユーザー登録処理」ならば、バリデーション、DB登録、通知送信などをすべて1クラスで行うのではなく、それぞれ専用のクラスに分離します。これにより、各クラスの変更理由が明確になり、将来的な改修や再利用がしやすくなります。
実務では、Webアプリケーションのユースケースを中心に、クラスごとの責務を整理することで、設計の見通しが大きく改善されます。
3. 詳細解説
以下では、「ユーザー登録処理」を例に、責務分割されたクラス設計を解説します。
一括処理の例(責務が集中している状態)
以下はNGパターンの例で、複数の処理が1つのクラスに集約されています。
class UserRegisterService {
public function register(array $data) {
// バリデーション
if (empty($data['email'])) {
throw new Exception('Email is required');
}
// DB登録(擬似処理)
echo "ユーザーDB登録:{$data['email']}\n";
// 通知送信
echo "通知送信:{$data['email']} に登録完了メール送信\n";
}
}
$service = new UserRegisterService();
$service->register(['email' => 'test@example.com']);
出力:
ユーザーDB登録:test@example.com
通知送信:test@example.com に登録完了メール送信
このように、バリデーション・DB登録・通知という異なる処理が1クラスに混在しています。
責務分割後の例
次に、それぞれの責務に応じたクラスを定義し、クラスの役割を分離した例を示します。
// バリデーション専用クラス
class UserValidator {
public function validate(array $data) {
if (empty($data['email'])) {
throw new Exception('Email is required');
}
}
}
// 永続化専用クラス(ここでは簡略化)
class UserRepository {
public function save(array $data) {
echo "ユーザーDB登録:{$data['email']}\n";
}
}
// 通知送信専用クラス
class NotificationService {
public function sendRegistrationEmail(string $email) {
echo "通知送信:{$email} に登録完了メール送信\n";
}
}
// サービスクラス(各処理を統括)
class UserRegisterService {
private $validator;
private $repository;
private $notifier;
public function __construct() {
$this->validator = new UserValidator();
$this->repository = new UserRepository();
$this->notifier = new NotificationService();
}
public function register(array $data) {
$this->validator->validate($data);
$this->repository->save($data);
$this->notifier->sendRegistrationEmail($data['email']);
}
}
$service = new UserRegisterService();
$service->register(['email' => 'test@example.com']);
このように処理が明確に分離されると、例えば通知処理の仕様変更時も他クラスには影響しません。
if文・switch文による処理分岐との比較
設計初心者によくある誤用として、複雑な条件分岐を1つのクラスに押し込めるケースがあります。
それよりも、処理の種類ごとにクラスを分ける方が拡張性・可読性に優れます。
4. よくあるミス・誤解・落とし穴
- 「クラスを増やすと面倒」として責務分割を避けると、保守性が著しく低下します。特にサービスクラスに処理を集めすぎるパターンが多く見られます。
- 責務の切り分けが曖昧なままクラスだけ増やすと、逆に把握しづらい設計になります。責務=「変更理由の一意性」で捉えると整理しやすくなります。
- 名前だけ分割して中身が一貫していないケース(例:ValidatorがDB操作するなど)も実務では散見されます。
PHPは柔軟なため曖昧な設計でも動いてしまいますが、それが設計の劣化を招きやすい点に注意が必要です。
5. まとめ
PHPのクラス設計において、責務を意識した分割は可読性・保守性の向上に直結します。
単一責任の原則に従い、関連する処理を専用クラスに整理することで、仕様変更時の影響範囲を抑え、実装の見通しも良くなります。
実務では、いきなり理想的な設計を目指すのではなく、まずは「変更理由が異なる処理は別クラスにする」という基本方針を意識することが重要です。