팩토리 패턴에 대해 본격적으로 학습해보겠습니다. 앞에서는 팩토리 패턴을 학습하기 위해 객체 생성과 문제점, 의존성에 대해 알아보았습니다. 팩토리 패턴은 객체 생성을 위임할 수 있는 클래스를 정의합니다. 팩토리 패턴의 클래스는 객체 생성을 담당하고 객체의 생성을 느슨한 관계가 되도록 처리합니다.
느슨한 결합
객체지향 프로그램의 첫 단추로 클래스를 선언했다면 그 다음으로 할 일은 객체를 생성하는 것입니다. 하지만 객체 생성은 객체 간 결합 관계를 발생시키고, 이러한 결합 관계는 객체 간에 의존성을 부여합니다. 팩토리 패턴에서는 [예제1-3]과 달리 코드에서 직접 클래스의 이름을 지정해 객체를 생성하지 않으며, 별도의 객체에 필요한 객체를 생성하도록 책임을 위임합니다.
다음 예제는 객체 생성을 책임질 Factory 클래스를 선언합니다.
<예제>
<?php
// 팩토리 클래스
class Factory
{
static public function getInstance()
{
echo "팩토리:객체를 생성하여 반환합니다.";
return new Korean();
}
}
Factory 클래스는 생성할 객체를 반환하는 메서드를 가지고 있습니다. Factory 클래스의 getInstance ( ) 메서드로 객체를 생성하고 생성된 객체를 반환합니다.
동적 팩토리
팩토리 패턴은 분리된 factory 클래스의 객체를 통해 필요로 하는 모든 객체의 생성을 위임합니다. 팩토리 패턴은 앞에서 문제로 언급했던 강력한 결합 관계 발생을 줄이고, 코드에서 new를 직접 사용하지 않고도 객체를 생성합니다. 앞에서 설계한 Factory 클래스를 이용해 기존의Hello 클래스를 수정하여 Factory 클래스의 getInstance ( ) 메서드를 정적static 타입으로 호출합니다.
<예제>
<?php
// 클래스 파일 선언
class Hello
{
public function greeting()
{
// return "안녕하세요";
// 새로운 객체를 생성합니다.
// $ko = new Korean;
// return $ko->text();
$ko = Factory::getInstance(); // 팩토리 호출
return $ko->text();
}
}
new 키워드를 직접 사용하지 않고 Factory 클래스의 getInstance ( ) 메서드가 Korean 클래스의 객체를 생성하여 반환합니다. Factory 클래스는 Korean 객체의 생성 작업을 위임하는 역할을 수행합니다.
Factory 클래스를 활용한 객체 생성
NOTE Factory 클래스를 사용하기 위해서는 new 키워드를 이용하여 팩토리 객체를 생성해야 합니다. new 키워드를 사용하지 않기 위해 Factory 클래스의 getInstance() 메서드를 정적 타입으로 선언하고 호출했습니다.
다음은 작성한 코드를 실행하는 메인 소스입니다. 필요한 클래스 선언 파일을 include하여 같이 실행합니다.
<예제>
<?php
// 클래스 파일을 읽어옵니다.
include "factory.php";
include "hello.php";
include "korean.php";
// 객체를 생성합니다.
$obj = new hello;
// 행위 호출
echo $obj->greeting();
$ php index.php
팩토리:객체를 생성하여 반환합니다.
안녕하세요
팩토리 패턴으로 변경된 코드를 통해 코드 자체에서 생성되는 강력한 의존 관계를 분리하고 느슨한 의존 관계로 변경했습니다. 앞에서 지적했던 의존 관계 문제가 해결되었습니다. 하지만 팩토리 패턴을 사용하면 객체를 직접 생성하는 것과 달리 Factory 객체를 통해 호출call하는 처리 과정이 한 단계 더 필요합니다. 이는 불필요한 호출 증가로 프로그램 성능 저하를 초래합니다. 그러나 객체 생성을 다른 객체에 위임함으로써 내부적인 결합을 제거하고, 동적으로 객체를 관리할 수 있다는 장점도 있습니다.
클래스의 선택
팩토리 패턴은 객체 생성을 위임합니다. 다음 예제를 보면서 객체 생성 위임에 대해 더 학습해 봅시다. 새로운 인사말을 출력하기 위해 영어로 된 인사말 클래스(English )를 하나 더 만듭니다.
<예제>
<?php
// 클래스 파일 선언
class English
{
public function text()
{
return "hello world";
}
}
인사말 객체지향 프로그램은 Korean, English 클래스의 객체를 생성하고 관리합니다. Factory 클래스를 다음과 같이 변경합니다.
<예제>
<?php
// 팩토리 클래스
class Factory
{
static public function getInstance($type=null)
{
echo "팩토리:객체를 생성하여 반환합니다.";
if($type = = "ko") {
return new Korean();
} else if($type = = "en") {
return new English();
}
}
}
팩토리 패턴이 여러 객체의 생성을 책입집니다. 이때는 생성할 객체를 선택할 수 있는 조건 로직이 필요한데, 이 경우 외부의 매개변수값을 받아 처리할 수 있습니다. Factory 클래스의 getInstance ( ) 메서드는 조건을 통해 Korean과 English 객체를 선택적으로 생성/반환할 수 있습니다. 조건이 추가/변경될 때마다 다른 객체를 생성하고 반환할 수 있습니다.
다음 예제의 Hello 클래스도 변경된 Factory 클래스에 맞게 수정합니다.
<예제>
<?php
// 클래스 파일 선언
class Hello
{
public function greeting($type)
{
// return "안녕하세요";
// 새로운 객체를 생성합니다.
// $ko = new Korean;
// return $ko->text();
$ko = Factory::getInstance($type); // 팩토리 호출
return $ko->text();
}
}
Factory 클래스는 2개의 클래스를 선택적으로 객체 생성하므로 Factory::getInstance ( ) 메서드에 생성할 객체명에 대한 조건값을 같이 전달합니다.
부장(팩토리)이 어떤 사원(객체)에게 지시를 내릴지 결정하는 것과 유사함
예를 들면 회사에서 부장이 사원1, 사원2, 사원3 등을 직접 지정해 선택적으로 담당할 일을 시키는 것과 같습니다. 이때 사원은 매개변수로 전달받는 방법과 유사하게 선택될 수 있습니다. 메인 예제 코드도 2개의 인사말을 모두 출력할 수 있도록 수정해보겠습니다.
<예제>
<?php
// 클래스 파일을 읽어옵니다.
include "factory.php";
include "hello.php";
include "korean.php";
include "English.php";
// 객체를 생성합니다.
$obj = new hello;
// 행위 호출
echo $obj->greeting("en")."";
echo $obj->greeting("ko")."";
$ php index.php
팩토리:객체를 생성하여 반환합니다.
hello world
팩토리:객체를 생성하여 반환합니다.
안녕하세요
2가지 언어의 인사말을 출력하는 팩토리 패턴을 구현합니다. 더 많은 언어 인사말을 출력할 때는 간단하게 인사말 클래스를 선언하고 조건만 추가합니다. 팩토리 패턴은 객체의 생성 처리를 동적으로 위임하므로, 이처럼 향후 클래스가 추가되거나 변경돼도 코드를 쉽게 수정할 수 있습니다.
형식 안정성
팩토리 클래스는 프로그램 내부에서 필요한 객체를 생성하는 역할을 수행합니다. 또한 다수의 객체를 생성할 때는 이를 선택하기 위해 매개변수를 전달합니다. 이때 전달되는 매개변수의 타입은 대부분 문자열을 사용합니다. 팩토리 클래스는 전달받은 매개변수를 비교해 일치하는 객
체를 생성하고 반환합니다. 하지만 전달받은 매개변수를 판별하지 못해 정확히 생성해야 할 객체를 결정하지 못하는 경우도 있습니다(조건 불일치).
예를 들어 한국어 인사말은 ko 코드를 사용합니다. 실수로 ko 코드가 아닌 kr 코드를 입력하면 팩토리 패턴은 정확한 객체를 생성하지 못하기 때문에 프로그램이 실행되지 않습니다. 이처럼 매개변수가 코드의 오류를 발생시키는 원인인 경우도 많습니다. 프로그램이 안전하게 동작하
도록 하기 위해 상수const를 사용하는 것도 좋은 방안입니다. 다음은 상수를 이용해 변경한 예제코드입니다.
<예제>
<?php
// 팩토리 클래스
class Factory
{
const KOREAN = "ko"; // 상수 사용
const ENGLISH = "en"; // 상수 사용
static public function getInstance($type=null)
{
echo "팩토리:객체를 생성하여 반환합니다.";
if($type = = self::KOREAN) {
return new Korean();
} else if($type = = self::ENGLISH) {
return new English();
}
}
}
메인 코드도 팩토리에서 선언된 상수를 이용해 인자값을 전달합니다. 이처럼 상수를 이용하면 많은 실수를 줄일 수 있습니다.
<예제>
<?php
// 클래스 파일을 읽어옵니다.
include "factory2.php";
include "hello.php";
include "korean.php";
include "English.php";
// 객체를 생성합니다.
$obj = new hello;
// 행위 호출
echo $obj->greeting(Factory::ENGLISH)."";
echo $obj->greeting(Factory::KOREAN)."";
$ php index2.php
팩토리:객체를 생성하여 반환합니다.
hello world
팩토리:객체를 생성하여 반환합니다.
안녕하세요
24가지 패턴으로 알아보는 객체지향의 원리
쉽게 배워 바로 써먹는 디자인 패턴
최신 콘텐츠