
PHP로 작성된 팩토리 메서드
팩토리 메서드는 제품 객체들의 구상 클래스들을 지정하지 않고 해당 제품 객체들을 생성할 수 있도록 하는 생성 디자인 패턴입니다.
팩토리 메서드는 메서드를 정의하며, 이 메서드는 직접 생성자 호출(new
연산자)을 사용하여 객체를 생성하는 대신 객체 생성에 사용되여야 합니다. 자식 클래스들은 이 메서드를 오버라이드하여 생성될 객체들의 클래스를 변경할 수 있습니다.
다양한 팩토리 패턴들과 개념들의 차이점을 이해하지 못하셨다면 팩토리 비교를 읽어보세요.
복잡도:
인기도:
사용 사례들: 팩토리 메서드 패턴은 PHP 코드에서 널리 사용되며 코드에 높은 수준의 유연성을 제공해야 할 때 매우 유용합니다.
식별: 팩토리 메서드는 구상 클래스들로부터 객체들을 생성하는 생성 메서드들로 인식될 수 있습니다. 구상 클래스들은 객체 생성 중에 사용되지만 팩토리 메서드들의 반환 유형은 일반적으로 추상 클래스 또는 인터페이스로 선언됩니다.
개념적인 예시
이 예시는 팩토리 메서드의 구조를 보여주고 다음 질문에 중점을 둡니다:
- 패턴은 어떤 클래스들로 구성되어 있나요?
- 이 클래스들은 어떤 역할을 하나요?
- 패턴의 요소들은 어떻게 서로 연관되어 있나요?
이 패턴의 구조를 배우면 실제 PHP 사용 사례를 기반으로 하는 다음 예시를 더욱 쉽게 이해할 수 있을 것입니다.
index.php: 개념적인 예시
<?php namespace RefactoringGuru\FactoryMethod\Conceptual; /** * The Creator class declares the factory method that is supposed to return an * object of a Product class. The Creator's subclasses usually provide the * implementation of this method. */ abstract class Creator { /** * Note that the Creator may also provide some default implementation of the * factory method. */ abstract public function factoryMethod(): Product; /** * Also note that, despite its name, the Creator's primary responsibility is * not creating products. Usually, it contains some core business logic that * relies on Product objects, returned by the factory method. Subclasses can * indirectly change that business logic by overriding the factory method * and returning a different type of product from it. */ public function someOperation(): string { // Call the factory method to create a Product object. $product = $this->factoryMethod(); // Now, use the product. $result = "Creator: The same creator's code has just worked with " . $product->operation(); return $result; } } /** * Concrete Creators override the factory method in order to change the * resulting product's type. */ class ConcreteCreator1 extends Creator { /** * Note that the signature of the method still uses the abstract product * type, even though the concrete product is actually returned from the * method. This way the Creator can stay independent of concrete product * classes. */ public function factoryMethod(): Product { return new ConcreteProduct1(); } } class ConcreteCreator2 extends Creator { public function factoryMethod(): Product { return new ConcreteProduct2(); } } /** * The Product interface declares the operations that all concrete products must * implement. */ interface Product { public function operation(): string; } /** * Concrete Products provide various implementations of the Product interface. */ class ConcreteProduct1 implements Product { public function operation(): string { return "{Result of the ConcreteProduct1}"; } } class ConcreteProduct2 implements Product { public function operation(): string { return "{Result of the ConcreteProduct2}"; } } /** * The client code works with an instance of a concrete creator, albeit through * its base interface. As long as the client keeps working with the creator via * the base interface, you can pass it any creator's subclass. */ function clientCode(Creator $creator) { // ... echo "Client: I'm not aware of the creator's class, but it still works.\n" . $creator->someOperation(); // ... } /** * The Application picks a creator's type depending on the configuration or * environment. */ echo "App: Launched with the ConcreteCreator1.\n"; clientCode(new ConcreteCreator1()); echo "\n\n"; echo "App: Launched with the ConcreteCreator2.\n"; clientCode(new ConcreteCreator2());
Output.txt: 실행 결과
App: Launched with the ConcreteCreator1. Client: I'm not aware of the creator's class, but it still works. Creator: The same creator's code has just worked with {Result of the ConcreteProduct1} App: Launched with the ConcreteCreator2. Client: I'm not aware of the creator's class, but it still works. Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}
실제 사례 예시
이 예시에서 팩토리 메서드 패턴은 소셜 네트워크 커넥터들을 만들기 위한 인터페이스를 제공합니다. 해당 패턴을 이용하여 네트워크에 로그인하고 게시물을 작성하고 잠재적으로 다른 활동들도 수행할 수 있는데 이 모든 것을 클라이언트 코드를 특정 소셜 네트워크의 특정 클래스에 결합하지 않으며 수행할 수 있습니다.
index.php: 실제 사례 예시
<?php namespace RefactoringGuru\FactoryMethod\RealWorld; /** * The Creator declares a factory method that can be used as a substitution for * the direct constructor calls of products, for instance: * * - Before: $p = new FacebookConnector(); * - After: $p = $this->getSocialNetwork; * * This allows changing the type of the product being created by * SocialNetworkPoster's subclasses. */ abstract class SocialNetworkPoster { /** * The actual factory method. Note that it returns the abstract connector. * This lets subclasses return any concrete connectors without breaking the * superclass' contract. */ abstract public function getSocialNetwork(): SocialNetworkConnector; /** * When the factory method is used inside the Creator's business logic, the * subclasses may alter the logic indirectly by returning different types of * the connector from the factory method. */ public function post($content): void { // Call the factory method to create a Product object... $network = $this->getSocialNetwork(); // ...then use it as you will. $network->logIn(); $network->createPost($content); $network->logout(); } } /** * This Concrete Creator supports Facebook. Remember that this class also * inherits the 'post' method from the parent class. Concrete Creators are the * classes that the Client actually uses. */ class FacebookPoster extends SocialNetworkPoster { private $login, $password; public function __construct(string $login, string $password) { $this->login = $login; $this->password = $password; } public function getSocialNetwork(): SocialNetworkConnector { return new FacebookConnector($this->login, $this->password); } } /** * This Concrete Creator supports LinkedIn. */ class LinkedInPoster extends SocialNetworkPoster { private $email, $password; public function __construct(string $email, string $password) { $this->email = $email; $this->password = $password; } public function getSocialNetwork(): SocialNetworkConnector { return new LinkedInConnector($this->email, $this->password); } } /** * The Product interface declares behaviors of various types of products. */ interface SocialNetworkConnector { public function logIn(): void; public function logOut(): void; public function createPost($content): void; } /** * This Concrete Product implements the Facebook API. */ class FacebookConnector implements SocialNetworkConnector { private $login, $password; public function __construct(string $login, string $password) { $this->login = $login; $this->password = $password; } public function logIn(): void { echo "Send HTTP API request to log in user $this->login with " . "password $this->password\n"; } public function logOut(): void { echo "Send HTTP API request to log out user $this->login\n"; } public function createPost($content): void { echo "Send HTTP API requests to create a post in Facebook timeline.\n"; } } /** * This Concrete Product implements the LinkedIn API. */ class LinkedInConnector implements SocialNetworkConnector { private $email, $password; public function __construct(string $email, string $password) { $this->email = $email; $this->password = $password; } public function logIn(): void { echo "Send HTTP API request to log in user $this->email with " . "password $this->password\n"; } public function logOut(): void { echo "Send HTTP API request to log out user $this->email\n"; } public function createPost($content): void { echo "Send HTTP API requests to create a post in LinkedIn timeline.\n"; } } /** * The client code can work with any subclass of SocialNetworkPoster since it * doesn't depend on concrete classes. */ function clientCode(SocialNetworkPoster $creator) { // ... $creator->post("Hello world!"); $creator->post("I had a large hamburger this morning!"); // ... } /** * During the initialization phase, the app can decide which social network it * wants to work with, create an object of the proper subclass, and pass it to * the client code. */ echo "Testing ConcreteCreator1:\n"; clientCode(new FacebookPoster("john_smith", "******")); echo "\n\n"; echo "Testing ConcreteCreator2:\n"; clientCode(new LinkedInPoster("john_smith@example.com", "******"));
Output.txt: 실행 결과
Testing ConcreteCreator1: Send HTTP API request to log in user john_smith with password ****** Send HTTP API requests to create a post in Facebook timeline. Send HTTP API request to log out user john_smith Send HTTP API request to log in user john_smith with password ****** Send HTTP API requests to create a post in Facebook timeline. Send HTTP API request to log out user john_smith Testing ConcreteCreator2: Send HTTP API request to log in user john_smith@example.com with password ****** Send HTTP API requests to create a post in LinkedIn timeline. Send HTTP API request to log out user john_smith@example.com Send HTTP API request to log in user john_smith@example.com with password ****** Send HTTP API requests to create a post in LinkedIn timeline. Send HTTP API request to log out user john_smith@example.com