우리는 생성자없이 살 수 있습니까?


25

어떤 이유로 든 모든 객체가 $ obj = CLASS :: getInstance () 방식으로 생성되었다고 가정 해 봅시다. 그런 다음 setter를 사용하여 종속성을 주입하고 $ obj-> initInstance ()를 사용하여 초기화 시작을 수행합니다. 생성자를 전혀 사용하지 않으면 해결할 수없는 실제 문제 나 상황이 있습니까?

추신 :이 방법으로 객체를 생성하는 이유는 일부 규칙에 따라 getInstance () 내부의 클래스를 바꿀 수 있기 때문입니다.

PHP에서 일하고 있습니다.


어떤 프로그래밍 언어?
gnat

2
PHP (왜 중요한가?)
Axel Foley

8
Factory 패턴을 사용하여 원하는 것을 얻을 수있는 것처럼 보입니다.
superM

1
참고로, 언급 된 팩토리 patern @superM은 제어 반전 (종속성 주입)을 구현하는 또 다른 방법입니다.
Joachim Sauer

그것은 자바 스크립트가 객체와 대략 무엇을하지 않습니까?
Thijser

답변:


44

나는 이것이 당신의 디자인 공간을 심각하게 방해한다고 말하고 싶습니다.

생성자는 전달 된 매개 변수를 초기화하고 유효성을 검증하기에 좋은 장소입니다. 더 이상이를 사용할 수없는 경우, 초기화, 상태 처리 (또는 "손상된"오브젝트의 생성자를 거부하는 것)가 훨씬 어렵고 부분적으로 불가능 해집니다.

예를 들어, 모든 Foo객체에 필요한 Frobnicator경우 생성자 Frobnicator가 null이 아닌 경우 생성자를 확인할 수 있습니다 . 생성자를 제거하면 확인하기가 더 어려워집니다. 그것이 사용될 모든 지점에서 점검합니까? 에서 init()방법 (효과적으로 생성자 메소드 외부화)? 절대 확인하지 않고 최선을 다 하시겠습니까?

여전히 모든 것을 구현할 수는 있지만 (결국 아직 완료되지 않은 경우도 있지만) 수행하기가 훨씬 더 어려울 수 있습니다.

개인적으로 Dependency Injection / Inversion of Control을 살펴볼 것을 제안 합니다 . 이러한 기술은 또한 구체적인 구현 클래스를 전환 할 수 있지만 생성자를 작성 / 사용하는 것을 막지는 않습니다.


"부러진"객체의 생성자를 명백하게 거부한다는 것은 무엇을 의미합니까?
Geek

2
@Geek : 생성자는 인수를 검사하여 해당 객체가 작동하는 객체인지를 결정할 수 있습니다 (예 : 객체에 필요한 HttpClient경우 해당 매개 변수가 널이 아닌지 확인). 이러한 제약 조건이 충족되지 않으면 예외가 발생할 수 있습니다. 구성 및 설정 값 접근 방식으로는 실제로 불가능합니다.
Joachim Sauer

1
OP는 내부에서 생성자의 외부화를 설명하고 있다고 생각합니다 init(). 이는 전체적으로 가능하지만 유지 보수 부담이 더 커집니다.
피터

26

생성자에게 2 가지 장점 :

생성자는 객체의 구성 단계를 원자 적으로 수행 할 수 있습니다.

생성자를 피하고 모든 것을 위해 setter를 사용할 수 있지만 Joachim Sauer가 제안한 필수 속성은 어떻습니까? 생성자를 사용하면 객체는 자신의 생성 논리를 소유하여 해당 클래스의 유효하지 않은 인스턴스가 없는지 확인합니다 .

인스턴스를 만들 때 Foo3 개의 속성을 설정해야하는 경우 생성자는 3 개 모두에 대한 참조를 가져 와서 유효성을 검사하고 유효하지 않은 경우 예외를 throw 할 수 있습니다.

캡슐화

세터에만 의존함으로써 객체를 올바르게 구축하는 것은 소비자의 부담입니다. 유효한 여러 속성 조합이있을 수 있습니다.

예를 들어, 모든 Foo인스턴스의 인스턴스 중 하나를 필요로 Bar재산 bar또는 인스턴스 BarFinder로 속성을 barFinder. 둘 중 하나를 사용할 수 있습니다. 유효한 모든 매개 변수 집합에 대한 생성자를 만들고 이러한 방식으로 규칙을 적용 할 수 있습니다.

객체의 논리와 의미는 객체 자체 내에 있습니다. 캡슐화가 좋습니다.


15

예, 생성자없이 살 수 있습니다.

물론, 많은 중복 보일러 판 코드가 생길 수 있습니다. 또한 응용 프로그램의 규모가 크면 해당 응용 프로그램에서 상용구 코드를 일관되게 사용하지 않을 때 문제의 원인을 찾는 데 많은 시간이 소요될 수 있습니다.

그러나 아닙니다. 자신의 생성자를 엄격하게 '필요'하지는 않습니다. 물론 클래스와 객체를 엄격하게 '필요'하지는 않습니다.

이제 목표가 객체 생성에 일종의 팩토리 패턴을 사용하는 것이면 객체를 초기화 할 때 생성자를 사용하는 것만으로는 상호 배타적이지 않습니다.


전혀. 클래스와 객체 없이도 시작할 수 있습니다.
JensG

5
모두 강력한 어셈블러를 환영합니다!
Davor Ždralo

9

생성자를 사용하면 유효하지 않은 객체가없는 것을 쉽게 만들 수 있다는 장점이 있습니다.

생성자는 객체의 모든 멤버 변수를 유효한 상태로 설정할 수있는 기회를 제공합니다. 그런 다음 뮤 테이터 메소드가 오브젝트를 유효하지 않은 상태로 변경할 수 없는지 확인하면 유효하지 않은 오브젝트가 없으므로 많은 버그를 피할 수 있습니다.

그러나 새 객체가 유효하지 않은 상태로 생성되고 사용할 수있는 유효한 상태로 전환하기 위해 일부 세터를 호출해야하는 경우, 클래스 소비자가 이러한 세터를 호출하거나 잘못 호출하는 것을 잊어 버릴 위험이 있습니다. 당신은 잘못된 개체로 끝납니다.

해결 방법은 팩토리 메소드를 통해서만 오브젝트를 작성하여 호출자에게 리턴하기 전에 오브젝트가 작성하는지 유효성을 점검합니다.


3

$ obj = CLASS :: getInstance (). 그런 다음 setter를 사용하여 종속성을 주입하고 $ obj-> initInstance ()를 사용하여 초기화 시작을 수행합니다.

나는 당신이 필요 이상으로 이것을 어렵게 만들고 있다고 생각합니다. 우리는 생성자를 통해 종속성을 잘 주입 할 수 있습니다-생성자가 많으면 사전과 같은 구조를 사용하여 사용하려는 것을 지정할 수 있습니다.

$obj = new CLASS(array(
    'Frobnicator' => (),
    'Foonicator' => (),
));

그리고 생성자 내에서 다음과 같이 일관성을 보장 할 수 있습니다.

if (!array_key_exists('Frobnicator', $args)) {
    throw new Exception('Frobnicator required');
}
if (!array_key_exists('Foonicator', $args)) {
    $args['Foonicator'] = new DefaultFoonicator();
}

$args 그런 다음 필요에 따라 개인 구성원을 설정하는 데 사용할 수 있습니다.

그렇게 생성자 내에서 완전히 수행되면 $obj질문에 설명 된 시스템에서와 같이 존재하지만 초기화되지 않은 중간 상태는 절대 존재하지 않습니다. 객체가 항상 올바르게 사용되는 것을 보장 할 수 없기 때문에 이러한 중간 상태를 피하는 것이 좋습니다.


2

나는 실제로 비슷한 것들에 대해 생각하고있었습니다.

내가 물었던 질문은 "어떤 생성자가 하는가, 다르게 할 수 있는가?"였습니다. 나는 그 결론에 도달했다.

  • 일부 속성이 초기화되도록합니다. 매개 변수로 승인하고 설정합니다. 그러나 이것은 컴파일러에 의해 쉽게 시행 될 수 있습니다. 필드 또는 속성에 "필수"로 주석을 달기 만하면 컴파일러는 인스턴스가 생성되는 동안 모든 것이 올바르게 설정되었는지 확인합니다. 인스턴스를 생성하는 호출은 아마도 동일 할 것입니다. 생성자 메서드는 없습니다.

  • 속성이 유효한지 확인하십시오. 이것은 assert 조건에 의해 쉽게 달성 될 수 있습니다. 다시 한 번 올바른 조건으로 속성에 주석을 달았습니다. 일부 언어는 이미이 작업을 수행합니다.

  • 좀 더 복잡한 구성 논리. 현대적인 패턴은 생성자에서 권장하지 않지만 특수 팩토리 메소드 또는 클래스를 사용하도록 제안합니다. 따라서이 경우 생성자의 사용은 최소화됩니다.

따라서 귀하의 질문에 대답하십시오 : 예, 가능합니다. 그러나 언어 디자인에 큰 변화가 필요합니다.

그리고 방금 내 대답이 꽤 OT라는 것을 알았습니다.


2

예, 생성자를 사용하지 않고도 거의 모든 작업을 수행 할 수 있지만 이는 객체 지향 프로그래밍 언어의 이점을 분명히 제공합니다.

현대 언어 (내가 프로그래밍 한 C #에 대해 이야기 할 것 입니다)에서는 생성자 에서만 실행할 수있는 코드 부분을 제한 할 수 있습니다 . 서투른 실수를 피할 수있는 덕분입니다. 그러한 것 중 하나는 읽기 전용 수정 자입니다.

public class A {
    readonly string rostring;

    public A(string arg) {
        rostring = arg;
    }

    public static A CreateInstance(string arg) {
        var result = new A();
        A.rostring = arg;  // < because of this the code won't compile!
        return result;
    }
}

으로 요아킴 사우어 권장 사용하는 이전 대신 Factory디자인 후두둑을 읽어 Dependency Injection. Mark Seemann의 .NET에서 Dependency Injection을 읽는 것이 좋습니다 .


1

요구 사항에 따라 유형으로 개체를 인스턴스화하십시오. 특정 유형을 리턴하기 위해 시스템의 전역 변수를 사용하여 오브젝트 자체가 될 수 있습니다.

그러나 코드에 "All"이 될 수있는 클래스 가 dynamic type의 개념입니다 . 저는 개인적으로이 접근 방식이 코드에서 불일치를 만들고 테스트 컴플렉스를 만들며 * 제안 된 작업의 흐름과 관련하여 "미래가 불확실 해집니다"고 생각합니다.

* 테스트에서 먼저 유형을 고려해야하고, 다음으로 달성하려는 결과를 고려해야한다는 사실을 언급하고 있습니다. 그런 다음 큰 중첩 테스트를 작성합니다.


1

다른 답변의 균형을 잡기 위해 다음과 같이 주장하십시오.

생성자는 객체의 모든 멤버 변수를 유효한 상태로 설정할 수있는 기회를 제공합니다. 유효하지 않은 객체를 가지지 않으므로 많은 버그로부터 벗어날 수 있습니다.

생성자를 사용하면 객체는 자신의 생성 논리를 소유하여 해당 클래스의 유효하지 않은 인스턴스가 없도록합니다.

이러한 진술은 때때로 다음과 같은 가정을 암시합니다.

클래스가 종료 될 때까지 객체를 유효한 상태로 만든 생성자가 있고 클래스의 메소드 중 어느 것도 해당 상태를 무효화하여 무효화하지 않으면 클래스 외부의 코드가 객체를 감지하는 것이 불가능합니다 해당 클래스의 유효하지 않은 상태.

그러나 이것은 사실이 아닙니다. 대부분의 언어에는 생성자 가 외부 코드로 전달하는 this(또는 self언어가 호출하는 것에 대한) 규칙이 없습니다 . 이러한 생성자는 위에서 언급 한 규칙을 완전히 준수하지만 반으로 구성된 객체를 외부 코드에 노출시킬 위험이 있습니다. 사소한 점이지만 쉽게 간과됩니다.


0

이것은 다소 일화 적이지만 일반적으로 객체를 완성하고 사용하기 위해 필수적인 상태의 생성자를 예약합니다. setter-injection을 제외하고 생성자가 실행되면 내 객체가 필요한 작업을 수행 할 수 있어야합니다.

연기 될 수있는 것은 생성자에서 제외합니다 (출력 값 준비 등). 이 접근법을 사용하면 생성자를 사용하지 않고 종속성 주입이 의미가 있다고 생각합니다.

또한 조기에 아무것도하지 않기 위해 멘탈 디자인 프로세스를 하드 와이어 링하는 이점이 있습니다. 최선을 다해 수행하는 것은 앞으로 작업을위한 기본 설정이기 때문에 사용하지 않을 수도있는 로직을 초기화하거나 수행하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.