import UIKit
// 1) 건초를 먹는 소
struct Cow {
func eat(_ food: Hay) { }
}
// 2) 자라면 Alfalfa가 되는 건초
struct Hay {
static func grow() -> Alfalfa { return Alfalfa() }
}
// 3) 수확하면 건초가되는 Alfalfa
struct Alfalfa {
func harvest() -> Hay { return Hay() }
}
// 4) 농장에서는 소에게 음식을 준다.
struct Farm {
func feed(_ animal: Cow) {
let alfalfa = Hay.grow()
let hay = alfalfa.harvest()
animal.eat(hay)
}
}
import Foundation
// MARK: - 소
struct Cow {
func eat(_ food: Hay) { } // 소는 건초를 먹는다.
}
struct Hay {
static func grow() -> Alfalfa { return Alfalfa() } // 재배한다.
}
struct Alfalfa {
func harvest() -> Hay { return Hay() } // 수확하여 건초를 얻는다.
}
// MARK: - 닭
struct Chicken {
func eat(_ food: Grain) { }
}
struct Grain {
static func grow() -> Wheat { return Wheat()}
}
struct Wheat {
func harvest() -> Grain { return Grain() }
}
// MARK: - 말
struct Horse {
func eat(_ food: Carrot) { }
}
struct Carrot {
static func grow() -> Root { return Root() }
}
struct Root {
func harvest() -> Carrot { return Carrot() }
}
// MARK: - 농장
struct Farm {
func feed(_ animal: Cow) {
let alfalfa = Hay.grow()
let hay = alfalfa.harvest()
animal.eat(hay)
}
func feed(_ animal: Chicken) {
let wheat = Grain.grow()
let grain = wheat.harvest()
animal.eat(grain)
}
func feed(_ animal: Horse) {
let root = Carrot.grow()
let carrot = root.harvest()
animal.eat(carrot)
}
}
Identify Common Capabilities (공통 기능 식별하기)
Subtype polymorphism을 이용한 리팩토링
import Foundation
// 1) 소, 닭, 말을 표현할 수 있는 Animal class의 도입
// 2) 각 동물 struct를 class로 변경한다.
// 3) Animal class를 상속하여, eat 메서드를 재정의한다.
class Animal {
func eat(_ food: Any) {
fatalError("Subclass must implement 'eat'")
}
}
class Cow: Animal {
override func eat(_ food: Any) {
guard let food = food as? Hay else { fatalError("Cow can't eat \(food)") }
}
}
struct Hay {
static func grow() -> Alfalfa { return Alfalfa() } // 재배한다.
}
struct Alfalfa {
func harvest() -> Hay { return Hay() } // 수확하여 건초를 얻는다.
}
// MARK: - 닭
class Chicken: Animal {
override func eat(_ food: Any) {
guard let food = food as? Grain else { fatalError("Chicken can't eat \(food)") }
}
}
struct Grain {
static func grow() -> Wheat { return Wheat()}
}
struct Wheat {
func harvest() -> Grain { return Grain() }
}
// MARK: - 말
class Horse: Animal {
override func eat(_ food: Any) {
guard let food = food as? Carrot else { fatalError("Horse can't eat \(food)") }
}
}
struct Carrot {
static func grow() -> Root { return Root() }
}
struct Root {
func harvest() -> Carrot { return Carrot() }
}
let h = Horse()
h.eat(Carrot())
h.eat(Hay())
import Foundation
class Animal<Food> { // Food: type parameter
func eat(_ food: Food) { fatalError("Subclass must implement 'eat'") }
}
class Cow: Animal<Hay> {
override func eat(_ food: Hay) { }
}
struct Hay {
static func grow() -> Alfalfa { return Alfalfa() } // 재배한다.
}
struct Alfalfa {
func harvest() -> Hay { return Hay() } // 수확하여 건초를 얻는다.
}
// MARK: - 닭
class Chicken: Animal<Grain> {
override func eat(_ food: Grain) { }
}
struct Grain {
static func grow() -> Wheat { return Wheat()}
}
struct Wheat {
func harvest() -> Grain { return Grain() }
}
// MARK: - 말
class Horse: Animal<Carrot> {
override func eat(_ food: Carrot) { }
}
struct Carrot {
static func grow() -> Root { return Root() }
}
struct Root {
func harvest() -> Carrot { return Carrot() }
}
let c = Cow()
c.eat(Hay())
c.eat(Carrot()) // compile error!!
아이디어와 세부 구현을 분리해서 문제를 해결할 수 있어.
protocol을 이용하는거지.
func feed<A>(_ animal: A) { } // 정의 불가능
func feed<A: Animal)(_ animal: A) { }
func feed<A>(_ animal: A) where A: Animal { }
func feed(_ animal: some Animal) { }
import Foundation
// 1. 동물의 기능을 인터페이스로 표현한다.
protocol Animal {
associatedtype Feed: AnimalFeed // 임의의 먹이을 지정한다.
func eat(_ food: Feed) // 실제 구현은 상세 정보에서 구현한다. -> 어떤 타입이든 파라미터로 받을 수 있다.
}
protocol AnimalFeed {
associatedtype CropType: Crop where CropType.FeedType == Self
static func grow() -> CropType
}
protocol Crop {
associatedtype FeedType: AnimalFeed where FeedType.CropType == Self
func harvest() -> FeedType
}
// 2. 각 동물이 프로토콜을 따르도록 할 수 있다.
// 프로토콜은 클래스, 구조체, 열거형, actor 등에서도 사용할 수 있다.
struct Cow: Animal {
func eat(_ food: Hay) { }
}
struct Horse: Animal {
func eat(_ food: Carrot) { }
}
struct Chicken: Animal {
func eat(_ food: Grain) { }
}
struct Hay: AnimalFeed {
static func grow() -> Alfalfa { return Alfalfa() }
}
struct Alfalfa: Crop {
func harvest() -> Hay { return Hay() }
}
struct Grain: AnimalFeed {
static func grow() -> Wheat { return Wheat()}
}
struct Wheat: Crop {
func harvest() -> Grain { return Grain() }
}
struct Carrot: AnimalFeed {
static func grow() -> Root { return Root() }
}
struct Root: Crop {
func harvest() -> Carrot { return Carrot() }
}
struct Farm {
func feed(_ animal: some Animal) {
let crop = type(of: animal).Feed.grow()
let produce = crop.harvest()
animal.eat(produce)
}
}
struct Farm {
func feed(_ animal: some Animal) {
let crop = type(of: animal).Feed.grow()
let produce = crop.harvest()
animal.eat(produce)
}
func feedAll(_ animals: [any Animal]) {
for animal in animals {
feed(animal)
}
}
}