SOLID 원칙:
SOLID는 객체 지향 프로그래밍에서 유지보수성과 확장성을 높이기 위한 다섯 가지 설계 원칙이라고 한다.
Swift에서도 이 원칙들을 적용해 클린하고 유연한 코드 구조를 만드는 방법에 대해서 공부하기위해 포스팅!
SRP - 단일 책임 원칙 (Single Responsibility Principle)
하나의 클래스는 하나의 책임만 가져야 한다.
- 하나의 객체는 오직 하나의 변화 이유만 가져야 함.
- 그동안 이 부분은 많이 간과하고 작업을 했었는데.. 조금 더 쪼개면서 책임을 최대한 분리하도록 노력 필요!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class ReportDataLoader { func load() -> ReportData { ... } }
class PDFFormatter { func format(data: ReportData) -> PDFFile { ... } }
class PDFReportGenerator { let loader = ReportDataLoader() let formatter = PDFFormatter()
func generateReport() { let data = loader.load() let pdf = formatter.format(data: data) } }
|
OCP - 개방-폐쇄 원칙 (Open/Closed Principle)
확장에는 열려 있고, 변경에는 닫혀 있어야 한다.
- 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있어야 함
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| protocol Payment { func pay(amount: Double) }
class CreditCard: Payment { func pay(amount: Double) { print("Paid via card") } }
class Paypal: Payment { func pay(amount: Double) { print("Paid via Paypal") } }
func processPayment(payment: Payment) { payment.pay(amount: 100.0) }
|
LSP - 리스코프 치환 원칙 (Liskov Substitution Principle)
자식 클래스는 언제나 부모 클래스를 대체할 수 있어야 한다.
- 하위 타입은 상위 타입으로 교체해도 프로그램이 깨지지 않아야 함
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Animal { func makeSound() { print("Animal sound") } }
class Dog: Animal { override func makeSound() { print("Bark") } }
func playSound(animal: Animal) { animal.makeSound() }
|
ISP - 인터페이스 분리 원칙 (Interface Segregation Principle)
클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 된다.
- 큰 프로토콜보다는 작은 프로토콜을 여러 개로 나누는 것이 좋음
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| protocol Worker { func work() func eat() }
protocol Workable { func work() }
protocol Eatable { func eat() }
class Engineer: Workable, Eatable { func work() { ... } func eat() { ... } }
class Robot: Workable { func work() { ... } }
|
DIP - 의존성 역전 원칙 (Dependency Inversion Principle)
상위 모듈은 하위 모듈에 의존하면 안 되고, 둘 다 추상화에 의존해야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| protocol MessageSender { func send() }
class EmailSender: MessageSender { func send() { print("Send Email") } }
class NotificationService { let sender: MessageSender
init(sender: MessageSender) { self.sender = sender }
func notify() { sender.send() } }
|
요약
원칙 |
설명 |
Swift 적용 예 |
SRP |
하나의 책임만 가져야 함 |
UI와 데이터 로직 분리 |
OCP |
기능 확장은 OK, 코드 수정은 NO |
프로토콜로 추상화 |
LSP |
자식은 부모를 대체 가능해야 함 |
상속과 재정의 |
ISP |
인터페이스는 최소 단위로 |
프로토콜 분리 |
DIP |
추상화에 의존 |
의존성 주입(DI) 활용 |