어떤 클래스의 인스턴스를 생성하는 데 사용하는 데이터와 코드가 여러 클래스에 퍼져 있다면, 그 생성 지식을 하나의 팩터리 클래스로 옮긴다.

동기

객체 생성을 위한 지식이 여러 클래스에 퍼져 있다면, 이를 '문어발 생성'이라고 이야기 한다. 하나의 클래스는 자신의 기능이 명확해야 하므로 문어발 생성이 존재한다는 것은 객체 생성과 상관없는 클래스가 객체 생성에 대한 책임을 지고 있다는 이야기가 된다. 특히 외부 설정값을 클래스별로 이리저리 전달하다가 생성 코드까지 전달할 경우 자주 일어나는데, 이런 경우에 Factory 패턴을 통해 객체 생성 로직과 외부 설정값 처리에 대한 로직을 캡슐화할수 있다. 이 방법으로 생성 로직이 변경되더라도 이리저리 쫓아다니며 코드를 고칠 필요없이 Factory 클래스만 수정하면 되기 때문에 편리하다.

Factory 패턴을 이용하면 하나의 Factory 인스턴스에 지속적으로 상태를 조정할 수 있으며, 하나의 인스턴스가 프로그램 내에서 계속 재사용될 수 있다. 경우에 따라서는 abstract class로 구현하여, 생성 옵션에 따라서 별개의 생성 클래스를 만들어 생성 로직의 복잡함을 줄일 수 있다.

절차

  1. 주어진 클래스의 인스턴스를 생성하기 위해 다른 클래스에서 knowledge를 사용하는 부분을 찾는다. creation method를 사용하지 않는다면, 사용하도록 수정한다.
  2. 팩터리로 사용할 클래스를 만든다. 이 때 팩터리 클래스의 이름은 생성할 클래스의 이름을 고려하여 정한다.
    ex) Pizza class의 인스턴스를 만드는 팩터리 -> PizzaFactory
  3. Move Method 리팩터링을 이용해, 생성 메서드를 Factory 클래스로 옮긴다.
  4. Factory의 인스턴스를 생성한 후, 생성할 클래스에서 Factory를 통해 인스턴스를 생성하도록 수정한다.
  5. 주변 코드를 보면서, 무엇이든 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

어떤 클래스의 인스턴스를 생성할 때 그것이 제공하는 여러 생성자 중 어떤 것을 호출해야 할지 결정하기가 어렵다면, 인스턴스를 생성해 리턴하는 생성 메서드로 각 생성자를 대체하여 그 용도가 명확히 드러나도록 한다.

동기

C++이나 Java 계열의 언어에서는 생성자의 이름이 항상 클래스의 이름으로 고정이다. Python의n의 경우에는 __init__이 생성자의 역할을 대신한다. 특히 C++이나 Java 계열의 언어에서 프로그래머는 생성자가 여러종류일 경우 생성자 코드를 살펴본 후 어떤 생성자를 호출할 지를 정해야한다. 이는 코딩을 할 때 생산성의 저하로 이어지게 된다.

생성자의 시그니처만 보고 용도를 명확히 알기는 어렵다. 생성자가 많을수록 프로그래밍 시 잘못된 생성자를 호출하는 경우도 있다. 극단적인 경우 새로 만들 생성자의 시그니처가 기존 생성자와 같은 경우도 있을 수 있다. 불가능한 선언이 되는것이다.

Creation Method를 이용하여 Constructor를 대체한다면 이러한 문제를 해결할 수 있으며, Creation Method의 이름을 통해 그 역할 또한 명시적으로 전달할 수 있다. Python의 경우에는 class method를 이용하여 구현하면 좋을 것 같다.

절차

  1. 리팩터링할 생성자를 하나 선택하여 이를 호출하는 코드를 확인한다. 이 부분에서 해당 호출부를 Extract Method 리팩터링을 진행한 후 이 메서드를 선언한 클래스로 Move Method 리팩터링을 진행한다.
  2. 선택한 생성자를 사용하는 모든 곳에서 생성자 대신 새로 만든 메서드를 호출하도록 변경한다.
  3. 만약 선택한 생성자가 다른 생성자를 호출하고 있다면, 해당 생성자 대신 호출되는 생성자를 사용하도록 변경한다.
  4. 위의 과정을 반복하여 필요한 모든 생성자를 리팩터링한다.
  5. 마지막으로 클래스의 생성자가 밖에서 사용되지 않는다면 이를 private으로 변경한다.


추가

추상 메서드가 많을 경우 Factory 클래스를 생성하여 생성하는 부분을 위임해도 괜찮다. 취향의 문제이다.

'패턴 공부' 카테고리의 다른 글

Move Creation Knowledge to Factory  (0) 2021.04.07
리팩터링이란?  (0) 2021.04.06
소프트웨어 패턴과 TDD  (0) 2021.04.06
서론  (0) 2021.04.06

리팩터링이란?

리팩토링은 겉으로 보이는 동작을 바꾸지 않고, 이해하거나 수정하기 쉽게 소프트웨어의 구조를 변경하는 행위이다.
리팩터링을 할땐 반복을 없애거나, 복잡한 로직을 단순화하고, 큰 함수를 나누고, 변수명을 수정하는 등 다양한 행위를 포함한다.
안전한 리팩터링을 위해서는 테스트가 필수이다. 만약 내 코드가 겉으로 보이는 동작이 달라졌다면 이는 테스트를 통해 확인할 수 있다.
따라서 테스트를 하지 않는다면 리팩터링을 하는데에 매우 소극적으로 변하게 되고 큰 구조의 변경이나 로직을 단순화하는 등 실제 결과를 다르게 할 수도 있는 거대한 리팩터링을 하는데에 무리가 있다.

리팩터링은 다음과 같은 이유들로 진행한다고 저자는 설명한다.

  • 새로운 코드를 더 쉽게 추가할 수 있도록 하기 위해
    코드를 새로 추가할 땐 두가지 방법중 하나를 택하게 된다. 기존 코드를 생각하지 않고, 무작정 프로그램을 작성하는 방법과, 새로운 기능이 예쁘게 들어맞도록 기존의 설계를 약간 수정하는 것이다. 전자의 방법은 '설계 부채'를 만들고, 이는 나중에 언젠간 리팩터링으로 갚아야 한다.
  • 기존 코드를 잘 이해하고 발전적인 설계로 개선하기 위해
    가끔 스스로 짠 코드도 이해하기 힘든 경우가 있다. 이런 코드에 무작정 주석을 다는 것보단, 코드가 명확하지 않은 부분이 있다면 리팩터링으로 해결하는 것이 옳을 것이다. '냄새'를 덮는게 아니라, 냄새의 근원을 찾아 없애는 것이다. 이 과정을 통해 코드의 유지보수와 확장이 매우 쉬워지게 된다.
  • 덜 짜증나는 코드를 만들기 위해
    일단 리팩터링을 하지 않은 코드들을 보면 짜증이 나기때문에 이를 덜 짜증나게 바꾸는 것으로 프로그래밍을 좀 더 즐겁게 만들수 있다.


사람이 읽기 쉬운 코드

다음과 같은 말이 있다.

컴퓨터가 이해하는 코드는 어느 바보나 다 짤 수 있다. 훌륭한 프로그래머는 사람이 이해할 수 있는 코드를 짠다.


사람이 이해하는 코드를 짜는것이 코드 리뷰를 진행하거나, 서로의 코드를 이해할 때 압도적으로 도움이 된다. 그런 점에서 코드를 깔끔하게 유지하는 것이 매우 중요하다. 이는 마치 방 청소와 같은데, 일이 쌓이게 되면 하기가 싫어진다. 그때 그때 작은 일을 해결해야 한다. 한번 귀찮다고 안하기 시작하면 난장판이 되는것은 금방이다.

작은 단계

항상 리팩터링을 할때 작은 단계로 점차 나아가는 것이 중요하다. 지나치게 큰 단계를 취하고 테스트를 통과시키기 위해 많은 고생을 하는 경우가 많다. 테스트는 나침반이다. 올바른 길에서 벗어나지 않도록 도와준다. 조금씩 조금씩 한걸음 한걸음 떼는 습관을 들이자.

설계 부채

인간은 완벽하지 않다. 언제나 완벽한 설계를 한다는 것은 사실상 불가능하고, 우리는 그렇기 때문에 '설계 부채'라는 것을 지게 된다. 제대로 되지 못한 설계를 리팩터링을 통해 갚지 않는다면, 이는 눈덩이처럼 불어나게 되고, 손쓸수 없는 지경에 까지 이르게 된다.

'패턴 공부' 카테고리의 다른 글

Move Creation Knowledge to Factory  (0) 2021.04.07
Replace Constructors with Creation Methods  (0) 2021.04.07
소프트웨어 패턴과 TDD  (0) 2021.04.06
서론  (0) 2021.04.06

+ Recent posts