외부에 service를 제공하는 모듈 (예를 들면, library)일 경우, 잘못된 사용을 원천적으로 막는 것은 상당히 어려우면서도 중요하다.(모듈의 사용성과도 밀접한 관계가 있다.)

그런데, module의 instance가 생성되고 나서, 초기화 작업 이후에는 다시 setting될 필요가 없는 값들의 경우, 되도록이면, final (혹은 const)로 선언하는 것이, 코드의 이해도를 높이기도 좋고, 잘못된 사용을 막기도 좋다.


이때, default argument 값을 사용할 수 있는 언어의 경우(C++, Python 등)는 별 문제 없는데, 그렇지 않는 경우(ex, Java)는 사용성을 위해서 module constructor를 overloading해야 한다.(대부분의 경우, default 변수가 사용될 경우, 굳이 매번 이 값들을 argument로 전달해야 하는 것은 상당히 번거롭다.)


예를 들면,  'name', 'numArms'와 'numLegs' 세개의 final 변수를 가지는 'Person' module의 경우


class Person {
    private final String mName;
    private final int mNumArms;
    private final int mNumLegs;

    public Person(String name, int numArms, int numLegs) {
        mName = name;
        mNumArms = numArms;
        mNumLegs = numLegs;
    }
    public Person(String name, int numArms) {
        this(name, numArms, 2);
    }
    public Person(String name) {
        this(name, 2, 2); // 2를 default value로 사용. 대부분의 사람은 팔 다리가 2개... 
    }
}

와 같이 작성된다.

그나마 argument가 3개인 경우가 저정도고, argument가 많아지면, 사용성을 높이기 위해서 overloading되는 생성자도 많아진다.

이 문제를 builder pattern으로 해결할 수 있다.

예를 들면.

class PersonBuilder <T extends PersonBuilder > { // Generic type T 를 사용하는 이유는, 이 Builder를 상속받는 builder를 위함이다.
    private final String mName;  // name은 default가 없으므로 생성시 외부에서 반드시 argument로 받아야 한다.
    private int mNumArms = 2;  // 대부분의 사람은 팔, 다리가 2개...
    private int mNumLegs = 2;

    public PersonBuilder(String name) {
        mName = name;
    }
    public T
    setNumArms(int numArms) {
        mNumArms = numArms;
        return (T)this;
    }
    public T
    setNumLegs(int numLegs) {
        mNumLegs = numLegs;
        return (T)this;
    }

    public Person
    create() {
        return new Person(mName, mNumArms, mNumLegs);
    }
}


그렇지만, 역시 해당 class를 상속받는 경우, 긴 argument를 가진 생성자를 상속해야 하는 불편함은 여전하지만, 이 문제 역시, builder를 상속받는 방법으로 해결할 수 있다.


class OldPerson extends Person { ...}

class OldPersonBuilder<T extends OldPersonBuilder> extends PersonBuilder<T> {
    public OldPersonBuilder
    create() { ...}
}


Generic을 사용했기 때문에, 아래와 같은 용법이 가능하다.

OldPersonBuilder<OldPersonBuilder> bldr = new OldPersonBuilder<OldPersonBuilder>("MyName");
bldr.setNumArms(2).setNumLegs(2);

대략 정리해 보면, 이와 같은 pattern을 사용할때는


Module에서 final. Default값 존재 X => Builder에서도 final

Module에서 final. Default값 존재 O => Builder에서 NOT final.


이정도일려나?


+ Recent posts