SOLID 원칙
2025. 1. 26. 15:52ㆍiOS
반응형
SOLID 원칙은 객체 지향 프로그래밍에서 소프트웨어 설계의 품질을 개선하기 위한 다섯 가지 원칙의 약어입니다. 이 원칙들은 유지보수성과 확장성을 높이며, 더 나은 코드 구조를 만드는 데 도움을 줍니다.
1. S - 단일 책임 원칙 (Single Responsibility Principle)
설명:
- 클래스는 하나의 책임만 가져야 합니다.
- 변경이 필요할 때는 단 하나의 이유만 있어야 합니다.
이점:
- 코드를 쉽게 이해하고 수정할 수 있습니다.
- 한 기능의 변경이 다른 기능에 영향을 주지 않게 합니다.
예시:
class ReportGenerator {
func generateReport() {
// 리포트 생성 로직
}
}
class ReportPrinter {
func printReport() {
// 리포트 출력 로직
}
}
위 코드에서 ReportGenerator와 ReportPrinter는 각기 다른 책임을 맡습니다.
2. O - 개방/폐쇄 원칙 (Open/Closed Principle)
설명:
- 소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 합니다.
- 기존 코드를 수정하지 않고 기능을 추가해야 합니다.
이점:
- 새로운 기능을 추가할 때 기존 코드가 영향을 받지 않습니다.
- 버그 발생 가능성이 줄어듭니다.
예시:
protocol Shape {
func draw()
}
class Circle: Shape {
func draw() {
print("Drawing Circle")
}
}
class Square: Shape {
func draw() {
print("Drawing Square")
}
}
func renderShapes(shapes: [Shape]) {
shapes.forEach { $0.draw() }
}
새로운 도형을 추가하려면 Shape 프로토콜을 구현하는 클래스만 추가하면 됩니다.
3. L - 리스코프 치환 원칙 (Liskov Substitution Principle)
설명:
- 자식 클래스는 부모 클래스에서 정의된 기능을 그대로 사용할 수 있어야 하며, 부모 클래스를 대체해도 문제가 없어야 합니다.
이점:
- 코드 재사용성을 높이고 예측 가능한 동작을 보장합니다.
예시:
class Bird {
func fly() {
print("Flying")
}
}
class Sparrow: Bird {}
class Penguin: Bird {
override func fly() {
fatalError("Penguins can't fly")
}
}
위 코드에서 Penguin은 부모 클래스의 동작을 깨트리므로 리스코프 치환 원칙을 위반합니다. 이를 수정하려면 Bird 클래스를 추상화하여 FlyingBird와 NonFlyingBird로 나누는 것이 좋습니다.
4. I - 인터페이스 분리 원칙 (Interface Segregation Principle)
설명:
- 인터페이스는 클라이언트가 필요로 하는 기능만 제공해야 하며, 불필요한 메서드나 기능을 강요하지 않아야 합니다.
이점:
- 클라이언트는 자신이 사용하지 않는 기능에 의존하지 않습니다.
- 불필요한 코드 변경을 피할 수 있습니다.
예시:
protocol Printer {
func printDocument()
}
protocol Scanner {
func scanDocument()
}
class MultiFunctionPrinter: Printer, Scanner {
func printDocument() {
print("Printing document")
}
func scanDocument() {
print("Scanning document")
}
}
class SimplePrinter: Printer {
func printDocument() {
print("Printing document")
}
}
위 코드에서는 필요한 기능만 인터페이스로 구현합니다.
5. D - 의존성 역전 원칙 (Dependency Inversion Principle)
설명:
- 고수준 모듈(정책 결정)은 저수준 모듈(세부 사항)에 의존해서는 안 됩니다. 둘 다 추상화에 의존해야 합니다.
- 추상화는 구체적인 사항에 의존하지 않아야 합니다.
이점:
- 모듈 간 결합도를 낮춥니다.
- 유연성과 테스트 용이성을 높입니다.
예시:
protocol NotificationService {
func send(message: String)
}
class EmailNotification: NotificationService {
func send(message: String) {
print("Sending Email: \(message)")
}
}
class SMSNotification: NotificationService {
func send(message: String) {
print("Sending SMS: \(message)")
}
}
class NotificationManager {
private let service: NotificationService init(service: NotificationService) {
self.service = service
}
func notify(message: String) {
service.send(message: message)
}
}
// Usage
let emailService = EmailNotification()
let manager = NotificationManager(service: emailService)
manager.notify(message: "Hello!")
NotificationManager는 구체적인 구현(EmailNotification 또는 SMSNotification)에 의존하지 않고 추상화된 NotificationService에 의존합니다.
결론
SOLID 원칙을 준수하면 코드가 더 읽기 쉽고, 유지보수하기 쉬우며, 변화에 강한 구조로 설계됩니다. 이러한 원칙은 각각 독립적으로 적용되지만, 함께 사용하면 더욱 강력한 설계 지침을 제공합니다.
반응형
'iOS' 카테고리의 다른 글
[iOS] SwiftUI와 Combine (0) | 2025.01.28 |
---|---|
[iOS] SwiftUI와 MVVM (1) | 2025.01.27 |
[iOS] 유니버설 링크(Universal Links) - 딥 링크(Deep Links) (1) | 2025.01.26 |
[iOS] State Restoration(상태 복원) shouldSaveApplicationState (1) | 2025.01.26 |
[iOS] WebView 백그라운드 시 앱 새로고침 이슈 (1) | 2025.01.25 |