티스토리 뷰

반응형

안녕하세요 Pingu입니다.🐧

 

지난 글에서는 Builder Pattern에 대해 알아봤는데요, 이번 글에서는 또 다른 Creational Pattern 중 하나인 Factory Method Pattern(팩토리 메서드)에 대해 알아보도록 하겠습니다.

팩토리 메서드 패턴이란?

객체를 만들기 위한 인터페이스를 정의하지만, 어떤 클래스의 인스턴스를 생성할지에 대한 결정은 하위 클래스가 정하도록 하는 방법입니다.

간단하게 말해서 객체 생성을 서브 클래스가 하도록 처리하는 패턴입니다.

 

즉 객체 생성을 캡슐화할 수 있으며 이로 인해 부모 클래스는 자식 클래스가 어떤 객체를 생성하는지 몰라도 됩니다.

팩토리 메서드 패턴은 위와 같은 구조를 갖습니다.

 

  • Product
    • Creator와 하위 클래스가 생성할 수 있는 모든 객체에 동일한 인터페이스를 선언합니다.
  • Concreate Product
    • Product가 선언한 인터페이스로 만든 실제 객체입니다.
  • Creator
    • 새로운 객체를 반환하는 팩토리 메서드를 선언합니다.
    • 여기서 반환하는 객체는 Product 인터페이스를 준수하고 있어야 해요.
  • Concrete Creator
    • 기본 팩토리 메서드를 override 하여 서로 다른 Product 객체를 만듭니다.

 

즉 팩토리 메서드 패턴은 객체를 만드는 Factory를 만드는 패턴이라고 할 수 있어요.

팩토리 메서드 패턴은 언제 쓰나요?

팩토리 메서드 패턴을 사용하는 예를 들어볼게요. 예를 들어 음악 재생 앱을 만들었다고 해보겠습니다. 처음 앱을 만들 땐 MusicPlayer라는 객체를 통해 음악만 재생 가능한 앱을 만들었는데, 앱이 잘 팔려서 비디오도 넣어야 하는 상황이 된거예요. 현재 음악만 재생할 수 있도록 만든 MusicPlayer만 있었기 때문에 만약 VideoPlayer를 추가하려고 한다면 코드 전체를 수정해야합니다. 또한 나중에 VR Player, AR Player 등과 같은 기능들이 추가된다면 추가 될 때 마다 코드 전체를 수정해야하는 문제가 생기겠죠?

 

이럴 때 사용할 수 있는 패턴이 팩토리 메서드 패턴입니다. 객체를 직접 만드는 것이 아닌 팩토리 메서드를 통해서 만드는 거에요. 아까의 예로 다시 돌아가 보면, MusicPlayer가 필요한 곳에서는 MusicPlayer를 생성하는 메서드를 호출하고 VideoPlayer가 필요한 곳에서는 VideoPlayer를 생성하는 메서드를 호출하는 거죠.

 

이렇게 된다면 새로운 유형의 Player를 만들어야 한다고 하더라도 팩토리 부분에 메서드를 추가해서 간단하게 처리할 수 있습니다. 하지만 각각의 Player들은 서로 다른 콘텐츠를 취급하더라도 궁극적인 기능은 같기 때문에 동일한 인터페이스를 사용해야 할 거예요.

팩토리 메서드 패턴의 결과

  • 새로운 기능이 추가되더라도 팩토리 메서드를 사용하여 새로운 객체를 만들 수 있습니다.
  • 기존 객체에 수정이 생기더라도 팩토리 메서드만 수정하면 됩니다.

예제

그럼 실제로 팩토리 메서드 패턴을 간단한 예제를 통해 사용해보도록 하겠습니다.

아까 예로 들었던 음악, 비디오 앱에 사용할 플레이어 객체를 만드는 것을 Swift 언어로 간단하게 만들어보겠습니다.

 

일단 Product에 해당하는 Player 프토토콜부터 만들어 볼게요.

// Product Interface
protocol Player {
    var content: String { get set }
    init(content: String)
    func play()
    func pause()
    func changeContent(name: String)
}

이렇게 만들어진 프로토콜을 채택하는 Concrete Product들이 바로 MusicPlayer, VideoPlayer겠죠?

// Concrete Product
class MusicPlayer: Player {
    var content: String
    required init(content: String) {
        self.content = content
    }
    
    func play() {
        print("MusicPlayer Play")
    }
    
    func pause() {
        print("MusicPlayer Pause")
    }
    
    func changeContent(name: String) {
        print("\(self.content)에서 \(name)로 음악 변경")
        self.content = name
    }
}
// Concrete Product
class VideoPlayer: Player {
    var content: String
    required init(content: String) {
        self.content = content
    }
    
    func play() {
        print("VideoPlayer Play")
    }
    
    func pause() {
        print("VideoPlayer Pause")
    }
    
    func changeContent(name: String) {
        print("\(self.content)에서 \(name)로 비디오 변경")
        self.content = name
    }
}

 

그럼 이젠 Creator를 만들어야 합니다. Creator는 특정 프로토콜을 채택한 객체만 반환할 수 있으니 지금 예제에서는 Player 프로토콜을 채택한 객체들을 반환하면 됩니다.

// Creator
protocol PlayerCreator {
    func createPlayer(content: String) -> Player
}

 

그럼 드디어 Factory를 만들면 됩니다.

// Factory (Concrete Creator)
struct MusicPlayerFactory: PlayerCreator {
    func createPlayer(content: String) -> Player {
        return MusicPlayer(content: content)
    }
}

struct VideoPlayerFactory: PlayerCreator {
    func createPlayer(content: String) -> Player {
        return VideoPlayer(content: content)
    }
}

이렇게 만들었다면 이제 Player 객체를 만들 때 Factory 객체를 통해서 만들 수 있게 됩니다.

 

그럼 한 번 실제로 만든 팩토리로 객체를 만들어보겠습니다.

 

이렇게 손쉽게 서로 다른 Player를 만들어서 사용할 수 있게 되었습니다.

만약 다른 Player를 추가하고 싶다면 기존 코드는 전혀 수정할 필요 없이 새로운 타입을 추가하고 새로운 팩토리를 추가해주기만 하면 됩니다.

 

만약 어떤 프로그램에서 100개의 플레이어를 사용한다고 한다면, 기존 코드는 그대로 둔 채 새로운 타입과 팩토리만 추가하면 되게 해주는 패턴이 팩토리 메서드 패턴입니다.

 

이렇게 팩토리 메서드 패턴을 직접 구현해봤는데요, 도움이 되셨으면 좋겠습니다!

전체 코드는 여기에서 볼 수 있습니다.

 

감사합니다.

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
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
글 보관함