Search
Duplicate
๐Ÿ“˜

Refactoring 1/E, 2/E - Martin Fowler

2nd Edition

๊ฐ€. ๋ฆฌํŒฉํ„ฐ๋ง ์ฃผ์š” ์›์น™

1) ๋ฆฌํŒฉํ„ฐ๋ง์ด๋ž€

โ€ข
๋ฆฌํŒฉํ„ฐ๋ง์ด๋ž€ ์ฝ”๋“œ์˜ ์™ธ๋ถ€ ๋™์ž‘์€ ๋™์ผํ•˜์ง€๋งŒ ๋‚ด๋ถ€์˜ ๊ตฌ์กฐ๋ฅผ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ฆฌํŒฉํ„ฐ๋ง์„ ํ•˜๋ฉด ์ฝ”๋“œ ์ž‘์„ฑ ํ›„์— ์ฒด๊ณ„์ ์œผ๋กœ ์„ค๊ณ„๋œ ์ฝ”๋“œ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฆฌํŒฉํ„ฐ๋ง์— ๋”ฐ๋ผ ์„ค๊ณ„๊ฐ€ ๊ฐœ์„ ๋˜๋ฉด ๊ฒฐ๊ณผ์ ์œผ๋กœ ๊ฐœ๋ฐœ๋น„์šฉ ๊ฐ์†Œ์— ๋”ฐ๋ฅธ ๊ฒฝ์ œ์  ํšจ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
โ€ข
๋ฆฌํŒฉํ„ฐ๋ง vs ์„ฑ๋Šฅ๊ฐœ์„ 
: ๋‘ ์ ‘๊ทผ ๋ชจ๋‘ ์†Œํ”„ํŠธ์›จ์–ด์˜ ๋‚ด๋ถ€ ๊ตฌ์กฐ์— ๋ณ€ํ™”๋ฅผ ์ฃผ์ง€๋งŒ ์™ธ๋ถ€ ๋™์ž‘ ๋ฐฉ์‹์—๋Š” ๋ณ€ํ™”๋ฅผ ์ฃผ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
: ๋ฆฌํŒฉํ„ฐ๋ง์˜ ๋ชฉ์ ์€ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์‰ฝ๊ฒŒ ์ฝ๊ณ  ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด ์„ฑ๋Šฅ๊ฐœ์„ ์˜ ๋ชฉ์ ์€ ์†Œํ”„ํŠธ์›จ์–ด์˜ ์„ฑ๋Šฅ์„ ๋†’์ด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ฆฌํŒฉํ„ฐ๋ง์— ๋”ฐ๋ผ ์ฝ”๋“œ๊ฐ€ ์ •๋ฆฌ๋œ ํ›„์— ์„ฑ๋Šฅ๊ฐœ์„  ์ž‘์—…์„ ํ•˜๋Š” ๊ฒƒ์„ ๊ถŒํ•ฉ๋‹ˆ๋‹ค. ์„ฑ๋Šฅ๊ฐœ์„ ๋งŒ์„ ๋ชฉ์ ์œผ๋กœ ์ž‘์—…ํ•˜๋‹ค๋ณด๋ฉด ์ข…์ข… ์ฝ”๋“œ๊ฐ€ ๋”์šฑ ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2) ๋ฆฌํŒฉํ„ฐ๋ง์˜ ํšจ๊ณผ

โ€ข
๋‹ค์–‘ํ•œ ๋ณ€ํ™”์— ๋Œ€์‘ํ•  ์žˆ๋Š” ๊ตฌ์กฐ๋ฅผ ๋‹ค์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
: ์ž˜ ์„ค๊ณ„๋œ ์†Œํ”„ํŠธ์›จ์–ด๋„ ์š”๊ตฌ์‚ฌํ•ญ ๋ณ€๊ฒฝ์— ๋”ฐ๋ผ ์ฝ”๋“œ๊ฐ€ ์ง€์† ์ˆ˜์ •๋˜๋ฉด, ์‹œ์Šคํ…œ์˜ ๋ฌด๊ฒฐ์„ฑ์ด ๋ง๊ฐ€์ง€๊ณ  ๊ธฐ์กด ์„ค๊ณ„๊ฐ€ ๋ฌด๋„ˆ์ง€๊ธฐ ๋งˆ๋ จ์ž…๋‹ˆ๋‹ค.
: ์„ค๊ณ„ ์ž‘์—…์ด ์™„๋ฃŒ๋˜์–ด๋„ ๊ตฌํ˜„์— ๋“ค์–ด๊ฐ€๋ฉด ํ•„์—ฐ์ ์œผ๋กœ ๊ธฐ์กด ์„ค๊ณ„์™€ ๋‹ค๋ฅธ ๋ถ€๋ถ„์ด ์ƒ๊น๋‹ˆ๋‹ค.
โ€ข
๊ตฌํ˜„ ์‹œ ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ๋ฐ”๋กœ ์žก๋Š” ๊ฒƒ์ด ์ƒ๋Œ€์ ์œผ๋กœ ์‰ฝ์Šต๋‹ˆ๋‹ค.
: ๋ฆฌํŒฉํ„ฐ๋ง์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์€ ์กฐ๊ฐ์œผ๋กœ ๋งŒ๋“ค๊ธฐ ๋•Œ๋ฌธ์— ์—๋Ÿฌ ๋ฐœ์ƒ๋ถ€๋ถ„์— ๋Œ€ํ•ด ์ƒ๋Œ€์ ์œผ๋กœ ์‰ฝ๊ฒŒ ํฌ์ฐฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
โ€ข
์†Œํ”„ํŠธ์›จ์–ด ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ์„ ๋‚ฎ์ถฅ๋‹ˆ๋‹ค.
: ํ˜‘์—…์ž๊ฐ€ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ณ  ์‰ฝ๊ฒŒ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํŒ€ ๋‹จ์œ„์˜ ๋Šฅ๋ฅ ์„ ๋†’์ž…๋‹ˆ๋‹ค.
: ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ ๊ตฌ์กฐ๊ฐ€ ๋ง๊ฐ€์ง€๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์†Œํ”„ํŠธ์›จ์–ด

3) ๋ฆฌํŒฉํ„ฐ๋ง์ด ํŠนํžˆ ํ•„์š”ํ•œ ์‹œ์ 

๋ฆฌํŒฉํ„ฐ๋ง์€ ํ•ญ์ƒ ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ฆฌํŒฉํ„ฐ๋ง๋งŒ์„ ์œ„ํ•ด ๋”ฐ๋กœ ์‹œ๊ฐ„์„ ๋‚ด์„œ ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค.
โ€ข
์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋Š” ์‹œ์ ์— ๋”์šฑ ์‹ ๊ฒฝ ์จ์„œ ๋ฆฌํŒฉํ„ฐ๋ง์„ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
: ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์—๋Ÿฌ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค. ๋ฆฌํŒฉํ„ฐ๋งํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ๋ช…ํ™•ํžˆ ๋งŒ๋“ค๋ฉด ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€์— ๋”ฐ๋ฅธ ์—๋Ÿฌ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ์„ ๋‚ฎ์ถœ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
โ€ข
ํ•ด๊ฒฐ์ด ์•ˆ๋˜๋Š” ๋ฒ„๊ทธ๊ฐ€ ์žˆ์„ ๋•Œ ๋ฆฌํŒฉํ„ฐ๋งํ•˜๋ฉด ๋ฒ„๊ทธ์˜ ์œ„์น˜๋ฅผ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
: ๋Œ€์ฒด๋กœ ๋ฒ„๊ทธ์˜ ์œ„์น˜๋ฅผ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์—†๋Š” ์ด์œ ๋Š” ์ฝ”๋“œ๋ฅผ ์‰ฝ๊ฒŒ ์ฝ์„ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
โ€ข
ํŒ€์›์—๊ฒŒ ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ๋ฐ›๊ธฐ ์ „์— ๋ฆฌํŒฉํ„ฐ๋งํ•˜๋ฉด ๋”์šฑ ๊ฐ€์น˜ ์žˆ๋Š” ์ฝ”๋ฉ˜ํŠธ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
: ๋‚จ์˜ ์ฝ”๋“œ๋ฅผ ์ฝ๋Š” ๊ฒƒ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ํž˜๋“ค๊ณ  ๋•Œ๋กœ๋Š” ๊ท€์ฐฎ์€ ์ผ์ž…๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋ฆฌ๋ทฐ ๋Œ€์ƒ์ด ๋˜๋Š” ์ฝ”๋“œ๊ฐ€ ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌ๋˜์–ด ์žˆ๋‹ค๋ฉด, ๋”์šฑ ์›ํ™œํ•˜๊ณ  ์ƒ์‚ฐ์ ์ธ ์ฝ”๋ฉ˜ํŠธ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜. Bad Smells in Code

๋ฆฌํŒฉํ„ฐ๋ง์ด ์ ˆ์‹คํ•œ ์ฝ”๋“œ์—๋Š” ์ผ์ •ํ•œ ํŒจํ„ด์ด ์žˆ์Šต๋‹ˆ๋‹ค.

1) ๊ธฐ์ดํ•œ ์ด๋ฆ„(Mysterious Name)

โ€ข
์ฝ”๋“œ์˜ ์ด๋ฆ„์ด ๋‹จ์ˆœ ๋ช…๋ฃŒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

2) ์ค‘๋ณต ์ฝ”๋“œ(Duplicated Code)

โ€ข
๋™์ผํ•œ ์ฝ”๋“œ ์กฐ๊ฐ์ด ์—ฌ๋Ÿฌ ๊ณณ์— ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
fun calculateSalesTax(amount: Double): Double { return amount * 0.08 } fun calculateServiceTax(amount: Double): Double { return amount * 0.08 }
Kotlin
๋ณต์‚ฌ

3) ๊ธด ํ•จ์ˆ˜(Long Function)

โ€ข
ํ•˜๋‚˜์˜ ํ•จ์ˆ˜์— ๋„ˆ๋ฌด ๊ธด ๋กœ์ง์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
fun calculateTotal(cart: List<Product>): Double{ var total = 0.0 // caculate sub tottal for (product in cart) { total += product.price } // apply discount if (total > 100) { total -= total * 0.1 } // add sales tax val tax = total * 0.08 total += tax return total }
Kotlin
๋ณต์‚ฌ

4) ๊ธด ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ชฉ๋ก (Long Parameter List)

โ€ข
ํ•จ์ˆ˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜์— ๋„ˆ๋ฌด ๋งŽ์€ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

5) ์ „์—ญ ๋ฐ์ดํ„ฐ(Global Data)

โ€ข
์ฝ”๋“œ๋ฒ ์ด์Šค์˜ ๋ชจ๋“  ๊ณณ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์ „์—ญ ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

6) ๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ(Mutable Data)

โ€ข
๋ณ€๊ฒฝ์— ๋”ฐ๋ผ ๋ถ€์ˆ˜์ž‘์šฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

7) ๋’ค์—‰ํ‚จ ๋ณ€๊ฒฝ (Divergent Change)

โ€ข
์—ฌ๋Ÿฌ ๊ด€์‹ฌ์‚ฌ์— ๋”ฐ๋ผ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๊ฐ€ ์ž์ฃผ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.
data class Address(var street: String, var city: String, var state: String, var zip: String) data class Order(var id: Int, var product: String, var quantity: Int) class UserProfile { var username: String = "" var email: String = "" var address: Address = Address("", "", "", "") var orders: MutableList<Order> = mutableListOf() // Method related to user profile management fun changeUsername(newUsername: String) { username = newUsername } // Method related to address management fun changeAddress(newAddress: Address) { address = newAddress } // Method related to order management fun placeOrder(newOrder: Order) { orders.add(newOrder) } }
Kotlin
๋ณต์‚ฌ

8) ์‚ฐํƒ„์ด ์ˆ˜์ˆ  (Shotgun Surgery)

โ€ข
๋™์ผํ•œ ์ฝ”๋“œ ์กฐ๊ฐ์ด ์—ฌ๋Ÿฌ ๊ด€์‹ฌ์‚ฌ์— ๋ถ„์‚ฐ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
class OrderConfirmation { fun sendOrderConfirmationEmail(order: Order) { // Send order confirmation email } } class OrderHistory { fun sendOrderConfirmationEmail(order: Order) { // Send order confirmation email } } class OrderStatus { fun sendOrderConfirmationEmail(order: Order) { // Send order confirmation email } }
Kotlin
๋ณต์‚ฌ

9) ๊ธฐ๋Šฅ ํŽธ์•  (Feature Envy)

โ€ข
๊ฐ์ฒด ๋‚ด ์ผ๋ถ€ ์†์„ฑ์ด ๋‹ค๋ฅธ ๊ฐ์ฒด์—์„œ ์ž์ฃผ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
class Customer( val name: String, val age: Int, val address: Address ) class Order( val customer: Customer, val items: List<Item> ) { fun calculateTotal(): Double {} { /*...*/ } fun printCustomerDetails() { println("Customer Name: ${customer.name}") println("Customer Age: ${customer.age}") println("Customer Address: ${customer.address}") } }
Kotlin
๋ณต์‚ฌ

10) ๋ฐ์ดํ„ฐ ๋ญ‰์น˜ (Data Clumps)

โ€ข
๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋Š” ๋ฐ์ดํ„ฐ์˜ ์กฐํ•ฉ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
class Order( private val orderNumber: String, private val customerName: String, private val customerEmail: String, private val customerAddress: Address, private val items: List<Item> ) { // ... other methods and logic ... } class Address( val street: String, val city: String, val state: String, val zipCode: String ) class Item( val name: String, val price: Double )
Kotlin
๋ณต์‚ฌ

11) ๊ธฐ๋ณธํ˜• ์ง‘์ฐฉ (Primitive Obsession)

โ€ข
์š”๊ตฌ์‚ฌํ•ญ์„ ๋งŒ์กฑํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ์ •์˜ํ•˜์ง€ ์•Š๊ณ  ๊ธฐ๋ณธํ˜• ํƒ€์ž…์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
data class User( val name: String, val email: String, val phoneNumber: String )
Kotlin
๋ณต์‚ฌ
๊ฐœ์„ 

12) ๋ฐ˜๋ณต๋˜๋Š” switch๋ฌธ (Repeated Switches)

โ€ข
์ผ€์ด์Šค๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค๋ฉด switch๋ฌธ์ด ์กด์žฌํ•˜๋Š” ๋ชจ๋“  ๊ณณ์—์„œ ์—ฐ๊ด€ ์ผ€์ด์Šค๋ฅผ ํ•จ๊ป˜ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
enum class AnimalType { DOG, CAT, BIRD } class Animal(val type: AnimalType) { fun makeSound() { when (type) { AnimalType.DOG -> println("Woof!") AnimalType.CAT -> println("Meow!") AnimalType.BIRD -> println("Tweet!") } } fun eat() { when (type) { AnimalType.DOG -> println("Dog is eating...") AnimalType.CAT -> println("Cat is eating...") AnimalType.BIRD -> println("Bird is eating...") } } }
Kotlin
๋ณต์‚ฌ
๊ฐœ์„ 

13) ๋ฐ˜๋ณต๋ฌธ(Loops)

โ€ข
์ฝ”๋“œ์˜ ์˜๋„๋ฅผ ์‰ฝ๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์—†๋Š” ๋ฐ˜๋ณต๋ฌธ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
fun sumOfPositiveNumbers(numbers: List<Int>): Int { var total = 0 for (number in numbers) { if (number > 0) { total += number } } return total }
Kotlin
๋ณต์‚ฌ
๊ฐœ์„ 

14) ์„ฑ์˜ ์—†๋Š” ์š”์†Œ(Lazy Element)

โ€ข
๋นˆ์•ฝํ•œ ๊ธฐ๋Šฅ์œผ๋กœ ์ •์˜๋œ ๊ฐ์ฒด๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
class UserName(val name: String) fun printUserName(userName: UserName) { println(userName.name) } val user = UserName("John Doe") printUserName(user) // Outputs: John Doe
Kotlin
๋ณต์‚ฌ

15) ์ถ”์ธก์„ฑ ์ผ๋ฐ˜ํ™” (Speculative Generality)

โ€ข
์•ž์œผ๋กœ ํ•„์š”ํ•  ๊ฒƒ์ด๋ผ๊ณ  ์ถ”์ธกํ•˜์—ฌ ๋ฏธ๋ฆฌ ๊ตฌํ˜„ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
interface PaymentMethod { fun processPayment(amount: Double) } class CreditCardPayment : PaymentMethod { override fun processPayment(amount: Double) { // ... code to process credit card payment ... } } class BitcoinPayment : PaymentMethod { override fun processPayment(amount: Double) { // ... code to process cash payment ... } }
Kotlin
๋ณต์‚ฌ

16) ์ž„์‹œ ํ•„๋“œ (Temporary Field)

โ€ข
ํŠน์ • ์ƒํ™ฉ์—์„œ๋งŒ ์‚ฌ์šฉ๋˜๋Š” ๋ณ€์ˆ˜๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
class Shopping { var eventDiscount: Double? = null fun getTotalPrice(items: List<Item>): Double { var total = items.sumOf { it.price } if (eventDiscount != null) { total -= total * eventDiscount!! } return total } }
Kotlin
๋ณต์‚ฌ
๊ฐœ์„ 

17) ๋ฉ”์‹œ์ง€ ์ฒด์ธ (Message Chain)

โ€ข
ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•˜๋‚˜์˜ ๊ฐ์ฒด์— ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ์š”์ฒญํ•  ๋•Œ, ๊ทธ ๊ฐ์ฒด๊ฐ€ ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ๋˜ ๋‹ค์‹œ ์š”์ฒญํ•˜๋Š” ๊ตฌ์กฐ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
class Order(val customer: Customer) class Customer(val address: Address) class Address(val city: City) class City(val name: String) // Client code val order = Order(Customer(Address(City("New York")))) val cityName = order.customer.address.city.name
Kotlin
๋ณต์‚ฌ
๊ฐœ์„ 

18) ์ค‘๊ฐœ์ž (Middle Man)

โ€ข
๊ฐ์ฒด๊ฐ€ ์Šค์Šค๋กœ์˜ ์ฑ…์ž„์„ ๋‹คํ•˜์ง€ ๋ชปํ•˜๊ณ , ๋‹ค๋ฅธ ๊ฐ์ฒด๋กœ ์ฑ…์ž„์„ ์œ„์ž„ํ•ฉ๋‹ˆ๋‹ค.
class Order(val totalCost: Double) class PaymentProcessor { fun processPayment(order: Order, paymentMethod: PaymentMethod) { paymentMethod.pay(order.totalCost) } } interface PaymentMethod { fun pay(amount: Double) } class CreditCard : PaymentMethod { override fun pay(amount: Double) { // Implement payment logic here } } // Client code val order = Order(100.0) val paymentMethod = CreditCard() PaymentProcessor.processPayment(order, paymentMethod)
Kotlin
๋ณต์‚ฌ
๊ฐœ์„ 

19) ๋‚ด๋ถ€์ž ๊ฑฐ๋ž˜ (Insider Trading)

โ€ข
ํ•˜๋‚˜์˜ ๊ฐ์ฒด๊ฐ€ ๋‹ค๋ฅธ ๊ฐ์ฒด์˜ ๋‚ด๋ถ€์™€ ์ง€๋‚˜์น˜๊ฒŒ ๊ฒฐํ•ฉ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
class Trader(private val stockMarket: StockMarket) { fun buy(stock: Stock, quantity: Int): Double { // needs to know about the inner workings of StockMarket to make a trade. if (!stockMarket.stocks.contains(stock)) { throw RuntimeException("Stock not available in the market.") } val cost = stock.price * quantity stock.price += quantity // directly manipulates the price of Stock return cost } } class Stock(var price: Double) class StockMarket { var stocks: MutableList<Stock> = mutableListOf() fun addStock(stock: Stock) { stocks.add(stock) } }
Kotlin
๋ณต์‚ฌ
๊ฐœ์„ 

20) ๊ฑฐ๋Œ€ํ•œ ํด๋ž˜์Šค (Large Classes)

โ€ข
ํ•œ ํด๋ž˜์Šค๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์€ ์ฑ…์ž„์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
class Trader(var name: String, var address: String, var phoneNumber: String, var accountBalance: Double) { //... fun buy(stock: Stock, quantity: Int) { // buy logic } fun sell(stock: Stock, quantity: Int) { // sell logic } fun updateAddress(newAddress: String) { this.address = newAddress } fun updatePhoneNumber(newPhoneNumber: String) { this.phoneNumber = newPhoneNumber } fun deposit(amount: Double) { this.accountBalance += amount } fun withdraw(amount: Double) { if (this.accountBalance < amount) { throw IllegalArgumentException("Insufficient balance!") } this.accountBalance -= amount } //... }
Kotlin
๋ณต์‚ฌ
๊ฐœ์„ 

21) ์„œ๋กœ ๋‹ค๋ฅธ ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋Œ€์•ˆ ํด๋ž˜์Šค๋“ค (Alternative Classes with Different Interfaces)

โ€ข
๋‘ ๊ฐ์ฒด๊ฐ€ ํฐ ํ‹€์—์„œ ์œ ์‚ฌํ•œ ๊ธฐ๋Šฅ์„ ํ•˜์ง€๋งŒ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.
class StockTrader { fun purchaseStock(stock: Stock, quantity: Int) { // Buy logic... } fun sellStock(stock: Stock, quantity: Int) { // Sell logic... } } class CryptoTrader { fun acquireCrypto(crypto: Crypto, quantity: Int) { // Buy logic... } fun disposeCrypto(crypto: Crypto, quantity: Int) { // Sell logic... } }
Kotlin
๋ณต์‚ฌ
๊ฐœ์„ 

22) ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค (Data Class)

โ€ข
๋ฐ์ดํ„ฐ ํด๋ž˜์Šค์—์„œ ์†์„ฑ์„ ์กฐ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
// Data Class data class Image(var width: Int, var height: Int, val format: String) { fun resize() { // ... code to resized width and height ... } }
Kotlin
๋ณต์‚ฌ

23) ์ƒ์† ํฌ๊ธฐ (Refused Bequest)

โ€ข
์ž์‹ ๊ฐ์ฒด์—์„œ ๋ถ€๋ชจ ๊ฐ์ฒด์˜ ๋ฉ”์†Œ๋“œ ์ค‘ ์ผ๋ถ€๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
open class Vehicle { open fun startEngine() { // Start engine } open fun move() { // Move } open fun stop() { // Stop } } class Car: Vehicle() { override fun startEngine() { // Start car engine } override fun move() { // Car moves } override fun stop() { // Car stops } } class Bicycle: Vehicle() { override fun startEngine() { throw UnsupportedOperationException("Bicycles don't have engines") } override fun move() { // Bicycle moves } override fun stop() { // Bicycle stops } }
Kotlin
๋ณต์‚ฌ
๊ฐœ์„ 

24) ์ฃผ์„ (Comments)

โ€ข
์ฝ”๋“œ ์ž์ฒด๋กœ ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์„ ์ถฉ๋ถ„ํžˆ ์„ค๋ช…ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์„์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
class OrderProcessing { fun processOrder(order: Order) { // Validate order if (order.items.isEmpty()) { throw IllegalArgumentException("Order cannot be empty") } if (order.customer == null) { throw IllegalArgumentException("Customer information is required") } // Calculate total var total = 0.0 for (item in order.items) { total += item.price } // Apply discount if (order.customer.isPremium) { total *= 0.9 // 10% discount for premium customers } // Create invoice val invoice = Invoice(order.customer, total) // Send invoice sendInvoice(invoice) } private fun sendInvoice(invoice: Invoice) { // logic to send the invoice } }
Kotlin
๋ณต์‚ฌ
๊ฐœ์„ 

๋‹ค. Basic Refactoring

1) ๊ฐœ์š”

graph TD
    stage1[๋‹จ๊ณ„ 1: ํ•จ์ˆ˜ ๊ตฌ์„ฑ ๋ฐ ์ด๋ฆ„ ์ง“๊ธฐ]
    Function(ํ•จ์ˆ˜ ์ถ”์ถœ ๋˜๋Š” ์ธ๋ผ์ธ)
    Variable(๋ณ€์ˆ˜ ์ถ”์ถœ ๋˜๋Š” ์ธ๋ผ์ธ)
    ChangeFunctionDeclaration(ํ•จ์ˆ˜ ์„ ์–ธ ๋ณ€๊ฒฝ)
    ChangeVariableName(๋ณ€์ˆ˜ ์ด๋ฆ„ ๋ณ€๊ฒฝ)
    VariableEncapsulation(๋ณ€์ˆ˜ ์บก์Šํ™”)
    CreateParameterObject(๋งค๊ฐœ๋ณ€์ˆ˜ ๊ฐ์ฒด ์ƒ์„ฑ)

    stage2[๋‹จ๊ณ„ 2: ํ•จ์ˆ˜ ๋ฌถ๊ธฐ]
    GroupFunctionsInClass(ํด๋ž˜์Šค๋กœ ํ•จ์ˆ˜ ๋ฌถ๊ธฐ)
    GroupFunctionsInTransformFunction(๋ณ€ํ™˜ ํ•จ์ˆ˜๋กœ ํ•จ์ˆ˜ ๋ฌถ๊ธฐ)

    stage3[๋‹จ๊ณ„ 3: ๋‹จ๊ณ„ ์ชผ๊ฐœ๊ธฐ]
    
    stage1 --> Function -->  stage2
    stage1 --> Variable -->  stage2
    stage1 --> ChangeFunctionDeclaration --> stage2
    stage1 --> ChangeVariableName --> stage2
    stage1 --> VariableEncapsulation --> stage2
    stage1 --> CreateParameterObject --> stage2
    
    stage2 --> GroupFunctionsInClass --> stage3
    stage2 --> GroupFunctionsInTransformFunction --> stage3
Mermaid
๋ณต์‚ฌ

2) ํ•จ์ˆ˜ ์ถ”์ถœ ๋˜๋Š” ์ธ๋ผ์ธ

โ€˜๋ชฉ์ โ€™๊ณผ โ€˜๊ตฌํ˜„โ€™์˜ ๋ถ„๋ฆฌ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ•จ์ˆ˜๋ฅผ ์ถ”์ถœํ•  ๊ฒƒ
โ€ข
์ฝ”๋“œ๊ฐ€ ๊ตฌํ˜„์„ ๋‹ด๊ณ  ์žˆ์œผ๋ฉด ํ•จ์ˆ˜๋กœ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ ์ถ”์ถœ๋œ ํ•จ์ˆ˜๊ฐ€ ๋ชฉ์ ์„ ๋‹ด๊ณ  ์žˆ์œผ๋ฉด ์ธ๋ผ์ธํ•ฉ๋‹ˆ๋‹ค.
โ€ข
ํ•จ์ˆ˜ ์ด๋ฆ„์ด ํ•จ์ˆ˜ ๋ณธ๋ฌธ ๋ณด๋‹ค ๊ธธ๋‹ค๊ฑฐ๋‚˜ ํ•จ์ˆ˜ ๋ณธ๋ฌธ์ด ๋„ˆ๋ฌด ์งง๋‹ค๋Š” ๊ฒƒ์€ ์ค‘์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
โ€ข
ํ•จ์ˆ˜ ์ถ”์ถœ ์˜ˆ์‹œ
class OrderProcessing() { fun placeOrder(order: Order) val totalPrice = order.items.sumOf { item -> item.price } var finalPrice = if (order.items.size >= 10) totalPrice * 0.9 else totalPrice order.status = Status.COMPLETED order.totalPrice = finalPrice } }
Kotlin
๋ณต์‚ฌ
to
class OrderService() { fun placeOrder(order: Order) { val totalPrice = order.items.sumOf { item -> item.price } val finalPrice = applyDiscounts(totalPrice) finalizeOrder(finalPrice) } private fun applyDiscounts(totalPrice: Double): Double { return if (order.items.size >= 10) totalPrice * 0.9 else totalPrice } private fun finalizeOrder(finalPrice: Double) { order.status = Status.COMPLETED order.totalPrice = finalPrice } }
Kotlin
๋ณต์‚ฌ
โ€ข
ํ•จ์ˆ˜ ์ธ๋ผ์ธ ์˜ˆ์‹œ
class OrderService() { fun placeOrder(order: Order) { val totalPrice = calculateTotal() val finalPrice = applyDiscounts(totalPrice) finalizeOrder(finalPrice) } // ...์ƒ๋žต }
Kotlin
๋ณต์‚ฌ
to
class OrderService() { fun placeOrder(order: Order) { val totalPrice = order.items.sumOf { item -> item.price } val finalPrice = applyDiscounts(totalPrice) finalizeOrder(finalPrice) } // ...์ƒ๋žต }
Kotlin
๋ณต์‚ฌ

3) ๋ณ€์ˆ˜ ์ถ”์ถœ ๋˜๋Š” ์ธ๋ผ์ธ

ํ•จ์ˆ˜์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ โ€˜๋ชฉ์ โ€™๊ณผ โ€˜๊ตฌํ˜„โ€™์„ ๊ธฐ์ค€์œผ๋กœ ๋ณ€์ˆ˜๋ฅผ ์ถ”์ถœํ•  ๊ฒƒ
โ€ข
๋งŒ์•ฝ ์ถ”์ถœ ๋Œ€์ƒ ์ฝ”๋“œ๊ฐ€ ํ•จ์ˆ˜ ์™ธ๋ถ€์—๋„ ์˜๋ฏธ๋ฅผ ๊ฐ–๋Š”๋‹ค๋ฉด ๋ณ€์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ํ•จ์ˆ˜๋กœ ์ถ”์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
โ€ข
๋ณ€์ˆ˜ ์ถ”์ถœ ์˜ˆ์‹œ
private fun applyDiscounts(totalPrice: Double): Double { return if (order.items.size >= 10) totalPrice * 0.9 else totalPrice }
Kotlin
๋ณต์‚ฌ
to
private fun applyDiscounts(totalPrice: Double): Double { val isDiscountApplicable = order.items.size >= 10 val discountRate = 0.9 return if (isDiscountApplicable) totalPrice * discountRate else totalPrice }
Kotlin
๋ณต์‚ฌ
โ€ข
๋ณ€์ˆ˜ ์ธ๋ผ์ธ ์˜ˆ์‹œ
private fun applyDiscounts(totalPrice: Double): Double { val itemSize = order.items.size val discountRate = 0.9 return if (itemSize >= 10) totalPrice * discountRate else totalPrice }
Kotlin
๋ณต์‚ฌ
to
private fun applyDiscounts(totalPrice: Double): Double { val discountRate = 0.9 return if (order.items.size >= 10) totalPrice * discountRate else totalPrice }
Kotlin
๋ณต์‚ฌ

4) ํ•จ์ˆ˜ ์„ ์–ธ ๋ณ€๊ฒฝ

ํ•จ์ˆ˜์˜ ์ด๋ฆ„๊ณผ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ โ€˜๋ชฉ์ โ€™์— ๋งž๊ฒŒ ์„ ์–ธํ•  ๊ฒƒ
โ€ข
ํ•จ์ˆ˜ ์ด๋ฆ„์˜ ๊ฒฝ์šฐ, ํ•จ์ˆ˜ ๋‚ด๋ถ€์˜ ๊ตฌํ˜„์ด ์•„๋‹ˆ๋ผ ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ๊ณณ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋ชฉ์ ์— ๋งž๊ฒŒ ์ •์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
โ€ข
๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ฒฝ์šฐ, ํ•„์ˆ˜ ๊ฐ’๋งŒ์„ ์ „๋‹ฌํ•˜์—ฌ ํ™œ์šฉ๋„๋ฅผ ๋†’์ผ ์ง€ ๋˜๋Š” ์—ฐ๊ด€ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ์บก์Šํ™” ์ˆ˜์ค€์„ ๋†’์ผ ์ง€๋ฅผ ๊ฒฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
โ€ข
ํ•จ์ˆ˜ ์ด๋ฆ„ ๋ณ€๊ฒฝ ์˜ˆ์‹œ
class Bank() { fun calculateInterestByAccountType(account: Account): Double { val rate = if (account.type == AccountType.CHECKING) 0.01 else 0.02 return account.balance * rate } }
Kotlin
๋ณต์‚ฌ
to
class Bank() { fun calculateInterest(account: Account): Double { val rate = if (account.type == AccountType.CHECKING) 0.01 else 0.02 return account.balance * rate } }
Kotlin
๋ณต์‚ฌ
โ€ข
๋งค๊ฐœ๋ณ€์ˆ˜ ๋ณ€๊ฒฝ ์˜ˆ์‹œ
fun applyDiscount(totalPrice: Double, numberOfItems: Int): Double { val discountRate = 0.9 return if (numberOfItems >= 10) totalPrice * discountRate else totalPrice }
Kotlin
๋ณต์‚ฌ
to
fun applyDiscount(order: Order): Double { val discountRate = 0.9 return if (order.items.size >= 10) order.totalPrice * discountRate else order.totalPrice }
Kotlin
๋ณต์‚ฌ

5) ๋ณ€์ˆ˜ ์บก์Šํ™”

๋ฐ์ดํ„ฐ์˜ ์œ ํšจ๋ฒ”์œ„๊ฐ€ ๋„“์€ ๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ๋Š” ์ ‘๊ทผ๊ณผ ๊ฐฑ์‹ ์— ๋Œ€ํ•ด ์บก์Šํ™”ํ•  ๊ฒƒ
โ€ข
์œ ์—ฐํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํ™˜๊ฒฝ์—์„œ๋Š” ์ฝ”๋“œ ์ „๋ฐ˜์— ๊ฑธ์ณ ์œ ํšจ๋ฒ”์œ„๊ฐ€ ๋„“์€ ๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค.
โ€ข
OOP ํ™˜๊ฒฝ์—์„œ๋Š” ํด๋ž˜์Šค๋ฅผ ๋ฒ—์–ด๋‚œ ๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ ์ž์ฒด๊ฐ€ ์šฉ์ธ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

6) ๋ณ€์ˆ˜ ์ด๋ฆ„ ๋ณ€๊ฒฝ

๋งฅ๋ฝ ์•ˆ์—์„œ ๋ณ€์ˆ˜์˜ โ€˜๋ชฉ์ โ€™์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ช…๋ช…ํ•  ๊ฒƒ
โ€ข
๋งฅ๋ฝ ์•ˆ์—์„œ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ๋ณ€์ˆ˜๋ช…์„ ํ•œ ๊ธ€์ž๋กœ ์ง“๋Š” ๊ฒƒ๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.
โ€ข
DB์— ์ €์žฅ๋˜๋Š” ์˜์†์„ฑ ๋ฐ์ดํ„ฐ์˜ ๊ฒฝ์šฐ, ๋ณ€์ˆ˜๋ช…์— ๋” ์‹ ๊ฒฝ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

7) ๋งค๊ฐœ๋ณ€์ˆ˜ ๊ฐ์ฒด ์ƒ์„ฑ

๊ด€๊ณ„ ์žˆ๋Š” ๋ฐ์ดํ„ฐ ๋ญ‰์น˜๊ฐ€ ๋ณด์ด๋ฉด ๊ฐ์ฒด๋กœ ๋ฌถ์„ ๊ฒƒ
โ€ข
๋ฐ์ดํ„ฐ ๋ญ‰์น˜๋ฅผ ๊ฐ์ฒด ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌ์กฐํ™”ํ•˜๋Š” ๊ณผ์ •์ด ๋ฐ˜๋ณต๋˜๋ฉด, ๋ฌธ์ œ๋ฅผ ๋ณด๋‹ค ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ธธ์ด ๋ณด์ž…๋‹ˆ๋‹ค.

8) ์—ฌ๋Ÿฌ ํ•จ์ˆ˜๋ฅผ ํด๋ž˜์Šค๋กœ ๋ฌถ๊ธฐ

๊ณตํ†ต ๋ฐ์ดํ„ฐ ์ค‘์‹ฌ์˜ ํ•จ์ˆ˜ ๋ญ‰์น˜๊ฐ€ ๋ณด์ด๋ฉด ํด๋ž˜์Šค๋กœ ๋ฌถ์„ ๊ฒƒ

9) ์—ฌ๋Ÿฌ ํ•จ์ˆ˜๋ฅผ ๋ณ€ํ™˜ ํ•จ์ˆ˜๋กœ ๋ฌถ๊ธฐ

ํŒ€์˜ ์Šคํƒ€์ผ์ด โ€˜๋ณ€ํ™˜ ํ•จ์ˆ˜๋กœ ๋ฌถ๊ธฐโ€™์ผ ๊ฒฝ์šฐ๋งŒ ์ฑ„ํƒ, ์•„๋‹ˆ๋ผ๋ฉด ํด๋ž˜์Šค๋กœ ๋ฌถ์„ ๊ฒƒ

1st Edition

๊ฐ€. Basic Refactoring

1. ๋ฉ”์†Œ๋“œ๋ฅผ ๋‚˜๋ˆ„๋Š” ์ด์œ 

โ€ข
๊ธด ๋ฉ”์†Œ๋“œ์—๋Š” ๋ณต์žกํ•œ ๋กœ์ง๊ณผ ๋งŽ์€ ์ •๋ณด๊ฐ€ ์—‰ํ‚ค๊ธฐ ์‰ฝ๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œ
โ€ข
๋‹ค๋ฅธ ๋ฉ”์†Œ๋“œ์—์„œ ์‚ฌ์šฉํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„์ง
โ€ข
์˜ค๋ฒ„๋ผ์ด๋”ฉ์ด ์‰ฌ์›Œ์ง

2. Precondition of Extracting Method

โ€ข
ํŠน์ • ์ฝ”๋“œ๋ฅผ ์ƒˆ๋กœ์šด ๋ฉ”์†Œ๋“œ๋กœ ๋‚˜๋ˆ„๊ธฐ ์ „, ์ถ”์ถœํ•˜๋ ค๋Š” ์ฝ”๋“œ์˜ ์ง‘ํ•ฉ์— ๋Œ€ํ•œ ์œ ์˜๋ฏธํ•œ ์ด๋ฆ„์ด ๋– ์˜ค๋ฅด์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ถ”์ถœํ•˜์ง€ ๋ง ๊ฒƒ

3. No Local Varialbes

โ€ข
์ถ”์ถœํ•˜๋ ค๋Š” ์ฝ”๋“œ ๋‚ด์— ์‚ฌ์šฉ๋˜๋Š” ์ง€์—ญ๋ณ€์ˆ˜๊ฐ€ ์—†๋‹ค๋ฉด ๊ทธ๋ƒฅ ๋ณต์‚ฌ ๋ถ™์—ฌ๋„ฃ๊ธฐํ•˜์—ฌ ์ƒˆ๋กœ์šด ๋ฉ”์†Œ๋“œ๋ฅผ ์ƒ์„ฑ

4. Using Local Variables

โ€ข
์ถ”์ถœํ•˜๋ ค๋Š” ์ฝ”๋“œ ๋‚ด์— ์‚ฌ์šฉ๋˜๋Š” ์ง€์—ญ๋ณ€์ˆ˜๊ฐ€ ์žˆ๋‹ค๋ฉด ์ƒˆ๋กœ์šด ๋ฉ”์†Œ๋“œ์— ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌํ•จ

5. Reassigning a Local Variable

โ€ข
์ถ”์ถœํ•˜๋ ค๋Š” ์ฝ”๋“œ ๋‚ด์— ์‚ฌ์šฉ๋˜๋Š” ๋ณ€์ˆ˜๊ฐ€ ์ƒˆ๋กœ์šด ๋ฉ”์†Œ๋“œ๋กœ ๋ถ„๋ฅ˜ํ•œ ์ฝ”๋“œ ์™ธ์—์„œ๋„ ์‚ฌ์šฉ๋œ๋‹ค๋ฉด ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ ์ง€์ •ํ•จ.
โ€ข
์ƒˆ๋กœ์šด ๋ฉ”์†Œ๋“œ๋กœ ๋ถ„๋ฅ˜ํ•œ ์ฝ”๋“œ ๋‚ด์—์„œ๋งŒ ์‚ฌ์šฉ๋œ๋‹ค๋ฉด ์ƒˆ๋กœ ์ƒ์„ฑํ•œ ๋ฉ”์†Œ๋“œ์˜ ์ง€์—ญ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉํ•จ
// Before void printOwing() { Enumeration e = _orders.elements(); double outstanding = 0.0; printBanner(); // calculate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); } printDetails(outstanding); } // After void printOwing(double previousAmount) { printBanner(); double outstanding = getOutstanding(previousAmount * 1.2); printDetails(outstanding); } double getOutstanding(double initialValue) { double result = initialValue; Enumeration e = _orders.elements(); while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); result += each.getAmount(); } return result; }
Java
๋ณต์‚ฌ

6. Inline Method & Temp

โ€ข
๊ฐ„๋‹จํ•˜๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตณ์ด ๋‚˜๋ˆ„๋Š” ๊ฒƒ์€ ์ฝ”๋“œ๋ฅผ ๋ณต์žกํ•˜๊ฒŒ ํ•จ
โ†’ ์ฝ”๋“œํ•ด์„์— ์–ด๋ ค์›€์ด ์—†๋‹ค๋ฉด inline์œผ๋กœ ์ฝ”๋“œ๋ฅผ ๊ฐ„๋‹จ
int getRating() { return (moreThanFiveLateDeliveries()) ? 2 : 1; } boolean moreThanFiveLateDeliveries() { return _numberOfLateDeliveries > 5; } // After int getRating() { return (_numberOfLateDeliveries > 5) ? 2 : 1; }
Java
๋ณต์‚ฌ
โ€ข
์˜๋ฏธ๊ฐ€ ๊ฐ„๋‹จํ•œ ๊ฒฝ์šฐ๋ฉด์„œ ๋™์‹œ์— ํ•œ๋ฒˆ๋งŒ ์‚ฌ์šฉ๋˜๋Š” ๋ณ€์ˆ˜์˜ ๊ฒฝ์šฐ, ๋ณ€์ˆ˜๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ๋ฉ”์†Œ๋“œ๋กœ ๋Œ€์ฒดํ•จ
double basePrice = anOrder.basePrice(); return (basePrice > 1000) // After return (anOrder.basePrice() > 1000)
Java
๋ณต์‚ฌ

7. Replace Temp with Query

โ€ข
๋ฉ”์†Œ๋“œ ์ด๋ฆ„๋งŒ์œผ๋กœ ๋งฅ๋ฝ์„ ์•Œ ์ˆ˜ ์žˆ์„ ๋งŒํผ ๊ฐ„๋‹จํ•œ ์‹์„ ๋ณ€์ˆ˜๋กœ ํ• ๋‹นํ–ˆ๋‹ค๋ฉด, ์ด๋ฅผ ๋ฉ”์†Œ๋“œ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์‚ฌ์šฉ
double basePrice = _quantity * _itemPrice; if (basePrice > 1000) return basePrice * 0.95; else return basePrice * 0.98; // After if (basePrice() > 1000) return basePrice() * 0.95; else return basePrice() * 0.98; double basePrice() { return _quantity * _itemPrice; }
Java
๋ณต์‚ฌ

8. Introduce Explaining Variable

โ€ข
ํŠน์ • ์‹์— ๋Œ€ํ•œ ์˜๋ฏธ๊ฐ€ ํ•œ ๋ˆˆ์— ๋“ค์–ด์˜ค์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์˜๋ฏธ๋ฅผ ๋ช…ํ™•ํžˆ ํ•  ๊ฒƒ
if ( (platform.toUpperCase().indexOf("MAC") > -1) && (browser.toUpperCase().indexOf("IE") > -1) && wasInitialized() && resize > 0 ) { // do something } // After final boolean isMacOs = platform.toUpperCase().indexOf("MAC") >-1; final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") >-1; final boolean wasResized = resize > 0; if (isMacOs && isIEBrowser && wasInitialized() && wasResized) { // do something }
Java
๋ณต์‚ฌ

9. Split Temporary Variable

โ€ข
์ž„์‹œ์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋ณ€์ˆ˜์ผ์ง€๋ผ๋„ ๊ฐ™์€ ์ด๋ฆ„์˜ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋ง๊ฒƒ
โ†’ ์ฝ”๋“œ๋ฅผ ์ฝ๋Š” ์‚ฌ๋žŒ์—๊ฒŒ ํ˜ผ๋™์„ ์ค„ ์ˆ˜ ์žˆ์Œ
โ€ข
์ˆœํ™˜๋ฌธ์˜ ์ธ๋ฑ์Šค์™€ Collecting temporary๋Š” ์˜ˆ์™ธ
double temp = 2 * (_height + _width); System.out.println (temp); temp = _height * _width; System.out.println (temp); // After final double perimeter = 2 * (_height + _width); System.out.println (perimeter); final double area = _height * _width; System.out.println (area);
Java
๋ณต์‚ฌ

10. Remove Assignments to Parameters

โ€ข
๋ฉ”์†Œ๋“œ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ๋œ ๊ฐ’์„ ์กฐ์ž‘ํ•˜์ง€ ๋ง ๊ฒƒ
โ†’ ์ž๋ฐ”์—์„ ๋Š” pass by value์˜ ์›์น™์ด ์ ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์— discount ๋ฉ”์†Œ๋“œ ์™ธ๋ถ€์—์„œ inputVal์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์ง€๋งŒ, ๋‹ค๋ฅธ ์–ธ์–ด(pass by reference)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” reader์—๊ฒŒ ํ˜ผ๋ž€์„ ์ค„ ์ˆ˜ ์žˆ์Œ
โ†’ ์ผ๊ด€์ ์ธ ์‚ฌ์šฉ์„ฑ์„ ๊ฐ์•ˆํ•˜๋ฉด ๋งค๊ฐœ๋ณ€์ˆ˜์— ๋ณ€ํ™”๋ฅผ ์ฃผ์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ฝ”๋“œ๋ฅผ ๋”์šฑ ๋ช…ํ™•ํ•˜๊ฒŒ ๋งŒ๋“ค์–ด ์คŒ
int discount (int inputVal, int quantity, int yearToDate) { if (inputVal > 50) inputVal -= 2; // After int discount (int inputVal, int quantity, int yearToDate) { int result = inputVal; if (inputVal > 50) result -= 2;
Java
๋ณต์‚ฌ

11. Replace Method with Method Object

โ€ข
์ง€์—ญ๋ณ€์ˆ˜๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์•„์„œ ๋ฉ”์†Œ๋“œ ๋ถ„ํ• ์ด ์–ด๋ ค์šธ ๊ฒฝ์šฐ, ๋ฉ”์†Œ๋“œ ๊ฐ์ฒด๋กœ ๋Œ€์ฒดํ•œ๋‹ค
class Order... double price() { double primaryBasePrice; double secondaryBasePrice; double tertiaryBasePrice; // long computation; ... } // After class Order... double price(){ return new PriceCalculator(this).compute() } } class PriceCalculator... compute(){ double primaryBasePrice; double secondaryBasePrice; double tertiaryBasePrice; // long computation; return ... }
Java
๋ณต์‚ฌ
Class Account int gamma (int inputVal, int quantity, int yearToDate) { int importantValue1 = (inputVal * quantity) + delta(); int importantValue2 = (inputVal * yearToDate) + 100; if ((yearToDate - importantValue1) > 100) importantValue2 -= 20; int importantValue3 = importantValue2 * 7; // and so on. return importantValue3 - 2 * importantValue1; } } // After class Gamma... private final Account _account; private int inputVal; private int quantity; private int yearToDate; private int importantValue1; private int importantValue2; private int importantValue3; Gamma (Account source, int inputValArg, int quantityArg, int yearToDateArg) { _account = source; inputVal = inputValArg; quantity = quantityArg; yearToDate = yearToDateArg; } int compute () { importantValue1 = (inputVal * quantity) + _account.delta(); importantValue2 = (inputVal * yearToDate) + 100; if ((yearToDate - importantValue1) > 100) importantValue2 -= 20; int importantValue3 = importantValue2 * 7; // and so on. return importantValue3 - 2 * importantValue1; } int gamma (int inputVal, int quantity, int yearToDate) { return new Gamma(this, inputVal, quantity,yearToDate).compute(); }
Java
๋ณต์‚ฌ

๋‚˜. Moving features between elements

1. Move method / field

โ€ข
๋ฉ”์†Œ๋“œ๋‚˜ ํ•„๋“œ๋‚˜ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” class์— ์ •์˜ํ•  ๊ฒƒ

2. Extract Class

โ€ข
ํ•˜๋‚˜์˜ ํด๋ž˜์Šค์— ๋„ˆ๋ฌด ๋งŽ์€ ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค๋ฉด ์—ฐ๊ด€์„ฑ์„ ๊ธฐ์ค€์œผ๋กœ ๋‘ ๊ฐœ ์ด์ƒ์˜ ํด๋ž˜์Šค๋กœ ๋‚˜๋ˆŒ ๊ฒƒ
โ†’ ์‹œ์Šคํ…œ์€ ์‹œ๊ฐ„์ด ๊ฐˆ ์ˆ˜๋ก ๋”์šฑ ๋ณต์žกํ•ด์ง. ํด๋ž˜์Šค๋„ ๋”์šฑ ๋ณต์žกํ•ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋Š” ํด๋ž˜์Šค๋Š” ๋ฏธ๋ฆฌ ๋‚˜๋ˆ„๋Š” ๊ฒƒ์ด ํšจ์œจ์ 
class Person { name, officeAreaCode, officeNumber, getTelephoneNumber() } // After class Person { name, getTelephoneNumber() } class TelephoneNumber { areaCode, number, getTelephoneNumber() }
Java
๋ณต์‚ฌ

3. Inline Class

โ€ข
๋ฆฌํŒฉํ„ฐ๋ง์„ ์ œ๋Œ€๋กœ ํ•˜๋ฉด ๊ฐ๊ฐ์˜ ํด๋ž˜์Šค๋“ค์ด ๋„ˆ๋ฌด ๋น„๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฏ€๋กœ, ๋งŽ์ด ์‚ฌ์šฉ๋˜์ง€ ์•Š๊ฑฐ๋‚˜ ๊ณผํ•˜๊ฒŒ ์ถ”์ถœ๋œ ํด๋ž˜์Šค๋Š” ๊ด€๋ จ ํด๋ž˜์Šค์— ํฌํ•จ์‹œํ‚ด

4. Hide Delegate

5. Remove Middle Man

โ€ข
4, 5๋ฒˆ์€ ์ถ”ํ›„ ๋‹ค์‹œ

6. Introduce Foreign Method

โ€ข
Server class์— ๋ฉ”์†Œ๋“œ๋กœ ์„ ์–ธ๋˜์ง€ ์•Š์•˜์ง€๋งŒ client class์— ํ•„์š”ํ•œ ๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ Foreign Method ์„ ์–ธํ•˜์—ฌ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
Date newStart = new Date(previousEnd.getYear(),previousEnd.getMonth(),previousEnd.getDate()+1); // After Date newStart = nextDay(previousEnd); private static Date nextDay(Date date){ return new Date(date.getYear(),date.getMonth(),date.getDate()+1); }
Java
๋ณต์‚ฌ

7. Introduce Local Extension

โ€ข
์ถ”ํ›„ ๋‹ค์‹œ ํ•™์Šต

๋‹ค. SIMPLIFYING CONDITIONAL EXPRESSIONS

1. Decompose Conditional

if (date.before (SUMMER_START) || date.after(SUMMER_END)) charge = quantity * _winterRate + _winterServiceCharge; else charge = quantity * _summerRate; // After if (notSummer(date)) charge = winterCharge(quantity); else charge = summerCharge (quantity);
Java
๋ณต์‚ฌ

2. Consolidate Conditional Expression

double disabilityAmount() { if (_seniority < 2) return 0; if (_monthsDisabled > 12) return 0; if (_isPartTime) return 0; // after double disabilityAmount() { if (isNotEligableForDisability()) return 0;
Java
๋ณต์‚ฌ

3. Consolidate Duplicate Conditional Fragments

if (isSpecialDeal()) { total = price * 0.95; send(); } else { total = price * 0.98; send(); } // after if (isSpecialDeal()) { total = price * 0.95; } else { total = price * 0.98; } send();
Java
๋ณต์‚ฌ

4. Remove Control Flag

โ€ข
control flag ๋Œ€์‹  break๋‚˜ continue ์‚ฌ์šฉ์„ ๊ถŒ์žฅ
โ†’ ์กฐ๊ฑด๋ฌธ์˜ ๋ชฉ์ ์„ฑ์„ ๋ณด๋‹ค ๋ช…ํ™•ํ•˜๊ฒŒ ํ•˜๋ฏ€๋กœ
void checkSecurity(String[] people) { boolean found = false; for (int i = 0; i < people.length; i++) { if (! found) { if (people[i].equals ("Don")){ sendAlert(); found = true; } if (people[i].equals ("John")){ sendAlert(); found = true; } } } } // after void checkSecurity(String[] people) { for (int i = 0; i < people.length; i++) { if (people[i].equals ("Don")){ sendAlert(); break; // or return } if (people[i].equals ("John")){ sendAlert(); break; // or return } } }
Java
๋ณต์‚ฌ

5. Replace Nested Conditional with Guard Clauses

double getPayAmount() { double result; if (_isDead) result = deadAmount(); else { if (_isSeparated) result = separatedAmount(); else { if (_isRetired) result = retiredAmount(); else result = normalPayAmount(); }; } return result; }; // after double getPayAmount() { if (_isDead) return deadAmount(); if (_isSeparated) return separatedAmount(); if (_isRetired) return retiredAmount(); return normalPayAmount(); };
Java
๋ณต์‚ฌ

๋ผ. ์š”์•ฝ

๋ฆฌํŒฉํ„ฐ๋ง์ด๋ž€ ์ฝ”๋“œ์˜ ๊ฒฐ๊ณผ๋Š” ๋™์ผํ•˜์ง€๋งŒ ๋‚ด๋ถ€์˜ ๊ตฌ์กฐ๋ฅผ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๋ฆฌํŒฉํ„ฐ๋ง์˜ ๋ชฉ์ ์€ ์ฝ”๋“œ ์ž‘์„ฑ ํ›„์— ์ฒด๊ณ„์ ์œผ๋กœ ์„ค๊ณ„๋œ ์ฝ”๋“œ๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ์ด๋‹ค.

1) ์˜ˆ์ œ ์ดํ•ด

โ€ข
Movie class: ๋‹จ์ˆœํ•œ ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค
โ€ข
Rental class: ๊ณ ๊ฐ์ด ์˜ํ™”๋ฅผ ๋นŒ๋ฆฌ๋Š” ํ–‰์œ„์„ ํ‘œํ˜„
โ€ข
Customer class: ์ƒ์ ์— ๋ฐฉ๋ฌธํ•œ ๊ณ ๊ฐ์„ ํ‘œํ˜„ํ•˜๋ฉฐ ๊ณ ๊ฐ์˜ ๋ฐ์ดํ„ฐ ์—‘์„ธ์„œ(๋‹ค๋ฅธ ํด๋ž˜์Šค ์ ‘๊ทผํ•จ์ˆ˜)๋ฅผ ํฌํ•จํ•จ

2) ๋ฆฌํŒฉํ„ฐ๋ง ์ „ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ

โ€ข
Refactoring ์‹œ์ž‘ ์ „, ๋ฐ˜๋“œ์‹œ ์ผ๋ จ์˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•จ
โ€ข
ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” self-checking ๊ธฐ๋Šฅ์„ ํฌํ•จํ•˜์—ฌ ์ถ”ํ›„ ํ…Œ์ŠคํŠธ ์ž‘์—…์— ๋”ฐ๋ฅธ ์‹œ๊ฐ„์„ ์ตœ์†Œํ™” ํ•ด์•ผ ํ•จ
: self-chcking ๊ธฐ๋Šฅ์ด๋ž€ ๋‹ค์Œ์˜ ๊ธฐ๋Šฅ์„ ํฌํ•จํ•จ. ๋ชจ๋“  ํ…Œ์ŠคํŠธ ํ†ต๊ณผ ์‹œ, okay ๋“ฑ์˜ ํ‘œ์‹œ, ์˜๋„ํ•œ ๊ฒฐ๊ณผ์™€ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ค๋ฅผ ์‹œ, ์ฐจ์ด์  ์ถœ๋ ฅ ๋“ฑ

3) ๋ฉ”์†Œ๋“œ ์ถ”์ถœ

โ€ข
๋” ์ž‘์€ ์กฐ๊ฐ์˜ ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์€ ์ฝ”๋“œ์˜ ์˜๋ฏธ๋ฅผ ๋”์šฑ ๋ช…ํ™•ํžˆ ํ•˜์—ฌ ํ˜‘์—… ์‹œ ์ƒ์‚ฐ์„ฑ์ด ์ฆ๊ฐ€ํ•จ
โ€ข
Decomposing์ด๋ž€ ์ž‘์—… ๋Œ€์ƒ์„ ์ฐพ์•„์„œ ๊ธฐ์กด ์ฝ”๋“œ์—์„œ ์ถ”์ถœํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋ญ‰์ณ์ง„ ์ฝ”๋“œ ๊ทธ๋ฃน์ด 1์ฐจ ํƒ€๊ฒŸ์ด๋‹ค. ์ฃผ๋กœ ๋ฐ˜๋ณต๋ฌธ์ด๋‚˜ switch๋ฌธ ๋“ฑ์˜ ํฐ ๋…ผ๋ฆฌ์  ์ง‘ํ•ฉ(logical clump)
โ€ข
Decomposing ํ•  ๋•Œ, ์ฃผ์˜ํ•  ์ ์œผ๋กœ ์•„๋ž˜์˜ ์„ธ ๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.
โ€ข
๊ธฐ์กด ์ฝ”๋“œ์—์„œ Decomposingํ•˜์—ฌ ์ƒˆ๋กœ์šด ๋ฉ”์†Œ๋“œ๋กœ ๋ถ„๋ฆฌ ์‹œ, ๋ถ„๋ฆฌ๋œ ๋ฉ”์†Œ๋“œ ๋‚ด์—์„œ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋Š” ๋ณ€์ˆ˜์™€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ๋ณ€์ˆ˜์— ๋‚˜๋ˆ„์–ด ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค. ํŠนํžˆ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋Š” ๋ณ€์ˆ˜๋Š” ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•จ. ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋Š” ๋ณ€์ˆ˜๋Š” ๋ถ„๋ฆฌ๋œ ๋ฉ”์†Œ๋“œ์—์„œ ์ƒˆ๋กœ์šด ๋ณ€์ˆ˜๋กœ ์ƒ์„ฑํ•˜์—ฌ ๋ฉ”์†Œ๋“œ์˜ ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ ์‚ฌ์šฉ
๋‹ค. ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์„ ๊ฒฝ์šฐ, ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„์„œ ์‚ฌ์šฉ
โ€ข
๋ฐ˜๋“œ์‹œ ํ…Œ์ŠคํŠธํ•˜์—ฌ ์ด์ƒ์œ ๋ฌด ํ™•์ธํ•  ๊ฒƒ(์ธ๊ฐ„์  ์‹ค์ˆ˜ ๊ฑธ๋Ÿฌ๋‚ด๊ธฐ)
โ€ข
์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑํ•œ ๋ฉ”์†Œ๋“œ์— ๋งž๊ฒŒ ๋ณ€์ˆ˜๋ช…์„ ๋ณ€๊ฒฝํ•  ๊ฒƒ

4) ๋ฉ”์†Œ๋“œ ์ด์ „

โ€ข
์ผ๋ฐ˜์ ์œผ๋กœ ๋ฉ”์†Œ๋“œ๋Š” ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ฐ์ฒด ๋‚ด ์กด์žฌํ•˜๋Š” ๊ฒƒ์ด ๊น”๋”ํ•จ
: ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๋Š” ํด๋ž˜์Šค ๋‚ด ๋ฉ”์†Œ๋“œ๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด ๊ฐ™์€ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ ๋ฐ›์ง€ ์•Š์•„๋„ ๋จ. ๋ณ€์ˆ˜ ์‚ฌ์šฉ์„ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ์Œ.
โ€ข
new method๋ฅผ ์ƒ์„ฑํ•˜๊ณ  old method๋ฅผ ๋ฐ”๋กœ ์ œ๊ฑฐํ•˜๊ธฐ ๋ณด๋‹ค old method๊ฐ€ ์‚ฌ์šฉ๋œ software interface ๋‚ด์—์„œ new method๋ฅผ ํ…Œ์ŠคํŠธํ•ด์•ผ ํ•œ๋‹ค.
โ€ข
ํ…Œ์ŠคํŠธํ•œ ํ›„ ์ด์ƒ ์—†์„ ์‹œ, ๊ธฐ์กด ๋ฉ”์†Œ๋“œ(amountFor)๊ฐ€ ์‚ฌ์šฉ๋œ ์ฝ”๋“œ์—์„œ ์ด๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  new method๋กœ ๋ฐ”๊ฟˆ
โ€ข
์ผ์‹œ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ณ€์ˆ˜๋ฅผ ์ตœ์†Œํ™”ํ•˜์—ฌ ์‚ฌ์šฉํ•ด์•ผ ํ•จ
: ์ผ์‹œ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ณ€์ˆ˜๊ฐ€ ์ด๊ณณ์ €๊ณณ์—์„œ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋ฉด ๊ฐ๊ฐ์˜ ๋ณ€์ˆ˜์˜ ์‚ฌ์šฉ์ฒ˜๋ฅผ ๋†“์น  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋‚˜์ค‘์— ํฐ ๋ฌธ์ œ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Œ
: ์ด๋Ÿฌํ•œ ์ž„์‹œ๋ณ€์ˆ˜๋Š” ๊ธธ๊ฒŒ ์ž‘์„ฑ๋œ ๋ฉ”์†Œ๋“œ์— ๋งŽ์ด ์žˆ์Œ
: ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ์„ ๋‘ ๋ฒˆ ์ด์ƒ ํ•˜๋Š” ๊ฒƒ์€ ๊ณ„์‚ฐ์„ ์ค‘๋ณต ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด๋ฏ€๋กœ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์Œ. ์ด ๋ฌธ์ œ์— ๋Œ€ํ•ด 'Refactoring and Performance' ๋ถ€๋ถ„์—์„œ ๋‹ค๋ฃธ
// Old method class Customer... private double amountFor(Rental aRental) { double result = 0; switch (aRental.getMovie().getPriceCode()) { case Movie.REGULAR: result += 2; if (aRental.getDaysRented() > 2) result += (aRental.getDaysRented() - 2) * 1.5; break; case Movie.NEW_RELEASE: result += aRental.getDaysRented() * 3; break; case Movie.CHILDRENS: result += 1.5; if (aRental.getDaysRented() > 3) result += (aRental.getDaysRented() - 3) * 1.5; break; } return result; }
Java
๋ณต์‚ฌ
// New method class Customer... private double amountFor(Rental aRental) { return aRental.getCharge(); } class Rental... double getCharge() { double result = 0; switch (getMovie().getPriceCode()) { case Movie.REGULAR: result += 2; if (getDaysRented() > 2) result += (getDaysRented() - 2) * 1.5; break; case Movie.NEW_RELEASE: result += getDaysRented() * 3; break; case Movie.CHILDRENS: result += 1.5; if (getDaysRented() > 3) result += (getDaysRented() - 3) * 1.5; break; } return result; }
Java
๋ณต์‚ฌ

5) ๋ฉ”์†Œ๋“œ ์ถ”์ถœ

โ€ข
frequentRenterPoints๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๋กœ์ง์„ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
โ€ข
frequentRenterPoints ๊ณ„์‚ฐ ์‹œ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์†Œ๋“œ๊ฐ€ Rental class์— ์žˆ์œผ๋ฏ€๋กœ ๊ฐ์ฒด๋ฅผ ์ด๋™ํ•˜์—ฌ ๋ฉ”์†Œ๋“œ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
โ€ข
frequentRenterPoints์˜ ์„ ์–ธ๋ถ€๊ฐ€ while ๋ฐ˜๋ณต๋ฌธ ์œ„์ชฝ์— ์กด์žฌํ•จ์œผ๋กœ, ์ƒˆ๋กœ์šด ๋ฉ”์†Œ๋“œ ๋‚ด๋ถ€์—๋Š” frequentRenterPoints ๋ณ€์ˆ˜์— ์ ‘๊ทผํ•˜๋Š” ๋กœ์ง์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Œ
// Old method class Customer... int frequentRenterPoints = 0; while (rentals.hasMoreElements()) { Rental each = (Rental) rentals.nextElement(); // add frequent renter points frequentRenterPoints++; // add bonus for a two day new release rental if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) frequentRenterPoints++; // show figures for this rental result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(each.getCharge()) + "\n"; totalAmount += each.getCharge(); }
Java
๋ณต์‚ฌ
// New method class Rental... int getFrequentRenterPoints() { if ((getMovie().getPriceCode() == Movie.NEW_RELEASE) && getDaysRented() > 1) return 2; else return 1; } class Customer... int frequentRenterPoints = 0; while (rentals.hasMoreElements()) { Rental each = (Rental) rentals.nextElement(); // add frequent renter points frequentRenterPoints += each.getFrequentRenterPoints(); // show figures for this rental result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(each.getCharge()) + "\n"; totalAmount += each.getCharge(); }
Java
๋ณต์‚ฌ

6) ์ž„์‹œ๋ณ€์ˆ˜ ์ œ๊ฑฐ

โ€ข
frequentRenterPoints์™€ totalAmount ๋ณ€์ˆ˜๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์ƒˆ๋กœ์šด ๋ฉ”์†Œ๋“œ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
โ€ข
์ผ์‹œ์ ์ธ ๋ณ€์ˆ˜๋ฅผ ์ œ๊ฑฐํ•จ์œผ๋กœ์จ ๊ธฐ์กด ๋ฉ”์†Œ๋“œ์˜ ์žฌํ™œ์šฉ์„ฑ์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.
private double getTotalCharge() { double result = 0; Enumeration<Rental> rentals = _rentals.elements(); while (rentals.hasMoreElements()) { Rental each = (Rental) rentals.nextElement(); result += each.getCharge(); } return result; } private int getTotalFrequentRenterPoints() { int result = 0; Enumeration<Rental> rentals = _rentals.elements(); while (rentals.hasMoreElements()) { Rental each = (Rental) rentals.nextElement(); result += each.getFrequentRenterPoints(); } return result; } public String statement() { Enumeration<Rental> rentals = _rentals.elements(); String result = "Rental Record for " + getName() + "\n"; while (rentals.hasMoreElements()) { Rental each = (Rental) rentals.nextElement(); // show figures for this rental result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(each.getCharge()) + "\n"; } // add footer lines result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n"; result += "You earned " + String.valueOf(getTotalFrequentRenterPoints()) + " frequent renter points"; return result; }
Java
๋ณต์‚ฌ

7) ๋ถ„๊ธฐ๋ฌธ์„ ๋‹คํ˜•์„ฑ ๊ธฐ๋ฐ˜์˜ ๊ฐ์ฒด๋กœ ์ด์ „

โ€ข
์กฐ๊ฑด๋ฌธ ๋‚ด์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋ฐ์ดํ„ฐ์˜ ๊ฒฝ์šฐ, ์ผ๋ฐ˜์ ์œผ๋กœ ๊ฐ™์€ ๊ฐ์ฒด ๋‚ด ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
: ๊ฐ™์€ ๊ฐ์ฒด ๋‚ด ๋ฐ์ดํ„ฐ์™€ ๋ฉ”์†Œ๋“œ๊ฐ€ ๊ณต์กดํ•˜๋ฉด, ๋ฐ์ดํ„ฐ์— ์ˆ˜์ •์‚ฌํ•ญ ๋ฐœ์ƒ ์‹œ ํ•ด๋‹น ๋ฉ”์†Œ๋“œ์— ์‰ฝ๊ฒŒ ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
: getCharge ๋ฉ”์†Œ๋“œ์˜ ๊ฒฝ์šฐ Movie ํด๋ž˜์Šค ๋‚ด ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๋กœ ๋‹ค๋ฃจ๋ฏ€๋กœ, Movie ํด๋ž˜์Šค๋กœ ์ด๋™ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
: ๋ฉ”์†Œ๋“œ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์„ ํƒํ•  ๋•Œ, ๋ณ€๋™ ๊ฐ€๋Šฅ์„ฑ์ด ์ ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์„ ํƒํ•˜์—ฌ ์ถ”ํ›„ ์ˆ˜์ • ์‹œ ํŽธ๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. Type information์˜ ๊ฒฝ์šฐ ๋ณ€๋™์„ฑ์ด ํฌ๋ฏ€๋กœ, ๋ฉ”์†Œ๋“œ ๋‚ด ๋ฐ์ดํ„ฐ๋กœ ๋‚จ๊ธฐ๋Š” ๊ฒƒ์ด ํ–ฅํ›„ ๊ด€๋ฆฌ๋ฉด์—์„œ ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.
// Old Methods Class Rental... double getCharge() { return _movie.getCharge(_daysRented); } int getFrequentRenterPoints() { return _movie.getFrequentRenterPoints(_daysRented); } class movie...
Java
๋ณต์‚ฌ
// New Methods class Movie... double getCharge(int daysRented) { double result = 0; switch (getPriceCode()) { case Movie.REGULAR: result += 2; if (daysRented > 2) result += (daysRented - 2) * 1.5; break; case Movie.NEW_RELEASE: result += daysRented * 3; break; case Movie.CHILDRENS: result += 1.5; if (daysRented > 3) result += (daysRented - 3) * 1.5; break; } return result; } int getFrequentRenterPoints(int daysRented) { if ((getPriceCode() == Movie.NEW_RELEASE) && daysRented > 1) return 2; else return 1; }
Java
๋ณต์‚ฌ
โ€ข
๋ฉ”์†Œ๋“œ ์ด๋™ ์‹œ, ๊ธฐ์กด ๋ฉ”์†Œ๋“œ๋ฅผ ์™„์ „ํžˆ ์‚ญ์ œํ•˜์ง€ ์•Š์Œ์œผ๋กœ์จ ํƒ€์ž… ๋ฐ ์ธํ„ฐํŽ˜์ด์Šค ํ™˜๊ฒฝ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
: ๊ฐ™์€ ๊ฐ์ฒด ๋‚ด ๋ฐ์ดํ„ฐ์™€ ๋ฉ”์†Œ๋“œ๊ฐ€ ๊ณต์กดํ•˜๋ฉด, ๋ฐ์ดํ„ฐ์— ์ˆ˜์ •์‚ฌํ•ญ ๋ฐœ์ƒ ์‹œ ํ•ด๋‹น ๋ฉ”์†Œ๋“œ์— ์‰ฝ๊ฒŒ ๋ฐ˜์˜ ๊ฐ€๋Šฅ
: getCharge method์˜ ๊ฒฝ์šฐ Movie class ๋‚ด ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๋กœ ๋‹ค๋ฃจ๋ฏ€๋กœ Movie class๋กœ ์ด๋™ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Œ
: ๋ฉ”์†Œ๋“œ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์„ ํƒํ•  ๋•Œ, ๋ณ€๋™ ๊ฐ€๋Šฅ์„ฑ์ด ์ ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์„ ํƒํ•˜์—ฌ ์ถ”ํ›„ ์ˆ˜์ • ์‹œ ํŽธ๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ. Type information์˜ ๊ฒฝ์šฐ ๋ณ€๋™์„ฑ์ด ํฌ๋ฏ€๋กœ, ๋ฉ”์†Œ๋“œ ๋‚ด ๋ฐ์ดํ„ฐ๋กœ ๋‚จ๊ธฐ๋Š” ๊ฒƒ์ด ํ–ฅํ›„ ๊ด€๋ฆฌ๋ฉด์—์„œ ํšจ์œจ์ 

8) ์ƒ์† ๊ตฌ์กฐ ๊ธฐ๋ฐ˜์˜ ํด๋ž˜์Šค ์„ธ๋ถ„ํ™”

โ€ข
switch๋ฌธ์„ ๋ถ„ํ• ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๊ฐ Movie type์— ๋Œ€ํ•ด Movie๋ฅผ ์ƒ์† ๋ฐ›๋Š” ํ•˜์œ„ ํด๋ž˜์Šค๋กœ ์„ธ๋ถ„ํ™”ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
โ€ข
ํ•˜์ง€๋งŒ ๋ฌธ์ œ์ ์€ Movie ํด๋ž˜์Šค์˜ price ๊ด€๋ จ ๋ฉ”์†Œ๋“œ๋“ค์„ ๋ชจ๋“  ํ•˜์œ„ ํด๋ž˜์Šค์™€ ๋ถ€๋ชจ ํด๋ž˜์Šค(Movie)๊ฐ€ ๊ฐ์ž ๊ด€๋ฆฌํ•˜๋‹ค๋ณด๋ฉด ์ถ”ํ›„ ์œ ์ง€๋ณด์ˆ˜์— ํฐ ์–ด๋ ค์›€์„ ๊ฒช์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.
: price์™€ ๊ด€๋ จ๋œ class๋ฅผ ์ถ”๊ฐ€ ์ƒ์„ฑํ•˜์—ฌ Movie ๊ด€๋ จ ํด๋ž˜์Šค๋“ค์ด price class์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ•จ
โ€ข
switch๋ฌธ์— ๋Œ€ํ•ด Movie์˜ ์ž์‹ ํด๋ž˜์Šค๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์€ ์•„๋ž˜ ์„ธ ๋‹จ๊ณ„๋ฅผ ๊ฑฐ์นฉ๋‹ˆ๋‹ค.
1๋‹จ๊ณ„: Replace Type Code with State/Strategy
โ†’ Price class๋ฅผ abstract class๋กœ ์ƒ์„ฑ ๋ฐ ์ดํ•˜ priceCode๋ณ„ ์ž์‹ class ์ƒ์„ฑ
โ†’ ๋ชจ๋“  ์ž์‹ ํด๋ž˜์Šค์— getPriceCode ๋ฉ”์†Œ๋“œ ์ƒ์„ฑ
โ†’ Movie์™€ Price class ๊ฐ„ ์ ‘์  ๋ฉ”์†Œ๋“œ ์ƒ์„ฑ(setPriceCode)
2๋‹จ๊ณ„: Move Method
โ†’ getCharge ๋ฉ”์†Œ๋“œ๋ฅผ Movie ํด๋ž˜์Šค์—์„œ Price ํด๋ž˜์Šค๋กœ ์ด๋™
3๋‹จ๊ณ„: Replace Conditional with Polymorphism
โ†’ getCharge, getFrequentRenterPoints ๋‘ ๋ฉ”์†Œ๋“œ์— ๋Œ€ํ•ด ๋‹คํ˜•์„ฑ์„ ํ™œ์šฉํ•˜์—ฌ Price class ๋‚ด ์ž์‹ ํด๋ž˜์Šค์˜ ํ•˜์œ„ ๋ฉ”์†Œ๋“œ๋กœ ๋ถ„๋ฆฌ

9) ๋ฆฌํŒฉํ„ฐ๋ง ๊ฒฐ๊ณผ ๋น„๊ต

โ€ข
์ฒซ๋ฒˆ์งธ ๋‹ค์ด์–ด๊ทธ๋žจ๊ณผ ๋น„๊ตํ•ด๋ณด๋ฉด ์ƒ๋‹น๋ถ€๋ถ„์—์„œ ์ฐจ์ด ํ™•์ธ
โ€ข
example code ๋ฐ”๋กœ๊ฐ€๊ธฐ(https://github.com/MJbae/Refactoring_JAVA/tree/chapter1_refactoring)
โ€ข
Before
โ€ข
After