์๋ ํ์ธ์ Foma ์ ๋๋ค!
์ค๋์ WWDC 2022์์ ์ ๋๋ฆญ๊ณผ ํ๋กํ ์ฝ์ ์ฌ์ฉํ์ฌ ์ ์(?)์ผ๋ก ์ฝ๋๋ฅผ ์ถ์ํํ๋ ๋ฐฉ๋ฒ์ ๋ํด ๋ค๋ฃฌ Embrace Swift generics ์ธ์ ์ ๋ํด ์ ๋ฆฌํด ๋ณด๋ ค๊ณ ํฉ๋๋ค.
๋ฐ๋ก ์์ํ ๊ฒ์~
(์ ๋ฐฉ์๋๋ก ํด์ํ์ฌ ์ ๋ฆฌํ๋ ๊ฒ์ด๋ ํ๋ฆฐ ์ ์ด๋ ๊ถ๊ธํ ์ ์ด ์๋ค๋ฉด ์ธ์ ๋ ๋๊ธ๋ก ์๋ ค์ฃผ์ธ์!)
Model with concrete types
๊ฐ์ฅ ๋จผ์ ์ด๋ ํ ๊ฒ๋ ์ถ์ํ๋ ์ ๋๋ฆญ์ ์ฌ์ฉํ์ง ์๊ณ ์๋ ๊ทธ๋๋ก ์ฆ, ๊ตฌ์ฒด์ ์ธ ํ์ ์ ์ด์ฉํด์ ๋์ฅ ์์คํ ์ ๊ตฌํํด ๋ณด๊ฒ ์ต๋๋ค.
๋์ฅ์ ์๊ฐ ์๋ค๊ณ ๊ฐ์ ํ๊ณ , ๊ทธ ์๋ ๊ฑด์ด(Hay)๋ฅผ ๋จน๋ struct๋ฅผ ์์ฑํด ์ฃผ๊ฒ ์ต๋๋ค.
struct Cow {
func eat(_ food:Hay) {...}
}
์ด์ ์๊ฐ ๋จน๋ ๊ฑด์ด๋ ์๋ผ๋ฉด ์ํํ(Alfalfa)๋ผ๋ ์๋ฌผ์ด ๋๋ struct๋ฅผ ์์ฑํด ์ค๋๋ค.
struct Hay {
static func grow() -> Alfalfa {}
}
์์์ ๊ฑด์ด๊ฐ ์๋ผ๋ฉด ์ํํ๊ฐ ๋๋ค๊ณ ํ์ฃ ? ๊ณ ๋ก ์ํํ๋ฅผ ์ํํ๋ฉด ๊ฑด์ด๊ฐ ๋๋ ์ํํ struct๋ฅผ ์์ฑํด ์ค๋๋ค.
struct Alfalfa {
func havest() -> Hay {}
}
๋ง์ง๋ง์ผ๋ก ์ ์ธ ๊ฐ์ struct๋ฅผ ์ด์ฉํ์ฌ ๋์ฅ struct๋ฅผ ๋ง๋ค์ด ์ค๋๋ค.
์์๊ฒ ๋จน์ด๋ฅผ ์ฃผ๊ธฐ ์ํด์
1. ์ํํ(๊ฑด์ด์ ์์ฌ๋ฃ)๋ฅผ ๊ธฐ๋ฅธ๋ค.
2. ์ํํ๋ฅผ ์ํํ์ฌ ๊ฑด์ด๋ฅผ ๋ง๋ ๋ค.
3. ๊ฑด์ด๋ฅผ ์์๊ฒ ๋จน์ธ๋ค.
struct Farm {
func feed(_ animal: Cow) {
let alfalfa = Hay.grow() // 1
let hay = alfalfa.harvest() // 2
animal.eat(hay) // 3
}
}
ํ์ง๋ง ๋ง์ฝ ์ฌ๊ธฐ์ ๋๋ฌผ์ด ์ ๋ฟ๋ง ์๋๋ผ ๋ง์ด๋ ๋ญ์ด ์ถ๊ฐ๋๋ค๋ฉด ์ฝ๋๋ฅผ ์ด๋ป๊ฒ ์์ฑํด์ผ ํ ๊น์?
struct Horse {
func eat(_ food: Carrot) {}
}
struct Chicken {
func eat(_ food: Grain) {}
}
์ถ๊ฐ๋ ๋๋ฌผ์ ์๋งํผ ๋์ฅ์ ๋จน์ด ์ฃผ๋ ๊ธฐ๋ฅ์ด ์๋ ์ฝ๋๋ฅผ ์ถ๊ฐ๋ก ์์ฑํ์ฌ ๊ตฌํํด์ผ ํ ๊ฒ์ ๋๋ค.
struct Farm {
//์ ๋จน์ด
func feed(_ animal: Cow) {
let alfalfa = Hay.grow()
let hay = alfalfa.harvest()
animal.eat(hay)
}
//๋ง ๋จน์ด
func feed(_ animal: Horse) {
let root = Carrot.grow()
let carrot = root.harvest()
animal.eat(carrot)
}
//๋ญ ๋จน์ด
func feed(_ animal: Chicken) {
let wheat = Grain.grow()
let grain = wheat.harvest()
animal.eat(grain)
}
}
farm.feed(Cow())
farm.feed(Horse())
farm.feed(Chicken())
ํ์ง๋ง ์ด๋ฐ ์์ผ๋ก ๊ตฌํํ๋ฉด ๋๋ฌผ์ ์ถ๊ฐํ๋ฉด ์ถ๊ฐํ ์๋ก ๊ฑฐ์ ๋น์ทํ ์ฝ๋๋ฅผ ๋ฐ๋ณต์ ์ผ๋ก ์์ฑํด์ผ ํ๋ ๋ฌธ์ ๊ฐ ์๊น๋๋ค.
Identify common capabilities
์ ๋ฌธ์ ๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํด์ ๊ฐ struct์์ ๊ณตํต์ ์ผ๋ก ๊ฐ์ง๊ณ ์๋ ํน์ง์ ๋์ดํฉ๋๋ค.
๋๋ฌผ๋ค์ด ๊ณตํต์ ์ผ๋ก ๊ฐ์ง๊ณ ์๋ ํน์ง์ ํน์ ๋จน์ด๋ฅผ ๋จน๋ ๊ฒ์ด์ฃ ?
๊ทธ๋ฌ๋ฏ๋ก ๋๋ฌผ์ด๋ผ๋ ํด๋์ค๋ฅผ ๋ง๋ค์ด eat ๋ฉ์๋๋ฅผ ๊ตฌํํ์ฌ ์ถ์ํํ์ฌ ๋๋ฌผ๋ง๋ค ๋ค๋ฅด๊ฒ ์ ์ฉํ๋ฉด ๋ ๊ฒ ์ ๋๋ค.
class Animal {
func eat(_ food:???) { fatalError("Subclass must implement 'eat'")}
}
Polymorphism
์ด๋ ๊ฒ ํ ์ฝ๋๋ก ์ฌ๋ฌ ํ๋์ ์ํํ๋ ๊ฒ์ "Polymorphism" ์ด๋ผ๊ณ ๋ถ๋ฅด๋๋ฐ์.
ํด๋ฆฌ ๋ชฐํผ์ฆ์ ์ธ ๊ฐ์ง ํํ๊ฐ ์กด์ฌํ๋๋ฐ์.
1. ์ค๋ฒ๋ก๋๋ฅผ ํตํ ad-hoc polymorphism
2. ์๋ธํ์ ์ ์ด์ฉํ subtype polymorphism
3. ์ ๋๋ฆญ์ ์ด์ฉํ parametric polymorphism
๊ฐ์ฅ ๋จผ์ ์ค๋ฒ๋ก๋๋ฅผ ์ด์ฉํด์ ์ฝ๋๋ฅผ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค.
์์์ ๋ง๋ค์ด ์ค ๋๋ฌผ struct๋ค์ ํด๋์ค๋ก ๋ฐ๊ฟ์ฃผ๊ณ Animal ํด๋์ค๋ฅผ ์์ ๋ฐ์ ๋ฉ์๋๋ฅผ override ํด์ค๋๋ค.
class Cow: Animal {
override func eat(_ food:Hay) {}
}
Using Any
ํ์ง๋ง ์์์ ๋ง๋ค์ด์ค Animal ํด๋์ค์ food ํ์ ์ ๊ตฌ์ฒด์ ์ผ๋ก ์ ํ ์๊ฐ ์์ต๋๋ค.
๊ณ ๋ก ๊ทธ๋๋ง ๊ด์ฐฎ์ ๋ฐฉ๋ฒ์ food๋ฅผ Any ํ์ ์ผ๋ก ๋ง๋ค์ด ์ฃผ๋ ๊ฒ ์ ๋๋ค.
class Animal {
func eat(_ food:Any) { fatalError("Subclass must implement 'eat'")}
}
class Chicken: Animal {
override func eat(_ food: Any) {
guard let food = food as? Grain else { fatalError("Chicken cannot eat \(food)")}
}
}
ํ์ง๋ง ์ด ๋ํ ํ์ ์ด ๋ง์ง ์๋ ์์์ด ๋ค์ด์์ ๊ฒฝ์ฐ ์ค๋ก์ง ๋ฐํ์์๋ง ๋ฒ๊ทธ๊ฐ ๋ฐ๊ฒฌ๋ ์ ์์ต๋๋ค.
Using type parameter
์ ์ฝ๋๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํด์ Any๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ์ํผ ํด๋์ค์ธ Animal์์ ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฐ์ ๋จน์ด์ ํ์ ์ ๊ฒฐ์ ํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
์๋์ ๊ฐ์ด Animal์ Food๋ฅผ ํ์ ์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ๊ฒ ํ๊ณ , ์์๋ฐ์ ํ์ ํด๋์ค์ ํน์ ๋จน์ด ํ์ ์ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ ฅํด ๊ตฌ์ฒด์ ์ธ ํ์ ์ ๊ฐ๋๋ก ํ๋ ๊ฒ์ด์ฃ .
class Animal<Food> {
func eat(_ food:Food) { fatalError("Subclass must implement 'eat'")}
}
class Chicken: Animal<Grain> {
override func eat(_ food: Grain) {}
}
์ด๋ ๊ฒ ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ด์ฉํด ์์ฑํ๋ ๊ฒ์ ๋จน์ด๋ฅผ ๋จน๋ ํ๋์ด ์๋ ๋ค๋ฅธ ๊ธฐ๋ฅ์ ๊ตฌํํ ๋๋ ํด๋น ํ์ ํ๋ผ๋ฏธํฐ์ ์ถ๊ฐ๋ก ์์ฑํด ์ค์ผํ๋ค๋ ๋ฌธ์ ์ ์ด ๋ฐ์ํฉ๋๋ค.
์๋์ ๊ฐ์ด ๋๋ฌผ์ด ๋จน๋ ์์ ๋ฟ๋ง ์๋๋ผ, ์ฌ๋ ๊ณณ, ์ด๋ค ๋ฌผํ์ ๋ง๋ค์ด ๋ด๋์ง ๋ฑ์ ์ถ๊ฐ์ ์ผ๋ก ๊ตฌํํ๋ ค๋ฉด
class Animal<Food, Habitat, Commodity> {
func eat(_ food:Food) { fatalError("Subclass must implement 'eat'")}
}
์๋์ ๊ฐ์ด ๋๋ฌผ๋ง๋ค ํ์ํ ํ์ ์ด <> ์์ ๋์ด๋๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
class Cow: Animal<Hay, Barn, Milk> {
override func eat(_ food:Hay) {}
}
Build an interface
๊ทธ๋ ๋ค๋ฉด ์ ๋ฌธ์ ๋ค์ ์ด๋ป๊ฒ ํด๊ฒฐํ ์ ์์๊น์?
๋ฐ๋ก ์ํํ๋ ์์ ์ ๋ํ ์์ด๋์ด๋ฅผ ๊ตฌํ ์ธ๋ถ ์ ๋ณด์ ๋ถ๋ฆฌํ๋ ๊ฒ, ์ฆ Protocol์ ์ฌ์ฉํ๋ ๊ฒ ์ ๋๋ค.
์๋์ ๊ฐ์ด Feed๋ผ๋ ์์์ ํ์ ์ ์ ํด๋๊ณ eat ๋ฉ์๋์ ์์์ ํ์ ์ ์ง์ ํด ์ค๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด ์ด๋ค ๊ตฌ์ฒด์ ์ธ ํ์ ์ด๋ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ ์ ์๋ ๊ฒ์ด์ฃ .
protocol Animal {
associatedtype Feed: FeedType
func eat(_ food: Feed)
}
๊ทธ๋ฆฌ๊ณ ๊ธฐ์กด Class๋ก ๋์ด ์๋ ๋๋ฌผ ํด๋์ค๋ค์ ๋ค์ struct๋ก ๋ณ๊ฒฝํ๊ณ ๋ฉ์๋์ override๋ฅผ ์ ๊ฑฐํด ์ค๋๋ค.
(ํ๋กํ ์ฝ์ enum,struct,actor ๋ฑ์๋ง ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.)
struct Horse: Animal {
func eat(_ food: Carrot) {}
}
Write generic code
๊ทธ๋ ๋ค๋ฉด ์ด์ ๋ถํฐ ๋ณธ๊ฒฉ์ ์ผ๋ก ์ ๋๋ฆญํ ์ฝ๋๋ฅผ ์์ฑํด ๋ณด๊ฒ ์ต๋๋ค.
๊ฐ์ฅ ๋ฌธ์ ๊ฐ ๋๋ Farm์ feed ์ฝ๋๋ฅผ ์ด๋ ํ ๋จน์ด๊ฐ ์๋ ํ ์ฝ๋๋ก ๋์ฒดํ ์ ์๊ฒ๋ ๋ง๋ค์ด์ผ ํ๋๋ฐ์.
์๋์ ๊ฐ์ด animal์ Animal ํํ ํ ์ฝ์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ผ๋ฉด ๋ ๊น์?
struct Farm {
func feed(_ animal: Animal){...}
}
ํ๋กํ ์ฝ๊ณผ ๊ฐ์ ๊ตฌ์ฒด์ ์ธ ํ์ ์ด ์๋ ๊ฒฝ์ฐ ๋ถํฌ๋ช ํ ํ์ (Opaque Type) ์ด๋ผ๊ณ ํ๋๋ฐ์.
๋ถํฌ๋ช ํ ํ์ ์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ๊ฑฐ๋, ๊ฒฐ๊ณผ๊ฐ์ผ๋ก ๋ฐํํ๋ ๊ฒ์ด ๋ถ๊ฐ๋ฅํฉ๋๋ค.
๊ทธ๋ ๋ค๋ฉด ์ด๋ป๊ฒ ์์ฑํด์ผ ํ ๊น์?
where
์ฐ์ ์ฒซ ๋ฒ์งธ๋ก๋ where ์ ์ ์ด์ฉํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
์๋์ ๊ฐ์ด A๋ผ๋ ์์ ํ์ ์ด๋ฆ์ ์ ํด์ฃผ๊ณ A๋ Animal์ด์ผ ๋ผ๋ ๊ฒ์ ์ง์ ํด ์ค ํ๋ผ๋ฏธํฐ๋ก Animal ํ๋กํ ์ฝ์ ๋ฐ๋ฅด๋ ๊ตฌ์ฒด์ ์ธ ํ์ ์ ๋ฐ์ ์ ์๋ ๊ฒ์ด์ฃ .
struct Farm {
func feed<A>(_ animal: A) where A:Animal{}
}
ํ์ง๋ง where์ ์ ๋ฉ์๋๊ฐ ์ค์ ๋ณด๋ค ๋ ๋ณต์กํด ๋ณด์ด๋ ๋จ์ ์ด ์กด์ฌํ์ต๋๋ค.
some
์ด๋ฌํ where์ ์ ๋จ์ ์ ๋ณด์ํ์ฌ ์ฝ๊ฒ ํํํ ์ ์๋ ๋ฐฉ๋ฒ์ด ๋ฐ๋ก "some" ์ ๋๋ค.
๋ฐ๋ก ์๋์ ๊ฐ์ด ์์ฑํ๋ฉด Animal ํ๋กํ ์ฝ์ ์ค์ํ๋ ํน์ ํ์ ์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ ์ ์๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค.
struct Farm {
func feed(_ animal: some Animal) {}
}
์ด๊ฒ์ SwiftUI๋ฅผ ๊ฒฝํํด ๋ณด์ ๋ถ๋ค์ ์๋์ฒ๋ผ some View๋ก ํ์๋๋ ๊ฒฝ์ฐ๋ฅผ ๋ง์ด ๋ณด์ จ์ ๊ฑฐ์์.
์ด ์๋ฏธ๋ ๋ฐ๋ก View๋ผ๋ ํ๋กํ ์ฝ์ ๋ฐ๋ฅด๋ ํน์ ํ์ ์ ์๋ฏธํ๋ ๊ฒ์ด์ฃ .
some์ ์ด์ฉํด์ feed ๋ฉ์๋๋ฅผ ์์ฑํ๋ฉด ์๋์ ๊ฐ์ด ์ด๋ค ๋๋ฌผ์ด ์ถ๊ฐ๋๋ ํ๋์ ๋ฉ์๋๋ก ๋์ฒํ ์ ์์ต๋๋ค.
struct Farm {
func feed(_ animal: some Animal) {
let crop = type(of: animal).FeedType.grow()
let produce = crop.harvest()
animal.eat(produce)
}
}
์ ๋ฉ์๋๋ฅผ ์ข ๋ ๊ตฌ์ฒด์ ์ผ๋ก ์ค๋ช ํ๋ฉด ๋จผ์ crop์ animal์ ํน์ ํ์ ์ ์์๋ธ ๋ค ํด๋น Feed์ grow ๋ฉ์๋๋ฅผ ํธ์ถํด Crop ์ธ์คํด์ค๋ฅผ ํ ๋นํฉ๋๋ค.
๊ทธ ๋ค์ ํด๋น crop์ ์ด์ฉํ์ฌ harvest ๋ฉ์๋๋ฅผ ์คํํด ๋จน์ด๋ฅผ ํ ๋นํ ๋ค animal์ eat๋ฉ์๋์ ํ๋ผ๋ฏธํฐ์ ํ ๋นํด ๋จน์ด๋ฅผ ์ฃผ๊ฒ ๋๋ ๊ฒ์ ๋๋ค.
(Feed๋ ์ง์ ์ ์ธ ๋จน์ด ์ฆ Hay, Grain, Carrot์ ์๋ฏธํ๋ฉฐ Crop์ ๊ทธ๊ฒ์ ์์ฌ๋ฃ๊ฐ ๋๋ Alfalfa, Wheat, Root๊ฐ ๋ฉ๋๋ค. ์์ธํ ๊ฑด ์๋ ๊นํ ์์ค์ฝ๋๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์!)
์๋ ํ๋กํ ์ฝ์ ๋ํด ์์ธํ ์ค๋ช ์ ์ฌ๊ธฐ ์์ ํ์ธํ ์ ์์ต๋๋ค.
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
}
any
๋ง์ฝ ์ฌ๋ฌ ๋๋ฌผ์๊ฒ ๋จน์ด๋ฅผ ์ฃผ๋ ค๋ฉด ์ด๋ค ์์ผ๋ก ์์ฑํด์ผ ํ ๊น์?
๊ฐ์ฅ ๋จผ์ ๋ ์ฌ๋ฆฌ๋ ๊ฒ์ ์๋์ ๊ฐ์ ๊ฒ ์ ๋๋ค.
func feedAll(_ animals: [Animal]) {
for animal in animals {
feed(animal)
}
}
ํ์ง๋ง ์์์ ํ๋กํ ์ฝ์ ํ์ ์ผ๋ก ์ง์ ํ ๋นํ์ง ๋ชปํ์ฃ ?
์ด๋ค ๋๋ฌผ์ด ๋ค์ด์ฌ์ง ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์ ์๋์ ๊ฐ์ด some Animal๋ก ์์ฑํด์ผ ํ ๊น์?
some์ ๊ธฐ๋ณธ ์ ํ์ด ๊ณ ์ ๋๊ณ ๋ฐฐ์ด์ ๋ชจ๋ ์์๊ฐ ๋์ผํ ์ ํ์ ๊ฐ์ ธ์ผ ํ๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ๋๋ฌผ์ ํ๊บผ๋ฒ์ ๊ฐ์ง ์ ์๊ฒ ๋ฉ๋๋ค.
func feedAll(_ animals: [some Animal]) {
for animal in animals {
feed(animal)
}
}โ
์ด๋ฌํ ๊ฒ์ ํด๊ฒฐํ ์ ์๋ ๋ฐฉ๋ฒ์ด ๋ฐ๋ก "any"๋ฅผ ์ฌ์ฉํ๋ ๊ฒ ์ ๋๋ค.
ํ๋กํ ์ฝ ์์ any ํค์๋๋ฅผ ๋ถ์ฌ์ฃผ๋ฉด ๋์ ์ผ๋ก ํ์ ์ ์ ์ฅํ๊ณ ๋ฐํ์ ์์ ํ์ธํ๊ธฐ ๋๋ฌธ์ ๋ชจ๋ ์ ํ์ ๋ฐฐ์ด์ ๋ด์ ์ ์์ต๋๋ค.
func feedAll(_ animals: [any Animal]) {
for animal in animals {
feed(animal)
}
}
์ฌ์ฉํ ๋๋ ์๋์ ๊ฐ์ด any์ ํจ๊ป ์ฌ์ฉํ๋ฉด ํ๋กํ ์ฝ์ ์ ํ ์ธ์คํด์ค๋ฅผ ๋ฐฐ์ด์ ๋ด์ ์ ์์ต๋๋ค.
let farm = Farm()
let animals:[any Animal] = [Horse(),Chicken(),Cow()]
farm.feedAll(animals)
Source Code
Reference
๋๊ธ