SOLID

ํด๋ฆฐ์ฝ”๋“œ๋กœ ์œ ๋ช…ํ•œ ๋กœ๋ฒ„ํŠธ ๋งˆํ‹ด์ด ์ •์˜ํ•œ ๊ฐ์ฒด ์ง€ํ–ฅ ์„ค๊ณ„์˜ 5๊ฐ€์ง€ ์›์น™

SRP: ๋‹จ์ผ ์ฑ…์ž„ ์›์น™(Single Responsibility Principle)

  • ํ•˜๋‚˜์˜ ํด๋ž˜์Šค๋Š” ํ•˜๋‚˜์˜ ์ฑ…์ž„(๋ชฉ์ )๋งŒ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค.

    • ์ฑ…์ž„์€ ํด ์ˆ˜๋„ ์žˆ๊ณ , ์ž‘์„ ์ˆ˜๋„ ์žˆ์œผ๋ฉฐ ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๋‹ค.

    • ํด๋ž˜์Šค๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์ด์œ ๊ฐ€ ํ•˜๋‚˜์˜ ์ด์œ ์—ฌ์•ผ ํ•œ๋‹ค.

  • ๋ณ€๊ฒฝ์— ์šฉ์ดํ•˜๊ฒŒ ์„ค๊ณ„ํ–ˆ๋‹ค๋ฉด ๋‹จ์ผ ์ฑ…์ž„ ์›์น™์„ ์ž˜ ๋”ฐ๋ฅธ ๊ฒƒ

class Order {
    private List<Item> items;

    public void addItem(Item item) {
        // ์•„์ดํ…œ์„ ์ฃผ๋ฌธ์— ์ถ”๊ฐ€ํ•˜๋Š” ๋กœ์ง
    }

    public double calculateTotal() {
        // ์ฃผ๋ฌธ์˜ ์ด ๊ฐ€๊ฒฉ์„ ๊ณ„์‚ฐํ•˜๋Š” ๋กœ์ง
    }
}

class Item {
    private String name;
    private double price;

    // ์•„์ดํ…œ์˜ ์ด๋ฆ„๊ณผ ๊ฐ€๊ฒฉ์„ ์„ค์ •ํ•˜๋Š” ์ƒ์„ฑ์ž ๋ฐ ๋ฉ”์„œ๋“œ
}

Order ํด๋ž˜์Šค๋Š” ์ฃผ๋ฌธ์— ๊ด€๋ จ๋œ ์ฑ…์ž„๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , Item ํด๋ž˜์Šค๋Š” ์•„์ดํ…œ์— ๊ด€๋ จ๋œ ์ฑ…์ž„๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

OCP: ๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™ (Open-Closed principle)

  • ํ™•์žฅ์—” ์—ด๋ ค์žˆ์œผ๋‚˜, ๋ณ€๊ฒฝ์—๋Š” ๋‹ซํ˜€์žˆ์–ด์•ผ ํ•œ๋‹ค.

  • ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ๋•Œ ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„ํ•ด์•ผ ํ•œ๋‹ค.

interface Shape {
    double calculateArea();
}

class Circle implements Shape {
    private double radius;

    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
}

class Rectangle implements Shape {
    private double width;
    private double height;

    @Override
    public double calculateArea() {
        return width * height;
    }
}

// ์ข‹์€ ์˜ˆ, ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅ
class Test1 {
    private Shape shape;

    public Test1(Shape shape) {
        this.shape = shape;
    }

    public void doSomething() {
        shape.calculateArea();
        // ...
    }
}


// ๋‚˜์œ ์˜ˆ, ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด์„  ํ•„๋“œ์™€ ์กฐ๊ฑด๋ฌธ์„ ์ถ”๊ฐ€ํ•ด์•ผํ•˜๋Š” ๋ณ€๊ฒฝ์ด ํ•„์š”
class Test2 {
    private Circle circle = new Circle();
    private Rectangle rectangle = new Rectangle();

    public void doSomething(String shape) {
        if (shape.equals("circle")) {
            circle.calculateArea();
        } else if (shape.equals("rectangle")) {
            rectangle.calculateArea();
        }
        // ...
    }
}

์œ„ ์ฝ”๋“œ์—์„œ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Shape ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ํด๋ž˜์Šค๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

LSP: ๋ฆฌ์Šค์ฝ”ํ”„ ์น˜ํ™˜ ์›์น™ (Liskov Substitution Principle)

  • ๊ฐ์ฒด๋Š” ์ •ํ™•์„ฑ์„ ๊นจ๋œจ๋ฆฌ์ง€ ์•Š์œผ๋ฉด์„œ ์ƒ์œ„ ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋ฅผ ํ•˜์œ„ ํด๋ž˜์Šค๋กœ ๋Œ€์ฒด ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค.

  • ๋ถ€๋ชจ ํด๋ž˜์Šค์˜ ๊ทœ์•…์„ ์ž์‹ ํด๋ž˜์Šค๊ฐ€ ๋‹ค ์ง€์ผœ์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

class Parent {
    public int calculate(int a, int b) {
        return a + b;
    }
}

class Child extends Parent {
    @Override
    public int calculate(int a, int b) {
        if (b == 0) {
            throw new IllegalArgumentException("b cannot be zero");
        }
        return a / b;
    }
}

public class test {
    public static void main(String[] args) {
        Parent p = new Child();
        System.out.println(p.calculate(10, 0)); // IllegalArgumentException
    }
}

์œ„ ์ฝ”๋“œ์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์ง€๋งŒ ๋„“์ด๋ฅผ ๊ตฌํ•˜๋Š”๋ฐ์— ๋…ผ๋ฆฌ์ ์ธ ์ฐจ์ด๊ฐ€ ์žˆ์–ด ์˜๋„ํ•˜์ง€ ์•Š์€ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

ISP: ์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ ์›์น™ (Interface segregation principle)

  • ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์„ ํฌํ•จํ•œ ๋ฒ”์šฉ ์ธํ„ฐํŽ˜์ด์Šค ๋ณด๋‹จ ํŠน์ • ํด๋ผ์ด์–ธํŠธ๋ฅผ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

  • ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋ช…ํ™•ํ•ด์ง€๋ฉฐ ๋Œ€์ฒด ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„์ง„๋‹ค.

interface Printer {
    void print(Document document);
}

interface Scanner {
    void scan(Document document);
}

class AllInOnePrinter implements Printer, Scanner {
    public void print(Document document) {
        // ์ถœ๋ ฅ ๋กœ์ง
    }

    public void scan(Document document) {
        // ์Šค์บ” ๋กœ์ง
    }
}

์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ํด๋ž˜์Šค์— ํ•„์š”ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ๊ตฌํ˜„ํ•˜๋„๋ก ํ•˜๋ฉด, ํ•„์š”ํ•œ ๊ธฐ๋Šฅ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ , ๋Œ€์ฒด ๊ฐ€๋Šฅ์„ฑ๋„ ๋†’์•„์ง„๋‹ค. ๋˜ํ•œ ๋ถˆํ•„์š”ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ ๋˜๊ณ , ์‚ฌ์šฉํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ์—๊ฒ ๋ถˆํ•„์š”ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ๋…ธ์ถœ์‹œํ‚ค์ง€ ์•Š๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

DIP: ์˜์กด ์—ญ์ „ ์›์น™ (Dependency inversion principle)

  • "์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผํ•˜๊ณ , ๊ตฌ์ฒดํ™”์— ์˜์กดํ•˜๋ฉด ์•ˆ๋œ๋‹ค."๋ฅผ ๋”ฐ๋ฅด๋Š” ์›์น™

  • ๊ตฌํ˜„ ํด๋ž˜์Šค์— ์˜์กดํ•˜์ง€ ์•Š๊ณ , ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กดํ•ด์•ผ ํ•œ๋‹ค.

  • ๊ตฌํ˜„์ฒด์— ์˜์กดํ•˜๊ฒŒ ๋˜๋ฉด ๋ณ€๊ฒฝ์ด ์–ด๋ ค์›Œ์ง€๊ณ , ํ…Œ์ŠคํŠธ๋„ ์–ด๋ ค์›Œ์ง„๋‹ค.

interface PaymentProvider {
    void processPayment(int amount);
}

class KakaoPaymentProvider implements PaymentProvider {
    public void processPayment(int amount) {
        // ์นด์นด์˜คํŽ˜์ด ๊ฒฐ์ œ ๋กœ์ง
    }
}

class OrderService {
    private final PaymentProvider paymentProvider;

    public OrderService(PaymentProvider paymentProvider) {
        this.paymentProvider = paymentProvider;
    }

    public void processOrder(Order order) {
        // ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ ๋กœ์ง
        paymentProvider.processPayment(order.getTotalAmount());
    }
}

class OrderServiceTest {
    void processOrder() {
        OrderService orderService = new OrderService(new KakaoPaymentProvider());
        orderService.processOrder(new Order());
    }
}

์œ„ ์ฝ”๋“œ๋Š” ์ธํ„ฐํŽ˜์ด์Šค์˜ ์กด์žฌ๋กœ OrderService -> PaymentProvider <- KakaoPaymentProvider๋กœ ์˜์กด ๋ฐฉํ–ฅ์ด ์—ญ์ „๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋ณ€๊ฒฝ์ด ์šฉ์ดํ•ด์ง„ ์ฝ”๋“œ๊ฐ€ ๋œ๋‹ค. ํ•˜์ง€๋งŒ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์—†์—ˆ๋‹ค๋ฉด OrderService -> KakaoPaymentProvider๋กœ ์˜์กดํ•˜๊ฒŒ ๋˜์–ด ๋ณ€๊ฒฝ์ด ์–ด๋ ค์›Œ์ง€๊ณ , ํ…Œ์ŠคํŠธ๋„ ์–ด๋ ต๊ฒŒ ๋œ๋‹ค.

Last updated