SOLID原則とは
オブジェクト指向設計における5つの基本原則をまとめたものでソフトウェアの保守性や拡張性を高めるための指針です。
S - 単一責任の原則(Single Responsibility Principle / SRP)
クラスやモジュールは、単一の機能や責任を持つべきであり、変更の理由が一つだけであるように設計するこど
規則を守ってない例
public class OrderService { public void createOrder(Order order) { System.out.println("Create order : " + order.getId()); } public void processPayment(Payment payment) { System.out.println("Process payment : " + payment.getId()); } }
規則を守った例
public class OrderService { public void createOrder(Order order) { System.out.println("Create order : " + order.getId()); } } public class PaymentService { public void processPayment(Payment payment) { System.out.println("Process payment : " + payment.getId()); } }
O - 解放/閉鎖の原則(Open/Closed Principle / OCP)
ソフトウェアの構成要素は、拡張に対して開かれており、修正に対して閉じられているべきです。つまり、新たな機能追加は既存のコードを変更せずに行えるようにすること
規則を守ってない例
public class DataExporter { public void export(String type) { if (type.equals("xml")) { System.out.println("Exporting XML file..."); } else if (type.equals("json")) { System.out.println("Exporting JSON file..."); } // 他のタイプを追加するには、DataExporterクラスの修正が必要 } }
規則を守った例
public interface Exporter { void export(); } public class XMLExporter implements Exporter { @Override public void export() { System.out.println("Exporting XML file..."); } } public class JSONExporter implements Exporter { @Override public void export() { System.out.println("Exporting JSON file..."); } } public class Main { public static void main(String[] args) { Exporter xmlExporter = new XMLExporter(); xmlExporter.export(); Exporter jsonExporter = new JSONEXporter(); jsonExporter.export(); } }
L - リスコフの置換原則(Liskov Substitution Principle / LSP)
派生クラスは、基底クラスと置き換えてもプログラムの正しさを損なわないように設計すること
規則を守ってない例
public class Bird { public void fly() { System.out.println("flying"); } } public class Penguin extends Bird { @Override public void fly() { throw new UnsupportedOperationException("Penguins can't fly"); } } public class Main { public static void main(String[] args) { Bird bird = new Bird(); bird.fly(); Bird penguin = new Penguin(); penguin.fly(); // Throws UnsupportedOperationException } }
規則を守った例
public interface Flyable { void fly(); } public class Bird { public void eat() { System.out.println("eating"); } } public class Pigeon extends Bird implements Flyable { @Override public void fly() { System.out.println("Pigeon is flying"); } } public class Penguin extends Bird { } public class Main { public static void main(String[] args) { Bird pigeon = new Pigeon(); pigeon.eat(); (Flyable pigeon).fly(); Bird penguin = new Penguin(); penguin.eat(); } }
I - インターフェース分離の原則(Interface Segregation Principle / ISP)
クライアントが使用しないメソッドへの依存を強制されないよう、特定の機能に特化した小さなインターフェースを設計すること
規則を守ってない例
public interface IPhone { void call(String number); void chargeWirelessly(); void unlockByTouchId(String bio); } public class IPhone12Pro implements IPhone { @Override public void call(String number) { System.out.println("Call to " + number); } @Override public void chargeWirelessly() { System.out.println("Charging..."); } @Override public void unlockByTouchId(String bio) { System.out.println("Unsupport touchId"); } } public class IPhone8 implements IPhone { @Override public void call(String number) { System.out.println("Call to " + number); } @Override public void chargeWirelessly() { System.out.println("Unsupport wireless charge"); } @Override public void unlockByTouchId(String bio) { System.out.println("unlocking by touchId"); } }
規則を守った例
public interface Callable { void call(String number); } public interface WirelessChargeable { void chargeWirelessly(); } public void TouchIdUnlockable { void unlockByTouchId(String bio); } public class IPhone12Pro implements Callable, WirelessChargeable { @Override public void call(String number) { System.out.println("Call to " + number); } @Override public void chargeWirelessly() { System.out.println("Charging..."); } } public class IPhone8 implements Callable, TouchIdUnlockable { @Override public void call(String number) { System.out.println("Call to " + number); } @Override public void unlockByTouchId(String bio) { System.out.println("unlocking by touchId"); } }
D - 依存性逆転の原則(Dependency Inversion Principle / DIP)
高水準モジュールは低水準モジュールに依存すべきではなく、両者とも抽象化されたインターフェースに依存するように設計すること
規則を守ってない例
// 低水準モジュール public class LightBulb { public void spin() { System.out.println("Fan is spinning"); } public void stop() { System.out.println("Fan is stopping"); } } // 高水準モジュール public class Switch { private LightBulb bulb; public Switch(LightBulb bulb) { this.bulb = bulb; } public void turnOn() { this.bulb.spin(); } public void turnOff() { this.bulb.stop(); } }
規則を守った例
public interface Switchable { void turnOn(); void turnOff(); } // インターフェースを実装した低水準モジュール public class Fan implements Switchable { @Override public void turnOn() { System.out.println("Fan is spinning"); } @Override public void turnOff() { System.out.println("Fan is stopping"); } } // 高水準モジュール public class Switch { private Switchable device; public Switch(Switchable device) { this.device = device; } public void turnOn() { this.device.turnOn(); } public void turnOff() { this.device.turnOff(); } }