어떤 클래스의 인스턴스를 생성하는 데 사용하는 데이터와 코드가 여러 클래스에 퍼져 있다면, 그 생성 지식을 하나의 팩터리 클래스로 옮긴다.
동기
객체 생성을 위한 지식이 여러 클래스에 퍼져 있다면, 이를 '문어발 생성'이라고 이야기 한다. 하나의 클래스는 자신의 기능이 명확해야 하므로 문어발 생성이 존재한다는 것은 객체 생성과 상관없는 클래스가 객체 생성에 대한 책임을 지고 있다는 이야기가 된다. 특히 외부 설정값을 클래스별로 이리저리 전달하다가 생성 코드까지 전달할 경우 자주 일어나는데, 이런 경우에 Factory 패턴을 통해 객체 생성 로직과 외부 설정값 처리에 대한 로직을 캡슐화할수 있다. 이 방법으로 생성 로직이 변경되더라도 이리저리 쫓아다니며 코드를 고칠 필요없이 Factory 클래스만 수정하면 되기 때문에 편리하다.
Factory 패턴을 이용하면 하나의 Factory 인스턴스에 지속적으로 상태를 조정할 수 있으며, 하나의 인스턴스가 프로그램 내에서 계속 재사용될 수 있다. 경우에 따라서는 abstract class로 구현하여, 생성 옵션에 따라서 별개의 생성 클래스를 만들어 생성 로직의 복잡함을 줄일 수 있다.
절차
- 주어진 클래스의 인스턴스를 생성하기 위해 다른 클래스에서 knowledge를 사용하는 부분을 찾는다. creation method를 사용하지 않는다면, 사용하도록 수정한다.
- 팩터리로 사용할 클래스를 만든다. 이 때 팩터리 클래스의 이름은 생성할 클래스의 이름을 고려하여 정한다.
ex) Pizza class의 인스턴스를 만드는 팩터리 -> PizzaFactory - Move Method 리팩터링을 이용해, 생성 메서드를 Factory 클래스로 옮긴다.
- Factory의 인스턴스를 생성한 후, 생성할 클래스에서 Factory를 통해 인스턴스를 생성하도록 수정한다.
- 주변 코드를 보면서, 무엇이든 Factory에 있는것이 나아보이는 것을 Factory로 전부 옮긴다. 아이디어의 기본은 Factory가 생성에 관한 가능한한 가장 많은 일을 하게 하는 것이다.
추가
다음과 같은 class를 보자.
public class Query...
public void createQuery() throws QueryException...
if (usingSDVersion52()){
query = new QuerySD52();
else
query = new QuerySD51();
여기서 조건문 부분을 팩터리로 변경한다면 다음과 같이 변경할 것이다.
public class Query...
public void createQuery() throws QueryException...
query = queryFactory.createQuery();
QueryFactory가 코드를 깔끔하게 만든것으로 보인다. 하지만 이 경우에는 그냥 내부 구현을 다른 곳으로 옮긴 것이 불가하다. Factory의 도입으로 코드의 설계가 개선되거나 이곳저곳에서 사용되는 생성 로직을 한곳으로 모으거나, 직접 객체를 생성할 때는 불가능한 방법으로 Factory의 상태를 조정하는것이 아니라면 Factory를 사용하는 것은 괜히 설계만 복잡하게 만들게 될 뿐이다. 이런 경우에는 Factory를 사용하지 않은 편이 나으며, 원래 코드도 충분히 좋은 코드라고 할 수 있다.
만약 다른 곳에서도 비슷한 일이 발생하며 여러가지 문어발식 생성이 발생하게 된다면, 그 때 리팩터링해도 늦지 않다. 항상 리팩터링은 민첩하고 꾸준하지만 게으르게 해라.
'패턴 공부' 카테고리의 다른 글
Replace Constructors with Creation Methods (0) | 2021.04.07 |
---|---|
리팩터링이란? (0) | 2021.04.06 |
소프트웨어 패턴과 TDD (0) | 2021.04.06 |
서론 (0) | 2021.04.06 |