SOLID 원칙

 

SOLID 원칙은 객체지향 설계(OOD)의 다섯 가지 기본 원칙을 나타내는 약어입니다. 이 원칙들을 따르면 확장 가능하고 유지보수하기 쉬운 소프트웨어를 개발할 수 있습니다.

1. 단일 책임 원칙(Single Responsibility Principle, SRP)

클래스는 단 하나의 책임(변경의 이유)을 가져야 합니다. 클래스의 책임이 명확하게 정의되어야 하며, 클래스가 변경되는 이유는 오직 하나여야 합니다.

예제 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// SRP 위반 예시
public class Employee {
    public void calculatePay() { /* 급여 계산 로직 */ }
    public void saveEmployee() { /* 직원 정보 저장 로직 */ }
    public void generateReport() { /* 보고서 생성 로직 */ }
}
// SRP 준수 예시
public class EmployeePayCalculator {
    public void calculatePay() { /* 급여 계산 로직 */ }
}
public class EmployeeRepository {
    public void saveEmployee() { /* 직원 정보 저장 로직 */ }
}
public class EmployeeReportGenerator {
    public void generateReport() { /* 보고서 생성 로직 */ }
}
cs

 

2. 개방-폐쇄 원칙(Open-Closed Principle, OCP)

소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 합니다. 즉, 기존 코드를 변경하지 않고도 새로운 기능을 추가할 수 있어야 합니다.

예제 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// OCP 위반 예시
public class AreaCalculator {
    public double calculateArea(Object shape) {
        if (shape instanceof Rectangle) {
            Rectangle rectangle = (Rectangle) shape;
            return rectangle.getWidth() * rectangle.getHeight();
        } else if (shape instanceof Circle) {
            Circle circle = (Circle) shape;
            return Math.PI * circle.getRadius() * circle.getRadius();
        }
        // 새로운 도형이 추가될 때마다 이 메소드를 수정해야 함
        return 0;
    }
}
// OCP 준수 예시
public abstract class Shape {
    public abstract double calculateArea();
}
public class Rectangle extends Shape {
    // …
    @Override
    public double calculateArea() {
        return width * height;
    }
}
public class Circle extends Shape {
    // …
    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
}
public class AreaCalculator {
    public double calculateArea(Shape shape) {
        return shape.calculateArea();
    }
}
cs

 

3. 리스코프 치환 원칙(Liskov Substitution Principle, LSP)

상위 타입의 객체를 하위 타입의 객체로 치환해도 프로그램의 정확성이 유지되어야 합니다. 즉, 하위 클래스는 상위 클래스의 계약(메소드 시그니처, 사전/사후 조건 등)을 준수해야 합니다.

예제 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// LSP 위반 예시
public class Rectangle {
    protected int width;
    protected int height;
    // …
}
public class Square extends Rectangle {
    @Override
    public void setWidth(int width) {
        this.width = width;
        this.height = width;
    }
    @Override
    public void setHeight(int height) {
        this.width = height;
        this.height = height;
    }
}
// LSP 준수 예시
public abstract class Shape {
    public abstract double calculateArea();
}
public class Rectangle extends Shape {
    // …
    @Override
    public double calculateArea() {
        return width * height;
    }
}
public class Square extends Shape {
    // …
    @Override
    public double calculateArea() {
        return side * side;
    }
}
cs

 

4. 인터페이스 분리 원칙(Interface Segregation Principle, ISP)

클라이언트는 사용하지 않는 메소드에 의존하도록 강요받아서는 안 됩니다. 인터페이스는 클라이언트의 요구사항에 맞게 작고 응집도 높은 단위로 분리되어야 합니다.

예제 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// ISP 위반 예시
public interface Printer {
    void print();
    void scan();
    void fax();
}
public class SimplePrinter implements Printer {
    @Override
    public void print() {
        // 인쇄 기능 구현
    }
    @Override
    public void scan() {
        throw new UnsupportedOperationException();
    }
    @Override
    public void fax() {
        throw new UnsupportedOperationException();
    }
}
// ISP 준수 예시
public interface Printer {
    void print();
}
public interface Scanner {
    void scan();
}
public interface Fax {
    void fax();
}
public class SimplePrinter implements Printer {
    @Override
    public void print() {
        // 인쇄 기능 구현
    }
}
public class AllInOnePrinter implements Printer, Scanner, Fax {
    // …
}
cs

 

5. 의존 역전 원칙(Dependency Inversion Principle, DIP)

고수준 모듈은 저수준 모듈의 구현에 의존해서는 안 됩니다. 둘 다 추상화에 의존해야 합니다. 추상화는 세부사항에 의존해서는 안 되며, 세부사항은 추상화에 의존해야 합니다.

예제 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// DIP 위반 예시
public class EmailSender {
    public void sendEmail(Gmail gmail) {
        gmail.send();
    }
}
// DIP 준수 예시
public interface EmailService {
    void sendEmail(String message);
}
public class EmailSender {
    private EmailService emailService;
    public EmailSender(EmailService emailService) {
        this.emailService = emailService;
    }
    public void sendEmail(String message) {
        emailService.sendEmail(message);
    }
}
public class Gmail implements EmailService {
    @Override
    public void sendEmail(String message) {
        // Gmail로 이메일 전송
    }
}
cs

 

SOLID 원칙을 따르면 코드의 가독성, 유지보수성, 확장성이 향상됩니다. 이 원칙들은 서로 상호 보완적이며, 객체지향 설계의 기본 지침으로 활용됩니다. 프로젝트의 요구사항과 상황에 맞게 이 원칙들을 적용하는 것이 중요합니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다