Refatoring을 입에 달고 사는 개발자들
개발에서는 언제나 리팩토링이라는 단어나 언급된다. 처음엔 잘 짜여진 코드라 할 지라도 시간이 지나 API가 변경이되고, 요구사항이 추가 혹은 삭제되기도 하면서 결국엔 레거시가 되어버리기 때문이다. 어쩌면 좋은 코드라는 건 시간에 따라 바뀌는 트렌드와 같다. 개발자는 그 트렌드에 맞추기 위해 고군분투하며 리팩토링을 외칠 수 밖에 없다. 그리고 리팩토링할 때 아래와 같은 개념으르 알아두면 좋다.
SOLID 원칙
프로그래머가 유지 보수와 확장성이 좋은 소프트웨어를 만들고자 할 때, 적용하면 좋은 원칙이다. Robert C. Martin 아저씨가 명명한 내용이다. 각 앞글자가 의미하는 문구는 아래 표와 같다.
SRP (Single responsibility principle) | 단일 책임 원칙 |
OCP (Open/Closed principle) | 개방-폐쇄 원칙 |
LSP (Liskov substitution principle) | 리스코프 치환 원칙 |
ISP (Interface segregation principle) | 인터페이스 분리 원칙 |
DIP (Dependency inversion principle) | 의존 관계 역전 원칙 |
SPR(Single Responsibility Principle)
단일 책임 원칙이다. 하나의 클래스는 하나의 책임만을 가져야 한다는 내용이다.
예를 들어, 아래와 같이 책을 나타내는 클래스가 있다. 여기서 책을 구매한 날짜에 해당하는 purchagedDate는 SRP를 위반하는 변수이다. 책을 구매한 구매자(Reader)나 구매자의 책장(Shelves)에 귀속되는 게 더욱 적당할 것이다.
class Book {
let price: Int
let title: String
let author: String
let purchacedDate: Date // violation SRP
}
OCP(Open-Closed Principle)
개방-폐쇠의 원칙이다. 확장에는 열리고, 변경에는 닫혀있어야 한다는 내용이다.
아래 예제는 구매정보(PurchasedInformation)에 신문 이나 eBook 을 추가 하는 경우에 대해 닫혀있다.
class Book {
let price: Int
let title: String
let author: String
}
class PurchasedInformation {
let book: Book
let price: Int
let branch: String
let purchasedDate: Date
}
// want to add newspaper
class Newspaper {
let price: Int
let title: String
let date: Date
}
아래와 같이 Readable을 상위 인터페이스로 연결하면 새로운 읽을 것들은 언제든지 추가 할 수 있으면서(Open), 이를 처리하는 클래스는 변경하지 않아도 된다(Closed).
protocol Readable {
var price: Int
var title: String
}
class Book: Readable {
let price: Int
let title: String
let author: String
}
class Newspaper: Readable {
let price: Int
let title: String
let date: Date
}
class PurchasedInformation {
let readingMeterial: Readable
let price: Int
let branch: String
let purchasedDate: Date
}
LSP(Liskov Substitution Principle)
리스코프 치환 원칙이다. 프로그램의 정확성을 깨트리지 않으면서 상위 타입을 하위 타입의 인터페이스로 바꿀 수 있어야 한다. 정사각형은 직사각형이 될 수 있지만, 직사각형은 정사각형이 될 수 없는 것과 같은 논리 이다. 예제에서도 책은 읽을 거리가 될 수 있지만, 읽을 거리가 책인 건 아니다. 즉, 책은 "읽은 거리"가 되기 위한 모든 조건을 갖춰야 한다는 뜻이다.
ISP(Interface Segregation Principle)
인터페이스 분리 원칙이다. 여러개의 범용 인터페이스를 사용하는 대신 하나의 특정 클라이언트를 위한필요한 정보만 모아서 하나의 인터페이스를 만들라는 내용이다. 필요한 메소드를 가진 인터페이스를 이곳저곳에서 상속 받다보면, 무의미한 메소드와 정보가 많아지기 때문이다.
DIP(Dependency Inversion Principle)
의존관계 역전 원칙이다. 프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다. 대표적인 방법이 바로 의존성 주입(DI)이다. 의존관계를 맺을 때 잘 변할 가능성이 없는 객체에 의존을 해야 한다는 뜻이다.
지금까지 예를 들고 있는 Book-Reader 관계에서는 Book보다 Reader가 변할 가능성이 더 높은 객체이다. 그런데 책이 아닌 신문, eBook 형태의 읽을 거리가 추가 되면, Book이 아닌 Reader의 코드가 변경되어야 한다. Reader 가 구체와 된 Book 객체가 아닌 추상화된 Readable을 의존하도록 한다. 그러면 읽을 거리가 변화할 때마다 Reader가 변할 필요가 없다. (결국 OCP 예제 코드와 같아짐.)
참고하면 좋을 책
- 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴