728x90
반응형
SMALL
2024.10.30 - [객체지향 프로그래밍(OOP)] - [쉬운설명]SOLID 원칙이란?
3. LSP (Liskov Substitution Principle) - 리스코프 치환 원칙
- 쉽게 말하면: "자식 클래스는 부모 클래스를 대신해서 사용될 수 있어야 해요."
📌 즉, 부모 클래스를 사용하는 코드는 자식 클래스를 사용해도 문제없이 동작해야 합니다.
예시: "도형의 넓이를 계산하기"
Java 코드
// LSP를 지키지 않은 코드 😥
class Rectangle {
protected double width;
protected double height;
public void setWidth(double width) {
this.width = width;
}
public void setHeight(double height) {
this.height = height;
}
public double getArea() {
return width * height; // 사각형 넓이
}
}
class Square extends Rectangle {
@Override
public void setWidth(double width) {
this.width = this.height = width; // 정사각형은 가로와 세로가 같아야 함
}
@Override
public void setHeight(double height) {
this.width = this.height = height;
}
}
// 동작 테스트
public class Main {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle();
rectangle.setWidth(5);
rectangle.setHeight(10);
System.out.println("Rectangle Area: " + rectangle.getArea()); // 50
Rectangle square = new Square(); // 부모 클래스 타입으로 자식 클래스 사용
square.setWidth(5);
square.setHeight(10); // 가로와 세로가 같아짐, 의도와 다르게 동작
System.out.println("Square Area: " + square.getArea()); // 100 (잘못된 결과)
}
}
Python 코드
# LSP를 지키지 않은 코드 😥
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def set_width(self, width):
self.width = width
def set_height(self, height):
self.height = height
def get_area(self):
return self.width * self.height # 사각형 넓이 계산
class Square(Rectangle):
def set_width(self, width):
self.width = self.height = width # 정사각형의 가로와 세로는 같아야 함
def set_height(self, height):
self.width = self.height = height
# 동작 테스트
rectangle = Rectangle(5, 10)
print("Rectangle Area:", rectangle.get_area()) # 50
square = Square(5, 10)
square.set_width(5)
square.set_height(10) # 정사각형이므로 가로=세로가 됨
print("Square Area:", square.get_area()) # 100 (잘못된 결과)
코드가 왜 잘못됐나요? 😥
Square
클래스가Rectangle
클래스를 대체했지만, 정사각형의 규칙 때문에 예상과 다르게 동작합니다.- LSP를 어김으로 인해
Rectangle
을 사용하는 코드에서 오류가 발생했어요.
4. ISP (Interface Segregation Principle) - 인터페이스 분리 원칙
- 쉽게 말하면: "사용하지 않는 기능은 구현하지 않아야 해요."
📌 큰 인터페이스 하나를 여러 작은 인터페이스로 나누어 필요한 것만 구현하게 해야 합니다.
예시: "동물의 행동"
Java 코드
// ISP를 지키지 않은 코드 😥
interface Animal {
void eat();
void fly(); // 모든 동물이 날 수는 없음
}
class Dog implements Animal {
public void eat() {
System.out.println("Dog eats");
}
public void fly() {
throw new UnsupportedOperationException("Dogs cannot fly"); // 강아지는 날 수 없어요!
}
}
// ISP를 지킨 코드 😊
interface Eater {
void eat();
}
interface Flyer {
void fly();
}
class Dog implements Eater {
public void eat() {
System.out.println("Dog eats");
}
}
class Bird implements Eater, Flyer {
public void eat() {
System.out.println("Bird eats");
}
public void fly() {
System.out.println("Bird flies");
}
}
Python 코드
# ISP를 지키지 않은 코드 😥
class Animal:
def eat(self):
pass
def fly(self):
pass # 모든 동물이 날 수는 없음
class Dog(Animal):
def eat(self):
print("Dog eats")
def fly(self):
raise NotImplementedError("Dogs cannot fly") # 강아지는 날 수 없어요!
# ISP를 지킨 코드 😊
class Eater:
def eat(self):
pass
class Flyer:
def fly(self):
pass
class Dog(Eater):
def eat(self):
print("Dog eats")
class Bird(Eater, Flyer):
def eat(self):
print("Bird eats")
def fly(self):
print("Bird flies")
5. DIP (Dependency Inversion Principle) - 의존성 역전 원칙
- 쉽게 말하면: "고수준 모듈은 저수준 모듈에 의존하지 않고, 둘 다 추상화된 인터페이스에 의존해야 해요."
📌 이를 통해 코드를 더 유연하고 확장 가능하게 만들 수 있어요.
예시: "알림 기능 구현"
Java 코드
// DIP를 지키지 않은 코드 😥
class EmailService {
public void sendEmail(String message) {
System.out.println("Email sent: " + message);
}
}
class Notification {
private EmailService emailService;
public Notification() {
this.emailService = new EmailService(); // EmailService에 의존
}
public void notify(String message) {
emailService.sendEmail(message);
}
}
// DIP를 지킨 코드 😊
interface MessageService {
void sendMessage(String message);
}
class EmailService implements MessageService {
public void sendMessage(String message) {
System.out.println("Email sent: " + message);
}
}
class SMSService implements MessageService {
public void sendMessage(String message) {
System.out.println("SMS sent: " + message);
}
}
class Notification {
private MessageService messageService;
public Notification(MessageService messageService) {
this.messageService = messageService; // 인터페이스에 의존
}
public void notify(String message) {
messageService.sendMessage(message);
}
}
Python 코드
# DIP를 지키지 않은 코드 😥
class EmailService:
def send_email(self, message):
print(f"Email sent: {message}")
class Notification:
def __init__(self):
self.email_service = EmailService() # EmailService에 의존
def notify(self, message):
self.email_service.send_email(message)
# DIP를 지킨 코드 😊
class MessageService:
def send_message(self, message):
pass
class EmailService(MessageService):
def send_message(self, message):
print(f"Email sent: {message}")
class SMSService(MessageService):
def send_message(self, message):
print(f"SMS sent: {message}")
class Notification:
def __init__(self, message_service):
self.message_service = message_service # 인터페이스에 의존
def notify(self, message):
self.message_service.send_message(message)
요약! 🌟
- SRP: 한 클래스는 하나의 일만!
- OCP: 기존 코드는 건드리지 말고, 새 기능은 확장!
- LSP: 자식 클래스는 부모를 완벽히 대체 가능해야!
- ISP: 필요 없는 기능은 구현하지 말자!
- DIP: 구체적인 구현 대신 인터페이스에 의존하자!
결론: SOLID 원칙을 지키면 코드는 더 유연하고, 읽기 쉽고, 수정하기 쉬워져요. ✨
반응형
SMALL
'객체지향 프로그래밍(OOP)' 카테고리의 다른 글
[쉬운설명] 함수형 프로그래밍 vs 객체지향 프로그래밍: 핵심 차이점 비교 (0) | 2024.12.18 |
---|---|
[Java] 의존성 주입 (Dependency Injection) 개념과 장점 쉽게 이해하기 (3) | 2024.12.18 |
[소프트웨어 설계] 디자인 패턴 완벽 정리: 싱글톤, 팩토리, 전략 패턴의 개념과 사용 사례 (0) | 2024.10.30 |
[Java] 인터페이스와 추상 클래스 차이점 완벽 정리: 개념, 특징과 예제 코드 비교 (0) | 2024.10.30 |
[객체지향 프로그래밍] OOP 4대 특성 완벽 정리: 캡슐화, 상속, 다형성, 추상화 개념과 예제 코드 (0) | 2024.10.30 |