[java] 긴 매개 변수 목록이있는 생성자를 사용하지 않고 크고 불변의 객체 만들기

불변 할 수있는 큰 (3 개 이상의 필드) 개체가 있습니다. 이 경우가 발생할 때마다 긴 매개 변수 목록으로 생성자 혐오감을 만드는 경향이 있습니다.

옳지 않고 사용하기 어렵고 가독성이 떨어집니다.

필드가 목록과 같은 일종의 수집 유형이면 더 나쁩니다. 단순 addSibling(S s)하면 객체 생성이 너무 쉬워 지지만 객체를 변경 가능하게 렌더링합니다.

그런 경우에 너희들은 무엇을 사용합니까?

나는 Scala와 Java를 사용하고 있지만 언어가 객체 지향적이라면 문제는 언어 불가지론 적이라고 생각합니다.

내가 생각할 수있는 솔루션 :

  1. “긴 매개 변수 목록이있는 생성자 혐오”
  2. 빌더 패턴



답변

글쎄, 일단 생성되면 읽기 쉽고 불변 객체를 모두 원하십니까?

유창한 인터페이스가 올바르게 완료 되면 도움이 될 것이라고 생각 합니다.

다음과 같이 보일 것입니다 (순수하게 구성된 예제).

final Foo immutable = FooFactory.create()
    .whereRangeConstraintsAre(100,300)
    .withColor(Color.BLUE)
    .withArea(234)
    .withInterspacing(12)
    .build();

대부분의 Java 프로그래머가 유창한 인터페이스를 잘못 이해하고 객체를 빌드하는 데 필요한 방법으로 객체를 오염시키기 때문에 “CORRECTLY DONE” 을 굵게 썼습니다 . 이는 물론 완전히 잘못된 것입니다.

비결은 build () 메서드 만이 실제로 Foo를 생성한다는 것입니다 (따라서 Foo는 변경 불가능할 수 있음).

FooFactory.create () , 여기서 XXX (..)withXXX (..) 모두 “다른 것”을 만듭니다.

다른 것이 FooFactory 일 수 있습니다. 여기에 한 가지 방법이 있습니다 ….

FooFactory는 다음과 같습니다.

// Notice the private FooFactory constructor
private FooFactory() {
}

public static FooFactory create() {
    return new FooFactory();
}

public FooFactory withColor( final Color col ) {
    this.color = color;
    return this;
}

public Foo build() {
    return new FooImpl( color, and, all, the, other, parameters, go, here );
}


답변

Scala 2.8에서는 copy케이스 클래스 의 메서드 뿐만 아니라 명명 된 매개 변수와 기본 매개 변수를 사용할 수 있습니다 . 다음은 몇 가지 예제 코드입니다.

case class Person(name: String, age: Int, children: List[Person] = List()) {
  def addChild(p: Person) = copy(children = p :: this.children)
}

val parent = Person(name = "Bob", age = 55)
  .addChild(Person("Lisa", 23))
  .addChild(Person("Peter", 16))


답변

음, Scala 2.8에서 이것을 고려하십시오.

case class Person(name: String,
                  married: Boolean = false,
                  espouse: Option[String] = None,
                  children: Set[String] = Set.empty) {
  def marriedTo(whom: String) = this.copy(married = true, espouse = Some(whom))
  def addChild(whom: String) = this.copy(children = children + whom)
}

scala> Person("Joseph").marriedTo("Mary").addChild("Jesus")
res1: Person = Person(Joseph,true,Some(Mary),Set(Jesus))

물론 여기에는 문제가 있습니다. 예를 들어, 만드는 시도 espouse하고 Option[Person], 그리고 서로 결혼 한 두 사람을 받고. 나는 그것을 해결할 방법을 생각할 수 없다. private var그리고 / 또는 private생성자와 공장에 의지하지 않고서는 그것을 해결할 수 없다 .


답변

다음은 몇 가지 추가 옵션입니다.

옵션 1

구현 자체를 변경 가능하게 만드 되 변경 가능 및 불변에 노출되는 인터페이스를 분리하십시오. 이것은 Swing 라이브러리 디자인에서 가져온 것입니다.

public interface Foo {
  X getX();
  Y getY();
}

public interface MutableFoo extends Foo {
  void setX(X x);
  void setY(Y y);
}

public class FooImpl implements MutableFoo {...}

public SomeClassThatUsesFoo {
  public Foo makeFoo(...) {
    MutableFoo ret = new MutableFoo...
    ret.setX(...);
    ret.setY(...);
    return ret; // As Foo, not MutableFoo
  }
}

옵션 2

응용 프로그램에 크고 미리 정의 된 불변 개체 (예 : 구성 개체) 집합이 포함 된 경우 Spring 프레임 워크 사용을 고려할 수 있습니다 .


답변

다양한 종류 의 불변성 이 있음을 기억하면 도움이됩니다 . 귀하의 경우에는 “팝 시클”불변성이 정말 잘 작동 할 것이라고 생각합니다.

팝 시클 불변성 : 내가 기발하게 한번 쓰기 불변성의 약간의 약화라고 부르는 것입니다. 초기화하는 동안 잠시 동안 변경 가능하다가 영원히 “고정”된 객체 나 필드를 상상할 수 있습니다. 이러한 종류의 불변성은 순환 적으로 서로를 참조하는 불변 객체 또는 디스크에 직렬화되고 역 직렬화시 전체 역 직렬화 프로세스가 완료 될 때까지 “유동적”이어야하는 불변 객체에 특히 유용합니다. 겨울 왕국.

따라서 개체를 초기화 한 다음 더 이상 쓸 수 없음을 나타내는 일종의 “고정”플래그를 설정합니다. 가급적이면 함수 뒤에 변이를 숨기면 함수가 API를 사용하는 클라이언트에게 여전히 순수합니다.


답변

변경 불가능한 객체가 변경자처럼 보이는 메서드 (예 : addSibling)를 노출하도록 만들 수도 있지만 새 인스턴스를 반환하도록 할 수도 있습니다. 그것이 불변의 Scala 컬렉션이하는 일입니다.

단점은 필요한 것보다 더 많은 인스턴스를 만들 수 있다는 것입니다. 부분적으로 빌드 된 객체를 처리하지 않으려는 경우가 아니면 대부분의 경우 괜찮은 형제가없는 일부 노드와 같은 중간 유효한 구성이있는 경우에만 적용 할 수 있습니다.

예를 들어 아직 목적지가없는 그래프 에지는 유효한 그래프 에지가 아닙니다.


답변

네 가지 가능성을 고려하십시오.

new Immutable(one, fish, two, fish, red, fish, blue, fish); /*1 */

params = new ImmutableParameters(); /*2 */
params.setType("fowl");
new Immutable(params);

factory = new ImmutableFactory(); /*3 */
factory.setType("fish");
factory.getInstance();

Immutable boringImmutable = new Immutable(); /* 4 */
Immutable lessBoring = boringImmutable.setType("vegetable");

나에게 2, 3, 4는 각각 다른 상황에 적응한다. 첫 번째는 OP에서 인용 한 이유로 사랑하기 어렵고 일반적으로 약간의 크립을 겪고 약간의 리팩토링이 필요한 디자인의 증상입니다.

내가 나열한 것은 (2) ‘공장’뒤에 상태가 없을 때 좋은 것이고, (3)은 상태가있을 때 선택한 디자인이다. 스레드 및 동기화에 대해 걱정하고 싶지 않을 때 (3)이 아닌 (2)를 사용하고 있으며 많은 개체를 생성하는 동안 값 비싼 설정을 분할하는 것에 대해 걱정할 필요가 없습니다. (3) 반면에 실제 작업이 공장 건설 (SPI에서 설정, 구성 파일 읽기 등)에 들어갈 때 호출됩니다.

마지막으로 다른 사람의 대답은 옵션 (4)에 대해 언급했습니다. 여기서 변경 불가능한 개체가 많고 선호되는 패턴은 오래된 개체에서 뉴스를 가져 오는 것입니다.

나는 ‘패턴 팬 클럽’의 회원이 아니라는 점에 유의하세요. 물론 어떤 것들은 따라 할만한 가치가 있지만, 사람들이 이름과 재미있는 모자를 주면 도움이되지 않는 삶을 사는 것 같습니다.