| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- springboot 게시판만들기
- 프로그래머스 괄호 회전하기 python
- 스프링부트 미니프로젝트
- python 괄호 회전하기
- 유니티기초
- 스프링부트 update
- 타입스크립트 기초문법
- 유니티
- 유니티Cube
- springboot 게시판
- 괄호 회전하기 파이썬
- 스프링부트 게시판만들기
- 파이썬 괄호 회전하기
- 타입스크립트 기본문법
- JS기초
- typescript 기초문법
- spring jpa 게시판
- 유니티Material
- 스프링 게시판 만들기
- spring jpa 사이드프로젝트
- springboot 사이드프로젝트
- springboot 게시판 프로젝트
- 스프링부트 블로그만들기
- springboot 미니프로젝트
- 스프링게시판프로젝트
- 파이썬 기초
- 타입스크립트 기초
- jpa 게시판
- 스프링부트 블로그
- 스프링부트 회원가입
- Today
- Total
Digking's cave
객체지향 설계 5대 원칙 (SOLID) 개념과 코드 예시 본문
객체지향 5대 원칙 (SOLID) 에 각 원칙을 보여주는 간단한 코드 예제까지!
객체지향 설계 5대 원칙 (SOLID) 쉽게 이해하기 🍎🍊🍌
① 의존 역전 원칙 (DIP)
[FruitService] --> (FruitRepo 인터페이스) <-- [DbFruitRepo], [MemoryFruitRepo]
상위 수준 모듈은 구체 클래스에 의존하지 않고 추상화(인터페이스) 에 의존해야 한다는 원칙이에요.
즉, "사과 저장소" 같은 구체 구현이 아니라, "과일 저장소 인터페이스"에만 의존하면 됩니다.
이렇게 하면 DB 저장소든 메모리 저장소든 쉽게 갈아끼울 수 있어 확장성이 좋아집니다.
예제 코드
interface FruitRepo { int priceOf(String name); }
class DbFruitRepo implements FruitRepo {
public int priceOf(String n){ return 500; }
}
class MemoryFruitRepo implements FruitRepo {
public int priceOf(String n){ return "apple".equals(n)?300:0; }
}
class FruitService {
private final FruitRepo repo; // 추상에만 의존
FruitService(FruitRepo repo){ this.repo = repo; }
int totalPrice(String... names){
int sum=0; for (String n: names) sum += repo.priceOf(n);
return sum;
}
}
② 리스코프 치환 원칙 (LSP)
(Fruit) <- Apple, Orange
자식 클래스는 부모 클래스를 대체해도 정상 동작해야 한다는 원칙이에요.
즉, Fruit 대신 Apple이나 Orange를 넣어도 계산 로직이 문제없이 돌아가야 합니다.
상속을 했다면 부모의 계약을 깨지 않고 지켜야 한다는 뜻이에요.
예제 코드
interface Fruit { String name(); int price(); }
class Apple implements Fruit {
public String name(){ return "apple"; }
public int price(){ return 300; }
}
class Orange implements Fruit {
public String name(){ return "orange"; }
public int price(){ return 500; }
}
// 부모 타입 자리에서 자식 타입이 잘 동작
static int checkout(Fruit f){ return f.price(); }
③ 단일 책임 원칙 (SRP)
[PriceCalculator] [ReceiptPrinter] [Inventory]
클래스는 오직 하나의 책임만 가져야 한다는 원칙이에요.
가격 계산, 재고 관리, 영수증 출력까지 다 하는 만능 클래스는 유지보수가 어렵습니다.
역할을 각각의 클래스로 나누면 수정 이유가 명확해지고 테스트도 훨씬 쉬워집니다.
예제 코드
class PriceCalculator {
int calc(Fruit f){ return f.price(); }
}
class ReceiptPrinter {
void print(Fruit f, int price){
System.out.println(f.name()+": "+price);
}
}
class Inventory {
void decrease(Fruit f){ /* 재고 감소 로직 */ }
}
④ 개방-폐쇄 원칙 (OCP)
(Fruit) <- Apple, Orange, Banana(확장!)
[Cashier]는 수정 필요 없음
코드는 확장에는 열려 있고 수정에는 닫혀 있어야 한다는 원칙이에요.
조건문(if/else)으로 과일 종류를 구분하면 새로운 과일이 추가될 때마다 기존 코드를 수정해야 합니다.
반대로 다형성으로 인터페이스를 두면 Banana 클래스를 새로 만들기만 하면 돼서 기존 코드에는 손댈 필요가 없습니다.
예제 코드
interface Fruit { int price(); }
class Apple implements Fruit { public int price(){ return 300; } }
class Orange implements Fruit { public int price(){ return 500; } }
class Banana implements Fruit { public int price(){ return 200; } } // 새 과일 추가
int priceOf(Fruit f){ return f.price(); } // 기존 코드는 수정 없음
⑤ 인터페이스 분리 원칙 (ISP)
BigFruitOps (❌ 너무 큼)
↓ 분리
Pricable | Eatable | StockManageable (✅ 필요한 것만)
클라이언트는 자신이 쓰지 않는 메서드에 의존하면 안 된다는 원칙이에요.
즉, price()만 필요한데 eat()과 restock()까지 들어있는 거대한 인터페이스는 불편합니다.
대신 Pricable, Eatable처럼 작은 인터페이스로 나누면 필요한 것만 의존할 수 있어 더 유연합니다.
예제 코드
interface Pricable { int price(); }
interface Eatable { void eat(); }
class Apple implements Pricable, Eatable {
public int price(){ return 300; }
public void eat(){ System.out.println("사과를 먹습니다."); }
}
class PriceTag {
private final Pricable p; // 필요한 기능만 의존
PriceTag(Pricable p){ this.p = p; }
void show(){ System.out.println(p.price()); }
}
✅ 이렇게 SOLID 원칙을 코드에 적용하면,
- 변경에 강하고
- 확장이 쉽고
- 유지보수와 테스트가 편한
깔끔한 객체지향 설계를 만들 수 있습니다!
'IT > IT 지식' 카테고리의 다른 글
| 인터프리터 언어 / 컴파일러 (0) | 2022.02.04 |
|---|