Table of contents
Open Table of contents
단일 책임 원칙 (Single Responsibility Principle, SRP)
하나의 모듈은 하나의, 오직 하나의 액터(변경 이유)만을 책임져야한다.
class User {
fun save() {
// save user
}
fun delete() {
// delete user
}
fun sendEmail() {
// send email
}
}
위 코드는 단일 책임 원칙을 위반합니다.
User
클래스는 사용자의 정보를 저장하고 삭제하는 책임과 이메일을 보내는 책임을 가지고 있습니다.
이를 분리하면 아래와 같습니다.
class User {
fun save() {
// save user
}
fun delete() {
// delete user
}
}
class EmailSender {
fun sendEmail() {
// send email
}
}
개방-폐쇄 원칙 (Open/Closed Principle, OCP)
소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에 대해서는 열려 있어야 하지만, 수정에 대해서는 닫혀 있어야 한다.
class Rectangle {
fun draw() {
// draw rectangle
}
}
class Circle {
fun draw() {
// draw circle
}
}
위 코드는 개방-폐쇄 원칙을 위반합니다.
Rectangle
클래스와 Circle
클래스는 draw
메서드를 가지고 있습니다.
이를 개선하면 아래와 같습니다.
interface Shape {
fun draw()
}
class Rectangle : Shape {
override fun draw() {
// draw rectangle
}
}
class Circle : Shape {
override fun draw() {
// draw circle
}
}
리스코프 치환 원칙 (Liskov Substitution Principle, LSP)
서브 타입은 언제나 기반 타입으로 대체할 수 있어야 한다.
open class Bird {
open fun fly() {
println("Bird is flying")
}
}
class Ostrich : Bird() {
override fun fly() {
throw UnsupportedOperationException("Ostrich can't fly")
}
}
위 코드는 리스코프 치환 원칙을 위반합니다.
Ostrich
클래스는 Bird
클래스를 상속받았지만, fly
메서드를 오버라이딩하여 Ostrich
클래스는 Bird
클래스의 서브 타입이 아닙니다.
이를 개선하면 아래와 같습니다.
open class Bird {
open fun fly() {
println("Bird is flying")
}
}
class Ostrich : Bird() {
override fun fly() {
println("Ostrich can't fly")
}
}
인터페이스 분리 원칙 (Interface Segregation Principle, ISP)
클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안된다.
interface Worker {
fun work()
fun eat()
}
class Programmer : Worker {
override fun work() {
println("Programmer is working")
}
override fun eat() {
println("Programmer is eating")
}
}
class Waiter : Worker {
override fun work() {
println("Waiter is working")
}
override fun eat() {
throw UnsupportedOperationException("Waiter can't eat")
}
}
위 코드는 인터페이스 분리 원칙을 위반합니다.
Waiter
클래스는 eat
메서드를 구현하지만, Waiter
클래스는 eat
메서드를 사용하지 않습니다.
이를 개선하면 아래와 같습니다.
interface Workable {
fun work()
}
interface Eatable {
fun eat()
}
class Programmer : Workable, Eatable {
override fun work() {
println("Programmer is working")
}
override fun eat() {
println("Programmer is eating")
}
}
class Waiter : Workable {
override fun work() {
println("Waiter is working")
}
}
의존 역전 원칙 (Dependency Inversion Principle, DIP)
고수준 모듈은 저수준 모듈에 의존해서는 안된다. 둘 다 추상화에 의존해야 한다. 또한, 추상화는 세부사항에 의존해서는 안된다. 세부사항이 추상화에 의존해야 한다.
class Light {
fun turnOn() {
println("Light is on")
}
fun turnOff() {
println("Light is off")
}
}
class Switch {
private val light = Light()
fun on() {
light.turnOn()
}
fun off() {
light.turnOff()
}
}
위 코드는 의존 역전 원칙을 위반합니다.
Switch
클래스는 Light
클래스에 의존하고 있습니다.
이를 개선하면 아래와 같습니다.
interface Switchable {
fun turnOn()
fun turnOff()
}
class Light : Switchable {
override fun turnOn() {
println("Light is on")
}
override fun turnOff() {
println("Light is off")
}
}
class Switch(private val switchable: Switchable) {
fun on() {
switchable.turnOn()
}
fun off() {
switchable.turnOff()
}
}