2.1 単一責任原則(Single Responsibility Principle)
クラスやメソッドは1つの責任しか持たないべきです。以下の例ではユーザー登録処理を実装しています。
違反例:
public class UserRegistrationController {
@RequestMapping("/register")
public String registerUser(String name, String pass) {
if (name == null || pass == null) return "error";
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db", "user", "pass");
String sql = "INSERT INTO users VALUES(?,?)";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.execute();
return "success";
}
}
この実装ではパラメータ検証・DB接続・SQL実行がすべて混在しており、再利用性や保守性が低いです。
改善例:
public class UserRegistrationController {
@Autowired
private UserRepository userRepo;
@RequestMapping("/register")
public String registerUser(String name, String pass) {
validateInput(name, pass);
userRepo.saveUser(name, pass);
return "success";
}
private void validateInput(String... inputs) {
// 入力検証ロジック
}
}
public interface UserRepository {
void saveUser(String name, String pass);
}
各クラス・メソッドがそれぞれの責務を分離することで、拡張性と保守性が向上します。
2.2 開閉原則(Open-Closed Principle)
既存の振る舞いを変更せずに新機能を追加できる設計を目指します。
違反例:
public static String formatDateTime(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
この実装では既存の呼び出し元に影響を与える変更が必要です。
改善例:
public static String formatDate(Date date) {
return new SimpleDateFormat("yyyy-MM-dd").format(date);
}
public static String formatDateTime(Date date) {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
}
既存のメソッドを残したまま新メソッドを追加することで、変更リスクを回避します。
2.3 リスコフ置換原則(Liskov Substitution Principle)
サブクラスはいつでもスーパークラスと置き換え可能である必要があります。
違反例:
public class ShapeComparator {
public int compareArea(Shape s) { /* 面積比較ロジック */ }
}
public class CircleComparator extends ShapeComparator {
@Override
public int compareArea(Shape s) {
// 円専用の比較ロジック(親クラスの振る舞いを破壊)
}
}
改善例:
public class ShapeComparator {
public int compareArea(Shape s) { /* 基本的な面積比較 */ }
}
public class ExtendedShapeComparator extends ShapeComparator {
public int comparePerimeter(Shape s) {
// 新しい比較方法を追加(既存ロジックを保持)
}
}
既存の振る舞いを維持しながら拡張することで、置換性を保証します。
2.4 インターフェース分離原則(Interface Segregation Principle)
クライアントは使用しないメソッドに依存すべきではありません。
違反例:
public interface OrderService {
void createOrder();
void cancelOrder();
void processPayment(); // 支払い専用メソッド
void refundPayment(); // 返金専用メソッド
}
改善例:
public interface OrderService {
void createOrder();
void cancelOrder();
}
public interface PaymentService {
void processPayment();
void refundPayment();
}
責務ごとにインターフェースを分割することで、不必要な依存を排除します。
2.5 依存性逆転原則(Dependency Inversion Principle)
具体的な実装ではなく抽象に依存する設計を目指します。
違反例:
public interface DataProcessor {
void process(ArrayList<String> data); // 具体的なクラスに依存
}
改善例:
public interface DataProcessor {
void process(List<String> data); // 抽象インターフェースに依存
}
public class ListProcessor implements DataProcessor {
public void process(List<String> data) {
// 具体的な実装
}
}
抽象層を介することで、実装変更の影響を最小限に抑えます。
2.6 デミテルの法則(Law of Demeter)
オブジェクト間の結合度を最小限に抑えるべきです。
違反例:
public class Company {
public void notifyAllEmployees() {
for (Department dept : departments) {
for (Employee emp : dept.getEmployees()) {
emp.receiveNotice(); // 多重ドットアクセス
}
}
}
}
改善例:
public class Company {
public void notifyAllEmployees() {
for (Department dept : departments) {
dept.notifyEmployees(); // 直接関係のあるオブジェクトとだけやり取り
}
}
}
public class Department {
public void notifyEmployees() {
for (Employee emp : employees) {
emp.receiveNotice();
}
}
}
各オブジェクトが直接やり取りする対象を最小限にすることで、結合度を低下させます。