SOLID 원칙
SOLID 원칙이란?
객체지향 설계에서 유지보수성과 확장성을 높이기 위한 5가지 방법
이 원칙을 준수하면, 유연하고 변경에 강한 코드 작성 가능!
단일 책임 원칙 (SRP)
Single Responsibility Principle
하나의 클래스는 단 하나의 책임(기능) 만 가져야 함
즉, 하나의 변경 이유(Reason to Change) 만 가져야 함
⚠️ 여러 기능이 한 클래스에 섞이면 유지보수가 어려움!
SRP 위반
class Report {
void generateReport() {
// 리포트 생성 로직
}
void saveToFile() {
// 파일 저장 로직
}
}
➡︎ 리포트 생성과 파일 저장이라는 두 가지 책임을 가짐
SRP 적용
class ReportGenerator {
void generateReport() {
// 리포트 생성 로직
}
}
class FileSaver {
void saveToFile() {
// 파일 저장 로직
}
}
개방-폐쇄 원칙 (OCP)
Open/Closed Principle
코드는 확장에는 열려(Open) 있고, 변경에는 닫혀(Closed) 있어야 함
즉, 새로운 기능을 추가할 때 기존 코드를 수정하지 않아야 함
OCP 위반
class PaymentService {
void pay(String paymentType) {
if (paymentType.equals("CreditCard")) {
// 신용카드 결제 로직
} else if (paymentType.equals("PayPal")) {
// PayPal 결제 로직
}
}
}
➡︎ 새로운 결제 방식이 추가될 때마다 기존 코드를 수정해야 함
OCP 적용
interface Payment {
void pay();
}
class CreditCardPayment implements Payment {
public void pay() {
// 신용카드 결제 로직
}
}
class PayPalPayment implements Payment {
public void pay() {
// PayPal 결제 로직
}
}
class PaymentService {
void processPayment(Payment payment) {
payment.pay();
}
}
➡︎ 다형성을 활용해서 기존 코드를 수정하지 않고, 새로운 결제 방식 추가 가능!
리스코프 치환 원칙(LSP)
Liskov Substitution Principle
자식 클래스는 부모 클래스를 대체할 수 있어야 함
즉, 부모 클래스를 상속받은 모든 클래스는 부모의 역할을 온전히 수행할 수 있어야 함
LSP 위반
class Rectangle {
int width, height;
void setWidth(int width) { this.width = width; }
void setHeight(int height) { this.height = height; }
}
class Square extends Rectangle {
void setWidth(int width) {
this.width = width;
this.height = width; // 가로와 세로를 동일하게 설정 (LSP 위반)
}
}
➡︎ Square 클래스는 Rectangle 을 대체할 수 없음
LSP 적용
interface Shape {
int getArea();
}
class Rectangle implements Shape {
int width, height;
public int getArea() { return width * height; }
}
class Square implements Shape {
int side;
public int getArea() { return side * side; }
}
➡︎ 공통 인터페이스를 활용
인터페이스 분리 원칙(ISP)
Interface Segregation Principle
하나의 큰 인터페이스보다, 여러 개의 작은 인터페이스로 분리하는 게 좋음
즉, 사용하지 않는 메서드에 의존하면 안됨
ISP 위반
interface Worker {
void work();
void eat();
}
class Robot implements Worker {
public void work() { System.out.println("일을 합니다."); }
public void eat() { throw new UnsupportedOperationException(); } // 로봇은 먹을 수 없음 (ISP 위반)
}
➡︎ Robot 은 eat() 메서드를 가질 필요가 없음
ISP 적용
interface Workable {
void work();
}
interface Eatable {
void eat();
}
class Robot implements Workable {
public void work() { System.out.println("일을 합니다."); }
}
➡︎ 인터페이스를 분리하여 불필요한 의존성 제거
의존성 역전 원칙(DIP)
Dependency Inversion Principle
상위 모듈이 하위 모듈에 의존하면 안 됨
즉, 세부 구현이 아니라 추상화(Interface)에 의존해야 함
DIP 위반
class MySQLDatabase {
void connect() { System.out.println("MySQL 연결"); }
}
class DataManager {
MySQLDatabase database = new MySQLDatabase(); // 특정 DB에 강하게 결합 (DIP 위반)
}
➡︎ DB 를 변경하려면 DataManager 코드를 수정해야 함
DIP 적용
interface Database { // 추상화 적용
void connect();
}
class MySQLDatabase implements Database {
public void connect() { System.out.println("MySQL 연결"); }
}
class PostgreSQLDatabase implements Database {
public void connect() { System.out.println("PostgreSQL 연결"); }
}
class DataManager {
Database database;
DataManager(Database database) { // 의존성 주입 (DIP 적용)
this.database = database;
}
}
➡︎ 추상화 적용 ➔ 인터페이스를 활용하여 유연한 설계 가능
추상화
추상화(Abstraction) 란?
객체의 핵심점인 특징만 노출하고, 불필요한 세부 사항은 숨기는 개념
인터페이스와 추상 클래스로 구현 가능
❕ 코드의 복잡도/결합도를 낮추고, 확장성을 높임
비교 항목 | 인터페이스 | 추상 클래스 |
목적 | 행동 정의 | 기본 기능 제공 |
메서드 구현 | 전부 구현 X (default 메서드 제외) | 일부 구현 가능 |
다중 상속 | 가능 - implements | 불가능 - extends |
사용 예시 | 다양한 객체가 동일한 동작을 할 때 | 공통 기능을 제공하면서 일부만 구현이 필요할 때 |
* 자세한 내용은 아래 글의 인터페이스 vs 추상 클래스 내용 참고
상속(Inheritance)과 다형성(Polymorphism)
상속상속(Inheritance) 이란?기존 클래스를 확장하여 새로운 클래스를 만드는 개념코드 재사용성 증가 및 객체 간 계층 구조 형성 상속의 기본 구조// 부모 클래스 (Super Class)class Animal { String name; voi
jelliclesu.tistory.com
주요 포인트
- SOLID 원칙은 단순 암기가 아니라 어떻게 적용되는 지 알아야 함
- SRP: 하나의 클래스는 하나의 책임만 가져야 함
- OCP: 코드는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 함
- LSP: 자식 클래스는 부모 클래스를 대체할 수 있어야 함
- ISP: 하나의 큰 인터페이스보다, 여러 개의 작은 인터페이스로 분리하는 게 좋음
- DIP: 상위 모듈이 하위 모듈에 의존하면 안 됨
- 추상화가 왜 필요한지, 인터페이스와 추상 클래스의 차이는 무엇인지 명확하게 알아야 함
- 추상화: 객체의 핵심적인 특징만 노출하고, 불필요한 세부 사항은 숨김
- 장점: 코드의 결합도는 낮아지고, 유지보수성 향상
- SOLID 원칙과 추상화를 연계하여 유연한 설계 방법을 설명할 줄 알아야 함
'백엔드 기본 개념 정리 > 객체지향 프로그래밍 (OOP)' 카테고리의 다른 글
객체지향의 한계와 대안, 구성(Composition) (0) | 2025.03.10 |
---|---|
의존성(Dependency) 과 싱글톤(Singleton) (0) | 2025.03.10 |
상속(Inheritance)과 다형성(Polymorphism) (0) | 2025.03.06 |
객체지향의 기본 개념(클래스, 객체, 캡슐화) (0) | 2025.03.05 |