λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
  • Dev Blog
CS/λ””μžμΈ νŒ¨ν„΄

[GoF λ””μžμΈ νŒ¨ν„΄] - νŒ©ν† λ¦¬ λ©”μ„œλ“œ νŒ¨ν„΄

by μœ μ§„μ˜ 2024. 5. 28.
λͺ¨λ“  μ½”λ“œλŠ” Swift둜 μž‘μ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

 

 

생성 νŒ¨ν„΄

생성 νŒ¨ν„΄μ΄λž€ GoF λ””μžμΈ νŒ¨ν„΄ 쀑 κ°μ²΄λ₯Ό μƒμ„±ν•˜λŠ” 절차λ₯Ό μΆ”μƒν™”ν•˜λŠ” 5가지 λ””μžμΈ νŒ¨ν„΄μ„ λΆ„λ₯˜ν•œ 것을 μΌμ»«λŠ” μš©μ–΄μ΄λ‹€. 생성 νŒ¨ν„΄μ€ ν˜„λŒ€μ˜ 객체 지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ΄ 상속보닀 μ»΄ν¬μ§€μ…˜μ„ μ‚¬μš©ν•˜λŠ” λ°©ν–₯으둜 μ§„ν™”ν•˜λ©΄μ„œ λ”μš± μ€‘μš”ν•΄μ§€κ³  μžˆλ‹€. (μ‹€μ œλ‘œ κ΅¬κΈ€μ—μ„œ κ°œλ°œν•œ ν”„λ‘œκ·Έλž˜λ° 언어인 Golang 은 μ•„μ˜ˆ μƒμ†μ΄λž€ κ°œλ…μ΄ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ”λ‹€.)

 

 

πŸ“Œ 상속과 μ»΄ν¬μ§€μ…˜

 

상속은 Animal - Cat, Dog ν˜Ήμ€ Man - Son, Husband, BoyFriend와 같이 IS-A 관계일 λ•Œ 객체의 κΈ°λŠ₯을 μ½”λ“œμ˜ 쀑볡 없이 ν™•μž₯μ‹œν‚¬ 수 μžˆλ‹€.

μ»΄ν¬μ§€μ…˜μ€ Computer - Monitor, Keyboard, Mouse와 같이 HAS-A 관계일 λ•Œ 각각의 객체듀을 μ‘°ν•©ν•˜μ—¬ 객체의 κΈ°λŠ₯을 ν™•μž₯μ‹œν‚¬ 수 μžˆλ‹€.

 

μœ„μ—μ„œ μ†Œκ°œν•œ κ΅κ³Όμ„œμ  μ˜ˆμ‹œλ‘œλŠ” IS-A 관계와 HAS-A 관계λ₯Ό λͺ…ν™•ν•˜κ²Œ νŒλ‹¨ν•  수 μžˆμ§€λ§Œ, μ‹€μ œ μ†Œν”„νŠΈμ›¨μ–΄λ₯Ό κ°œλ°œν•  λ•Œμ—λŠ” μ΄λŸ¬ν•œ 관계듀에 λŒ€ν•œ νŒλ‹¨μ„ ν•˜κΈ° 쉽지 μ•Šλ‹€. 예λ₯Ό λ“€μ–΄, HttpResponseλΌλŠ” 객체의 κΈ°λŠ₯을 ν™•μž₯μ‹œμΌœ ClientHttpResponse, ServerHttpResponseλΌλŠ” 객체λ₯Ό λ§Œλ“ λ‹€κ³  생각을 ν•΄λ³΄μž. HttpResponse와 ClientHttpResponse/ServerHttpResponse의 관계가 IS-A 관계인지 HAS-A 관계인지 λͺ…ν™•ν•˜κ²Œ νŒλ‹¨ν•  수 μžˆλ‚˜?

 

class HttpResponse {}
class Client{}
class Server{}

class ClientHttpResponse: HttpResponse { // 상속
	private var client: Client
}

class ClientHttpResponse { // μ»΄ν¬μ§€μ…˜
	private var client: Client
	private var httpResponse: HttpResponse
}

class ServerHttpResponse: HttpResponse {
	private var server: Server
}

class ServerHttpResponse {
	private var server: Server
	private var httpResponse: HttpResponse
}

 

 

객체 지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ— λŒ€ν•œ 지식이 λΆ€μ‘±ν•œ μ‚¬λžŒλ“€μ€ λŒ€λΆ€λΆ„ 상속을 μ‚¬μš©ν•˜μ—¬ ClientHttpResponse/ServerHttpResponse 객체λ₯Ό λ§Œλ“€ 것이닀. 상속은 μ½”λ“œμ˜ 쀑볡 없이 효과적으둜 κΈ°λŠ₯을 ν™•μž₯μ‹œν‚¬ 수 μžˆλŠ” κ°•λ ₯ν•œ κ°œλ…μΈλ°, μ •ν™•ν•œ 객체 섀계가 이루어지지 μ•Šμ€ μƒνƒœμ—μ„œ λ¬΄ν„±λŒ€κ³  상속을 μ‚¬μš©ν•œλ‹€λ©΄ 객체 κ°„μ˜ 결합도가 높아지고 μΊ‘μŠν™”λ₯Ό ν•΄μΉ  수 μžˆλŠ” 문제점이 λ°œμƒν•œλ‹€.

 

λ”°λΌμ„œ, λͺ…ν™•ν•˜κ²Œ IS-A 관계라고 νŒλ‹¨ν•  수 μ—†λŠ” κ²½μš°μ—λŠ” 상속보단 μ»΄ν¬μ§€μ…˜μ„ μ‚¬μš©ν•˜λŠ” 것이 더 μœ λ¦¬ν•˜λ‹€.

 

 

νŒ©ν† λ¦¬ νŒ¨ν„΄

νŒ©ν† λ¦¬ νŒ¨ν„΄μ€ 객체의 생성 λ‘œμ§μ„ μΆ”μƒν™”ν•˜λŠ” 생성 νŒ¨ν„΄μ˜ 근간이 λ˜λŠ” νŒ¨ν„΄μ΄λ‹€. μ΄λ¦„μ—μ„œ μ•Œ 수 μžˆλ“―μ΄ μ—¬λŸ¬ 객체듀을 μƒμ„±ν•˜λŠ” 곡μž₯ 객체λ₯Ό λ§Œλ“€μ–΄ μ‚¬μš©ν•˜λŠ” νŒ¨ν„΄μ΄λ‹€. AnimalFactoryλΌλŠ” 곡μž₯ 객체λ₯Ό μ˜ˆμ‹œλ₯Ό λ“€μ–΄ μ½”λ“œλ‘œ 확인해 보자.

 

AnimalFactory 객체 μ½”λ“œ

더보기
enum AnimalType {
  case dog, cat
}

class AnimalFactory { // 곡μž₯ 객체
  func createAnimal(_ animalType: AnimalType) -> Animal {
    switch animalType {
    case .cat:
      return Cat()
    case .dog:
      return Dog()
    }
  }
}

protocol Animal {
  func speak()
}

class Cat: Animal {
  func speak() {
    print("meow")
  }
}

class Dog: Animal {
  func speak() {
    print("bark!")
  }
}

 

AnimalFactory 객체 μ‚¬μš© μ˜ˆμ‹œ

더보기
class Zoo {
    private var animals = [Animal]()
    private var animalFactory = AnimalFactory()

    func addAnimal(_ animalType: AnimalType) {
        let newAnimal = factory.createAnimal(animalType) // 객체 생성 과정을 좔상화
        animals.append(newAnimal)
    }

    func speakAll() {
        animals.forEach { $0.speak() }
    }
}

let zoo = Zoo()

zoo.addAnimal(AnimalType.cat)
zoo.addAnimal(AnimalType.dog)
zoo.speakAll()

 

μœ„ μ½”λ“œμ—μ„œ 확인해 λ³Ό 수 μžˆλ“―μ΄ ν΄λΌμ΄μ–ΈνŠΈ κ°μ²΄μ—μ„œ 직접 μ„œλ²„ 객체의 μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜λŠ” 것이 μ•„λ‹ˆλΌ νŒ©ν† λ¦¬ κ°μ²΄μ—κ²Œ κ·Έ μž‘μ—…μ„ 맑긴닀. 이에 따라 μ„œλ²„ 객체의 생성 둜직과 ν΄λΌμ΄μ–ΈνŠΈ 객체의 λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ λΆ„λ¦¬ν•¨μœΌλ‘œμ¨ 단일 μ±…μž„ 원칙을 μ€€μˆ˜ν•  수 있게 되고, μ„œλ²„ 객체 생성 방법을 μžμ„Ένžˆ μ•Œμ§€ μ•Šμ•„λ„ μ›ν•˜λŠ” μ„œλ²„ 객체λ₯Ό μš”μ²­λ§Œ ν•˜λ©΄ μ‰½κ²Œ λ°›μ•„μ˜¬ 수 μžˆλ‹€.

 

 

νŒ©ν† λ¦¬ λ©”μ„œλ“œ νŒ¨ν„΄

νŒ©ν† λ¦¬ λ©”μ„œλ“œ νŒ¨ν„΄μ— λŒ€ν•΄ μ„€λͺ…ν•˜κΈ° 이전에 νŒ©ν† λ¦¬ νŒ¨ν„΄μ˜ λ¬Έμ œμ λΆ€ν„° μ•Œμ•„λ³΄μž. AnimalFactory 객체의 createAnimal λ©”μ„œλ“œμ˜ μ½”λ“œμ—μ„œ 객체 생성 과정을 λΆ„κΈ° 처리 ν•˜κ³  μžˆμŒμ„ 확인해 λ³Ό 수 μžˆλ‹€. λΆ„κΈ° 처리 μ½”λ“œ 같이 경직도가 높은 μ½”λ“œλŠ” 개방-폐쇄 원칙을 μ€€μˆ˜ν•˜μ§€ λͺ»ν•œλ‹€. 즉, CowλΌλŠ” Animal 객체가 μΆ”κ°€λœλ‹€λ©΄ λΆ„κΈ° 처리 문을 μˆ˜μ •ν•΄μ€˜μ•Ό ν•œλ‹€.

μ΄λŸ¬ν•œ 경직도 높은 μ½”λ“œλ₯Ό 객체 지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ—μ„œλŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό ν™œμš©ν•˜μ—¬ ν•œλ²ˆ 더 μΆ”μƒν™”ν•˜μ—¬ λ‹€ν˜•μ„±μ„ μ΄μš©ν•˜λŠ” λ°©μ‹μœΌλ‘œ ν•΄κ²°ν•œλ‹€.

 

νŒ©ν† λ¦¬ λ©”μ„œλ“œ νŒ¨ν„΄μ΄ λ°”λ‘œ νŒ©ν† λ¦¬ νŒ¨ν„΄μ—μ„œ 곡μž₯ 객체λ₯Ό μΈν„°νŽ˜μ΄μŠ€λ‘œ ν•œλ²ˆ 더 μΆ”μƒν™”ν•˜μ—¬ λ‹€ν˜•μ„±μ„ μ΄μš©ν•˜λŠ” λ””μžμΈ νŒ¨ν„΄μ΄λ‹€.

 

UML 클래슀 λ‹€μ΄μ–΄κ·Έλž¨

 

https://yagom.net/courses/design-pattern-in-swift/lessons/생성-νŒ¨ν„΄/topic/factory-method/

 

  • Creator – Factory의 κΈ°λ³Έ 역할을 μ •μ˜ν•˜λŠ” 객체
  • Concrete Creator – Creatorλ₯Ό μ±„νƒν•˜κ³  있으며 Product에 λ§žλŠ” ꡬ체적 κΈ°λŠ₯을 κ΅¬ν˜„
  • Product – Concrete Productκ°€ ν•΄μ•Ό ν•  λ™μž‘λ“€μ„ μ„ μ–Έν•˜λŠ” 객체
  • Concrete Product – Productλ₯Ό μ±„νƒν•˜λ©° 그에 맞게 λ§Œλ“  μ‹€μ œ 객체

 

μ½”λ“œ μ˜ˆμ‹œ

Product와 Concrete Prouct

protocol Animal { // Product
  func speak()
}

class Cat: Animal { // Concrete Product
  func speak() {
    print("meow")
  }
}

class Dog: Animal { // Concrete Product
  func speak() {
    print("bark")
  }
}

 

Creator와 Concrete Creator

protocol AnimalFactory {  // Creator
  func createAnimal() -> Animal // νŒ©ν† λ¦¬ λ©”μ„œλ“œ μ„ μ–Έ
}

class CatCreator: AnimalFactory { // Concrete Creator
  func createAnimal() -> Animal {  // νŒ©ν† λ¦¬ λ©”μ„œλ“œ κ΅¬ν˜„
    return Cat()
  }
}

class DogCreator: AnimalFactory { // Concrete Creator
  func createAnimal() -> Animal { // νŒ©ν† λ¦¬ λ©”μ„œλ“œ κ΅¬ν˜„
    return Dog()
  }
}

 

ν΄λΌμ΄μ–ΈνŠΈ 객체 μ‚¬μš© μ˜ˆμ‹œ

class Zoo {
  private var animals = [Animal]()

  func addAnimal(creator: AnimalFactory) {
    let newAnimal = creator.createAnimal() // Concrete Creator μ‚¬μš©
    animals.append(newAnimal)
  }

  func speakAll() {
    animals.forEach { $0.speak() }
  }
}

var zoo = Zoo()

zoo.addAnimal(DogCreator())
zoo.addAnimal(CatCreator())
zoo.speakAll()

 

μž₯점

νŒ©ν† λ¦¬ νŒ¨ν„΄ μ½”λ“œμ™€ λΉ„κ΅ν•΄μ„œ 보면 λΆ„κΈ° 처리문이 사라진 것을 확인해 λ³Ό 수 μžˆλ‹€. 이에 따라 λ§Œμ•½, CowλΌλŠ” Animal 객체λ₯Ό μΆ”κ°€ν•˜λ € ν•  λ•Œ, Concrete Product와 Concrete Creator 객체만 ν•˜λ‚˜μ”© λ§Œλ“€μ–΄μ£Όλ©΄ μ›λž˜ μ‘΄μž¬ν•˜λŠ” μ½”λ“œλ₯Ό μˆ˜μ •ν•˜λŠ” 일은 λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€. 즉, 개방-폐쇄 원칙을 μ€€μˆ˜ν•˜κ²Œ λœλ‹€.

 

단점

정말 λŸ¬ν”„ν•˜κ²Œ λ§ν•œλ‹€λ©΄, λΆ„κΈ° μ²˜λ¦¬λ¬Έμ„ μ‚¬μš©ν•˜μ§€ μ•ŠμœΌλ €κ³  Creatorλ₯Ό μ •μ˜ν•΄ μ£Όκ³  Concrete Creator κ°μ²΄κΉŒμ§€ μƒμ„±ν•΄μ€˜μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— 배보닀 배꼽이 더 μ»€μ§€λŠ” κ²½μš°κ°€ λ°œμƒν•  수 μžˆλ‹€.

λŒ“κΈ€