개요
SOLID는 객체 지향 프로그래밍 및 설계에서 다섯 가지 기본 원칙을 의미합니다.
이 원칙들은 코드의 유지보수성과 확장성을 높이는 데 도움을 줍니다.
각 원칙과 간단한 예제를 함께 살펴보겠습니다.
1. 단일 책임 원칙 (Single Responsibility Principle - SRP)
2. 개방-폐쇄 원칙 (Open-Closed Principle - OCP)
3. 리스코프 치환 원칙 (Liskov Substitution Principle - LSP)
4. 인터페이스 분리 원칙 (Interface Segregation Principle - ISP)
5. 의존성 역전 원칙 (Dependency Inversion Principle - DIP)
1. 단일 책임 원칙 (Single Responsibility Principle - SRP)
원칙: 하나의 클래스는 하나의 책임만 가져야 합니다. 즉, 클래스는 하나의 기능만을 가져야 하며,
이를 변경하는 이유는 오직 하나여야 합니다.
// 잘못된 예: User 클래스가 로그인과 데이터베이스 저장 두 가지 책임을 가지고 있음
class User {
public void login() {
// 로그인 로직
}
public void save() {
// 데이터베이스 저장 로직
}
}
// SRP를 따른 예: User, UserRepository, UserService로 각각의 책임을 분리
class User {
// User 관련 데이터
}
class UserRepository {
public void save(User user) {
// 데이터베이스 저장 로직
}
}
class UserService {
public void login(User user) {
// 로그인 로직
}
}
2. 개방-폐쇄 원칙 (Open-Closed Principle - OCP)
원칙: 소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에 대해서는 열려 있어야 하지만,
수정에 대해서는 닫혀 있어야 합니다.
// 잘못된 예: 새로운 도형이 추가될 때마다 AreaCalculator 클래스를 수정해야 함
class Rectangle {
public double width;
public double height;
}
class AreaCalculator {
public double calculate(Rectangle rectangle) {
return rectangle.width * rectangle.height;
}
}
// OCP를 따른 예: Shape 인터페이스를 사용하여 새로운 도형 추가 시 기존 코드를 수정할 필요 없음
interface Shape {
double area();
}
class Rectangle implements Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
public double area() {
return width * height;
}
}
class AreaCalculator {
public double calculate(Shape shape) {
return shape.area();
}
}
3. 리스코프 치환 원칙 (Liskov Substitution Principle - LSP)
원칙: 서브 타입은 언제나 자신의 기반 타입으로 교체할 수 있어야 합니다.
즉, 프로그램의 정확성을 깨뜨리지 않으면서 자식 클래스의 인스턴스로 부모 클래스의 인스턴스를 대체할 수 있어야 합니다.
// 잘못된 예: Ostrich 클래스가 Bird 클래스의 fly 메서드를 오버라이딩하여 예외를 발생시킴
class Bird {
public void fly() {
// 날다
}
}
class Ostrich extends Bird {
public void fly() {
throw new UnsupportedOperationException();
}
}
// LSP를 따른 예: Bird 인터페이스를 사용하여 날 수 있는 새와 날 수 없는 새를 분리
interface Bird {
void move();
}
class FlyingBird implements Bird {
public void move() {
fly();
}
private void fly() {
// 날다
}
}
class Ostrich implements Bird {
public void move() {
run();
}
private void run() {
// 달리다
}
}
4. 인터페이스 분리 원칙 (Interface Segregation Principle - ISP)
원칙: 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫습니다.
즉, 클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 합니다.
// 잘못된 예: Worker 인터페이스가 HumanWorker와 RobotWorker 모두에게 불필요한 메서드를 포함
interface Worker {
void work();
void eat();
}
class HumanWorker implements Worker {
public void work() {
// 일하다
}
public void eat() {
// 먹다
}
}
class RobotWorker implements Worker {
public void work() {
// 일하다
}
public void eat() {
throw new UnsupportedOperationException();
}
}
// ISP를 따른 예: Workable, Eatable 인터페이스로 분리하여 각 클래스가 필요한 메서드만 구현하도록 함
interface Workable {
void work();
}
interface Eatable {
void eat();
}
class HumanWorker implements Workable, Eatable {
public void work() {
// 일하다
}
public void eat() {
// 먹다
}
}
class RobotWorker implements Workable {
public void work() {
// 일하다
}
}
5. 의존성 역전 원칙 (Dependency Inversion Principle - DIP)
원칙: 고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 둘 다 추상화에 의존해야 합니다.
즉, 추상화된 인터페이스나 슈퍼클래스를 통해 의존성을 역전시켜야 합니다.
// 잘못된 예: Switch 클래스가 Light 클래스에 직접 의존
class Light {
public void turnOn() {
// 불 켜기
}
public void turnOff() {
// 불 끄기
}
}
class Switch {
private Light light;
public Switch(Light light) {
this.light = light;
}
public void operate() {
light.turnOn();
}
}
// DIP를 따른 예: Switchable 인터페이스를 사용하여 의존성을 추상화
interface Switchable {
void turnOn();
void turnOff();
}
class Light implements Switchable {
public void turnOn() {
// 불 켜기
}
public void turnOff() {
// 불 끄기
}
}
class Switch {
private Switchable device;
public Switch(Switchable device) {
this.device = device;
}
public void operate() {
device.turnOn();
}
}
정리
해당 포스팅에서는 SOLID 원칙에 대한 정의와 예제 케이스를 통하여,
각각 원칙이 무슨 역할을 하는지에 대해 간단하게 정리해봤습니다.
조금 더 깊은 내용을 알고싶으신 분들은 아래 정리가 잘 된 블로그 포스팅들이 있어 링크 공유드립니다.
https://mangkyu.tistory.com/194
'개발메모 > 간단정리' 카테고리의 다른 글
[간단정리] RDBMS, NoSQL 특징 및 차이점 (1) | 2024.08.06 |
---|---|
[간단정리] {JSON}, <XML> 이란? (0) | 2024.07.18 |
[간단정리] 메세지 큐(Message Queue)란? (1) | 2024.07.13 |
[간단정리] Domain, Host, URL, URI 용어 정리 (1) | 2024.06.29 |
[간단정리] Java에서의 Hash(해시) 함수 (0) | 2024.03.14 |