Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- ios
- UICollectionView
- Observable
- 리펙토링
- Refactoring
- map
- Human interface guide
- SWIFT
- tableView
- combine
- HIG
- 스위프트
- RxCocoa
- clean architecture
- Xcode
- 클린 코드
- 리팩토링
- swiftUI
- collectionview
- uiscrollview
- 리펙터링
- Clean Code
- uitableview
- UITextView
- 애니메이션
- rxswift
- Protocol
- swift documentation
- ribs
- MVVM
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[Refactoring] 9-4 조건부 로직 최소화 (조건부 로직을 다형성으로 바꾸기) 본문
조건부 로직을 다형성으로 바꾸기
- 조건부 로직이 복잡해지면 수정하기가 굉장히 어려워지므로, 더 높은 수준의 개념인 다형성을 사용하여 조건들을 분리하는 방법
- switch 문 안의 case가 여러개 있을 때 case별로 클래스를 하나씩 만들어서 공통 switch 로직의 중복을 제거하는 것이 목표
- 특히 switch case로 나누어진 분기 부분들에서 조건부 로직을 자신만의 방식으로 처리하도록 구성할 때, 다형성을 사용하여 각 클래스에서 처리하도록하면 SRP(Single Responsibility Principle) 원칙도 지키는 효과
조건부 로직을 다형성으로 바꾸기 예시
- 리펙토링 전)
- 사용자가 맥주를 고르고 이 맥주의 가격을 측정하는 프로그램
- 맥주의 종류는 enum으로 정의된 상태
- 각 맥주의 가격이 각각 다름
enum Beer {
case hoganda
case hite
case heineken
}
let beer = Beer.hite
var price = 0
var taxFee = 0
var premium = false
switch beer {
case .hoganda:
print("this is hoganda")
price = 1000
taxFee = 300
case .hite:
print("this is hite")
price = 500
case .heineken:
print("this is heineken")
price = 2000
if premium {
taxFee = 1000
} else {
taxFee = 500
}
}
let result = price + taxFee
print("result:", result) // 500
- 현재 문제점
- switch의 가장 단점인, 하나의 코드가 여러개의 case들을 책임지는 상태이므로 단일 책임 원칙(SRP)을 지키지 못하고 있음
- 조건식 안에가 길어지고 있기 때문에 코드가 쉽게 읽히지 않는 상태
- 다형성으로 리펙토링
- 수퍼 클래스가 될 MyBeer 구현
- MyBeer를 서브 클래싱하여 getPrice(), getTax()만 오버라이딩하고 사용하는쪽에서는 total()을 호출하여 원하는 값을 획득하도록 구현
class MyBeer {
func getPrice() -> Int {
0
}
func getTax() -> Int {
0
}
func total() -> Int {
getPrice() + getTax()
}
}
- Hoganda, Hite, Heineken모두 MyBeer를 서브클래싱하여 구현
class MyBeer {
func getPrice() -> Int {
0
}
func getTax() -> Int {
0
}
func total() -> Int {
getPrice() + getTax()
}
}
class Hoganda: MyBeer {
init?(beer: Beer) {
guard beer == .hoganda else { return nil }
print("this is hoganda")
}
override func getPrice() -> Int {
1000
}
override func getTax() -> Int {
300
}
}
class Hite: MyBeer {
init?(beer: Beer) {
guard beer == .hite else { return nil }
print("this is hite")
}
override func getPrice() -> Int {
500
}
}
class Heineken: MyBeer {
private let premium: Bool
init?(beer: Beer, premium: Bool) {
guard beer == .heineken else { return nil }
print("this is heineken")
self.premium = premium
}
override func getPrice() -> Int {
2000
}
override func getTax() -> Int {
premium ? 1000 : 500
}
}
- switch문도 사용하는쪽에서는 상관하지 않아도 되므로, switch만 신경쓰는 컴포넌트를 생성
- 생성자가 필요 없기 때문에 enum타입의 빌더를 정의
enum BeerBuilder {
static func createBeer(_ beerType: Beer) -> MyBeer? {
switch beerType {
case .hoganda:
return Hoganda(beer: beer)
case .hite:
return Hite(beer: beer)
case .heineken:
return Heineken(beer: beer)
default:
return nil
}
}
}
- 사용하는쪽코드
- 사용하는쪽에서는 switch문도 신경쓰지 않아도 되고 Builder에서 각각의 케이스 문에 대한 일을 MyBeer 서브클래싱에서 처리하기 때문에 코드를 수정할 때 개발자는 고려해야하는 범위를 줄여주는 장점이 있는 좋은 코드로 탄생
let result2 = BeerBuilder.createBeer(beer)?.total()
print("result:", result2) // 500
* 조건부 로직을 상속을 사용하여 리펙토링 했지만 더욱 리펙토링을 잘 하려면 상속보다는 Interface인 protocol을 사용하는게 더욱 적합한데, 이 글은 다음 포스팅 글, 조건부 로직을 protocol로 리펙토링하기에서 계속
* 전체 코드: https://github.com/JK0369/ExRefactoring9_4
* 참고
- Refactoring (Martin Flowler)
'Refactoring (리펙토링)' 카테고리의 다른 글
[Refactoring] 9-6 조건부 로직 최소화 (Assertion 추가하기) (0) | 2023.06.05 |
---|---|
[Refactoring] 9-5 조건부 로직 최소화 (조건부 로직을 protocol로 리펙토링하기) (0) | 2023.06.04 |
[Refactoring] 9-3 조건부 로직 최소화 (중첩 조건문을 보호 구문으로 바꾸기, swift에서 guard문의 의미) (0) | 2023.06.02 |
[Refactoring] 9-2. 조건부 로직 최소화 (조건문 통합하기) (0) | 2023.05.29 |
[Refactoring] 9-1. 조건부 로직 최소화 (조건문 분해하기) (0) | 2023.05.27 |
Comments