it-swarm-ko.tech

생성자가 예외를 발생시키는 것이 적절한시기는 언제입니까?

생성자가 예외를 발생시키는 것이 적절한시기는 언제입니까? (또는 목표 C의 경우 : 이니셜이 언제 nil을 반환하는 것이 옳은가?)

객체가 완료되지 않으면 생성자가 실패해야하므로 객체 생성을 거부해야합니다. 즉, 생성자는 호출자와 계약을 맺어야 기능적으로 작동하는 객체를 제공해야합니다. 합리적입니까?

197
Mark R Lindsey

생성자의 임무는 객체를 사용 가능한 상태로 만드는 것입니다. 이것에 대해 기본적으로 두 개의 사고 학교가 있습니다.

한 그룹은 2 단계 건설을 선호합니다. 생성자는 단지 객체를 잠자기 상태로 만들어서 작업을 거부합니다. 실제 초기화를 수행하는 추가 기능이 있습니다.

나는이 접근법의 이유를 결코 이해하지 못했습니다. 나는 1 단계 구성을 지원하는 그룹에 단단히 속해 있으며, 건설 후에 객체가 완전히 초기화되고 사용할 수 있습니다.

1 단계 생성자는 객체를 완전히 초기화하지 못하면 던져야합니다. 객체를 초기화 할 수없는 경우, 존재하지 않아야하므로 생성자가 던져야합니다.

261
Sebastian Redl

Eric Lippert 말한다 4 가지 예외가 있습니다.

  • 치명적인 예외는 귀하의 잘못이 아니며,이를 예방할 수 없으며, 현명하게 정리할 수 없습니다.
  • 뼈 달린 예외는 당신 자신의 실수이며, 당신은 그들을 막을 수 있었으므로 코드의 버그입니다.
  • Vexing 예외는 불행한 설계 결정의 결과입니다. Vexing 예외는 완전히 예외가 아닌 상황에서 발생하므로 항상 잡아서 처리해야합니다.
  • 마지막으로 외인성 예외는 불행한 디자인 선택의 결과가 아니라는 점을 제외하고는 다소 예외적 인 것처럼 보입니다. 오히려, 그들은 당신의 아름답고 선명한 프로그램 논리에 영향을 미치는 어수선한 외부 현실의 결과입니다.

생성자는 절대 치명적인 예외를 발생시키지 않아야하지만 실행 코드로 인해 치명적인 예외가 발생할 수 있습니다. "메모리 부족"과 같은 것은 제어 할 수 없지만 생성자에서 발생하면 발생합니다.

본 코드 예외는 코드에서 절대 발생하지 않아야합니다.

Vexing 예외 (예 : Int32.Parse())는 예외가 아닌 상황이 아니기 때문에 생성자에 의해 발생해서는 안됩니다.

마지막으로 외생 적 인 예외는 피해야하지만 네트워크 또는 파일 시스템과 같은 외부 환경에 의존하는 생성자에서 무언가를 수행하는 경우 예외를 throw하는 것이 좋습니다.

참조 링크 : https://blogs.msdn.Microsoft.com/ericlippert/2008/09/10/vexing-exceptions/

56
Jacob Krall

객체 초기화를 생성에서 분리하여 얻을 수있는 것은 일반적으로 없습니다. RAII는 정확합니다. 생성자에 대한 성공적인 호출은 완전히 초기화 된 라이브 객체를 생성하거나 실패해야합니다. ALL 모든 코드의 어느 시점에서나 실패 경로는 항상 예외를 던져야합니다. 특정 수준에서 추가 복잡성을 제외하고 별도의 init () 메소드를 사용하면 아무것도 얻지 못합니다. ctor 계약은 기능적으로 유효한 객체를 반환하거나 자체적으로 정리되어 throw되어야합니다.

별도의 init 메소드를 구현하는 경우 still 메소드를 호출해야합니다. 여전히 예외를 던질 가능성이 있으며, 여전히 처리해야하며, 어쨌든 생성자 직후에 거의 항상 호출해야합니다. 단, 2 대신 4 개의 가능한 객체 상태 (IE, 구성, 초기화, 초기화되지 않은, 유효하고 존재하지 않는 대 실패).

어쨌든 25 년 동안 OO 별도의 init 메소드가 '일부 문제를 해결할 것'인 것처럼 보이는 개발 사례는 디자인 결함입니다. 이제는 지금 구성하지 말아야하며 지금 필요하면 초기화해야합니다 KISS 항상 행동 원리라는 간단한 개념과 함께 원칙을 따라야합니다. 상태 및 모든 인터페이스의 API는 객체가 수행하는 방식을 반영해야하며 클라이언트 코드는 객체에 초기화가 필요한 내부 상태가 있음을 인식하지 않아야합니다. 따라서 패턴 이후의 초기화는이 원칙을 위반합니다.

30
Alhazred

부분적으로 생성 된 클래스가 발생할 수있는 모든 문제로 인해 결코 말하지 않을 것입니다.

생성하는 동안 무언가를 확인해야하는 경우 생성자를 비공개로 만들고 공개 정적 팩토리 메소드를 정의하십시오. 무언가가 잘못되면 메소드가 발생할 수 있습니다. 그러나 모든 것이 체크 아웃되면 생성자를 호출하므로 던져지지 않습니다.

6
Michael L Perry

생성자는 해당 객체의 생성을 완료 할 수 없을 때 예외를 발생시켜야합니다.

예를 들어, 생성자가 1024KB의 램을 할당해야하는데 그렇게하지 않으면 예외를 발생시켜야합니다. 이렇게하면 생성자의 호출자가 객체를 사용할 준비가되지 않았으며 오류가 있음을 알게됩니다 어딘가에 수정해야합니다.

반 초기화되고 반죽음의 객체는 호출자가 알 수있는 방법이 없기 때문에 문제와 문제를 유발할뿐입니다. 오히려 true 또는 false를 반환하는 isOK () 함수에 대한 호출을 실행하기 위해 프로그래밍에 의존하는 것보다 문제가 발생할 때 생성자가 오류를 throw하도록하고 싶습니다.

5
Denice

특히 생성자 내부에 리소스를 할당하는 경우 항상 피하기 쉽습니다. 언어에 따라 소멸자가 호출되지 않으므로 수동으로 정리해야합니다. 언어의 객체 수명이 언제 시작되는지에 따라 다릅니다.

내가 실제로 한 유일한 시간은 어딘가에 보안 문제가 있었을 때입니다. 즉, 개체를 만들 수 없어야하지 않아야합니다.

4
blowdart

생성자가 자체를 올바르게 정리하는 한 예외를 throw하는 것이 합리적입니다. RAII 패러다임 (자원 획득이 초기화 됨)을 따르는 경우 is 생성자가 의미있는 작업을하는 것은 매우 일반적입니다. 잘 작성된 생성자는 완전히 초기화 할 수 없으면 자체적으로 정리됩니다.

4
Matt Dillard

내가 알 수있는 한, 아무도 1 단계 및 2 단계 구조의 최고를 구현하는 상당히 명백한 솔루션을 제시하지 않습니다.

note :이 답변은 C #을 가정하지만 원칙은 대부분의 언어로 적용될 수 있습니다.

첫째, 두 가지 장점 :

1 단계

1 단계 구성은 잘못된 상태의 개체가 존재하지 않도록하여 모든 종류의 잘못된 상태 관리와 함께 제공되는 모든 버그를 방지함으로써 이점을 제공합니다. 그러나 생성자가 예외를 던지기를 원하지 않기 때문에 일부 사람들이 이상하게 느끼고 때로는 초기화 인수가 유효하지 않을 때해야 할 일이 있습니다.

public class Person
{
    public string Name { get; }
    public DateTime DateOfBirth { get; }

    public Person(string name, DateTime dateOfBirth)
    {
        if (string.IsNullOrWhitespace(name))
        {
            throw new ArgumentException(nameof(name));
        }

        if (dateOfBirth > DateTime.UtcNow) // side note: bad use of DateTime.UtcNow
        {
            throw new ArgumentOutOfRangeException(nameof(dateOfBirth));
        }

        this.Name = name;
        this.DateOfBirth = dateOfBirth;
    }
}

검증 방법을 통한 2 단계

2 단계 구성은 유효성 검사를 생성자 외부에서 실행할 수있게하므로 생성자 내에서 예외를 throw하지 않아도됩니다. 그러나 "유효하지 않은"인스턴스가 남게되므로 인스턴스를 추적하고 관리해야하는 상태가되거나 힙 할당 직후에 버립니다. 그것은 왜 우리가 사용하지 않는 객체에 대해 힙 할당과 메모리 수집을 수행합니까?

public class Person
{
    public string Name { get; }
    public DateTime DateOfBirth { get; }

    public Person(string name, DateTime dateOfBirth)
    {
        this.Name = name;
        this.DateOfBirth = dateOfBirth;
    }

    public void Validate()
    {
        if (string.IsNullOrWhitespace(Name))
        {
            throw new ArgumentException(nameof(Name));
        }

        if (DateOfBirth > DateTime.UtcNow) // side note: bad use of DateTime.UtcNow
        {
            throw new ArgumentOutOfRangeException(nameof(DateOfBirth));
        }
    }
}

개인 생성자를 통한 단일 단계

그렇다면 어떻게 생성자에서 예외를 방지하고 즉시 버려 질 객체에 대해 힙 할당을 수행하지 못하게 할 수 있습니까? 매우 기본적입니다. 생성자를 private으로 만들고 인스턴스화를 수행하도록 지정된 정적 메서드를 통해 인스턴스를 생성하므로 힙 할당 만 after 유효성 검사.

public class Person
{
    public string Name { get; }
    public DateTime DateOfBirth { get; }

    private Person(string name, DateTime dateOfBirth)
    {
        this.Name = name;
        this.DateOfBirth = dateOfBirth;
    }

    public static Person Create(
        string name,
        DateTime dateOfBirth)
    {
        if (string.IsNullOrWhitespace(Name))
        {
            throw new ArgumentException(nameof(name));
        }

        if (dateOfBirth > DateTime.UtcNow) // side note: bad use of DateTime.UtcNow
        {
            throw new ArgumentOutOfRangeException(nameof(DateOfBirth));
        }

        return new Person(name, dateOfBirth);
    }
}

개인 생성자를 통한 비동기 단일 단계

앞서 언급 한 유효성 검사 및 힙 할당 방지 이점 외에도 이전 방법론은 비동기 지원이라는 또 다른 멋진 이점을 제공합니다. API를 사용하기 전에 베어러 토큰을 검색해야하는 경우와 같이 다단계 인증을 처리 할 때 유용합니다. 이 방법으로, 유효하지 않은 "로그 아웃 된"API 클라이언트가 생기지 않고 요청을 수행하는 동안 권한 부여 오류가 발생하면 API 클라이언트를 다시 작성할 수 있습니다.

public class RestApiClient
{
    public RestApiClient(HttpClient httpClient)
    {
        this.httpClient = new httpClient;
    }

    public async Task<RestApiClient> Create(string username, string password)
    {
        if (username == null)
        {
            throw new ArgumentNullException(nameof(username));
        }

        if (password == null)
        {
            throw new ArgumentNullException(nameof(password));
        }

        var basicAuthBytes = Encoding.ASCII.GetBytes($"{username}:{password}");
        var basicAuthValue = Convert.ToBase64String(basicAuthBytes);

        var authenticationHttpClient = new HttpClient
        {
            BaseUri = new Uri("https://auth.example.io"),
            DefaultRequestHeaders = {
                Authentication = new AuthenticationHeaderValue("Basic", basicAuthValue)
            }
        };

        using (authenticationHttpClient)
        {
            var response = await httpClient.GetAsync("login");
            var content = response.Content.ReadAsStringAsync();
            var authToken = content;
            var restApiHttpClient = new HttpClient
            {
                BaseUri = new Uri("https://api.example.io"), // notice this differs from the auth uri
                DefaultRequestHeaders = {
                    Authentication = new AuthenticationHeaderValue("Bearer", authToken)
                }
            };

            return new RestApiClient(restApiHttpClient);
        }
    }
}

이 방법의 단점은 내 경험상 거의 없습니다.

일반적으로이 방법을 사용하면 공개 기본 생성자가없는 객체를 직렬화 해제하는 것이 어렵 기 때문에 더 이상 클래스를 DTO로 사용할 수 없습니다. 그러나 개체를 DTO로 사용하는 경우 실제로 개체 자체의 유효성을 검사하는 것이 아니라 개체를 사용하려고 할 때 개체의 값을 무효화해야합니다. 기술적으로 값이 "잘못된"것이 아니기 때문입니다. DTO에.

또한 IOC 컨테이너가 객체를 생성하도록 허용해야 할 때 팩토리 메소드 또는 클래스를 생성하게됩니다. 그렇지 않으면 컨테이너가 객체를 인스턴스화하는 방법을 알지 못하므로). 그러나 많은 경우에 팩토리 메소드는 Create 메소드 자체 중 하나입니다.

4
cwharris

이니셜 라이저에서 예외를 throw하면 [[[MyObj alloc] init] autorelease] 패턴을 사용하는 코드가 있으면 예외가 자동 릴리스를 건너 뜁니다.

이 질문을보십시오 :

init에서 예외를 발생시킬 때 누출을 어떻게 방지합니까?

3
stevex

UI 컨트롤 (ASPX, WinForms, WPF 등)을 작성하는 경우 디자이너 (Visual Studio)가 컨트롤을 만들 때 처리 할 수 ​​없으므로 생성자에서 예외가 발생하지 않도록해야합니다. 제어 수명주기 (제어 이벤트)를 알고 가능하면 지연 초기화를 사용하십시오.

3
Nick

C++ FAQ 섹션 17.217.4 를 참조하십시오.

일반적으로 생성자가 작성되어 실패하지 않는 경우 결과를 이식하고 유지하는 것이 더 쉬운 코드를 발견했으며 실패 할 수있는 코드는 오류 코드를 반환하고 객체를 비활성 상태로 두는 별도의 메서드에 배치됩니다. .

3
moonshadow

생성자에서 객체를 초기화 할 수없는 경우 예외를 던지십시오. 하나의 예는 잘못된 인수입니다.

일반적으로 문제의 원인이 무언가를 잘못 알리는 메소드에 더 가까이있을 때 디버깅이 더 쉬워 지므로 가능한 한 빨리 예외를 처리해야합니다.

2
user14070

유효한 객체를 만들 수 없으면 생성자에서 예외를 던져야합니다. 이를 통해 수업에 적절한 불변을 제공 할 수 있습니다.

실제로는 매우주의해야합니다. C++에서는 소멸자가 호출되지 않으므로 리소스 할당 후 던지면 제대로 처리하도록주의를 기울여야합니다!

이 페이지 는 C++의 상황에 대해 자세히 설명합니다.

2
Luke Halliwell

Objective-C에서 모범 사례를 해결할 수는 없지만 C++에서는 생성자가 예외를 throw하는 것이 좋습니다. 특히 isOK () 메소드를 호출하지 않고 구성시 발생하는 예외적 인 상태를보고 할 수있는 다른 방법이 없기 때문에.

함수 try 블록 기능은 생성자 멤버 별 초기화 실패를 지원하도록 특별히 설계되었습니다 (정규 함수에도 사용될 수 있음). 예외 정보를 수정하거나 보강 할 수있는 유일한 방법입니다. 그러나 원래 디자인 목적 (생성자에서 사용)으로 인해 빈 catch () 절로 예외를 삼킬 수 없습니다.

1
mlbrock

대답이 전적으로 언어에 구애받지 않을 수 있는지 잘 모르겠습니다. 일부 언어는 예외와 메모리 관리를 다르게 처리합니다.

나는 예외를 사용하지 않는 언어에 의해 개발자가 불타 버렸기 때문에 예외는 절대 사용되지 않아야하고 초기화 프로그램에서는 오류 코드 만 요구하는 코딩 표준에서 일했습니다. 가비지 수집이없는 언어는 힙을 처리하고 스택을 매우 다르게 처리하므로 RAII 이외의 객체에 중요 할 수 있습니다. 팀이 일관성을 유지하도록 결정하는 것이 중요하지만 생성자 이후에 초기화 프로그램을 호출해야하는 경우 기본적으로 알 수 있습니다. 모든 메소드 (생성자 포함)는 어떤 예외를 처리 할 수 ​​있는지 문서화해야합니다. 따라서 호출자는 메소드를 처리하는 방법을 알고 있습니다.

객체를 초기화하는 것을 잊기 쉽기 때문에 일반적으로 단일 단계 구성을 선호하지만 많은 예외가 있습니다.

  • 예외에 대한 귀하의 언어 지원은 그리 좋지 않습니다.
  • newdelete을 (를) 계속 사용해야하는 중요한 설계 이유가 있습니다.
  • 초기화는 프로세서를 많이 사용하므로 개체를 만든 스레드에 대해 비동기 적으로 실행해야합니다.
  • DLL 다른 언어를 사용하는 응용 프로그램의 인터페이스 외부에서 예외를 발생시킬 수 있습니다. 공용 인터페이스보다 먼저 잡 힙니다 (C #에서 C++ 예외를 잡을 수는 있지만 건너 뛸 수있는 방법이 있습니다).
  • 정적 생성자 (C #)
1
Denise Skidmore

예, 생성자가 내부 부분 중 하나를 빌드하지 못하면 선택적으로 생성자 문서에 정식으로 언급 된 명시 적 예외 를 던질 책임이 있습니다 (특정 언어로 선언). .

이것은 유일한 옵션이 아닙니다 : 생성자를 끝내고 객체를 만들 수는 있지만 'isCoherent ()'메서드가 false를 반환하여 불일치 상태를 시그널링 할 수 있습니다 (일부 경우 순서가 바람직 할 수 있음) 예외로 인한 실행 워크 플로우의 잔인한 중단을 피하기 위해)
경고 : EricSchaefer의 말에 따르면 단위 테스트에 약간의 복잡성을 가져올 수 있습니다 (던지는 함수를 트리거하는 조건으로 인해 함수의 사이 클릭 복잡도 를 증가시킬 수 있음)

호출자로 인해 실패하면 (호출자가 제공 한 null 인수와 같이 호출 된 생성자가 널이 아닌 인수를 예상하는 경우) 생성자는 검사되지 않은 런타임 예외를 처리합니다.

1
VonC

생성하는 동안 예외를 던지는 것은 코드를 더 복잡하게 만드는 좋은 방법입니다. 단순 해 보이는 것들이 갑자기 어려워집니다. 예를 들어 스택이 있다고 가정 해 봅시다. 스택을 팝하고 최고 값을 어떻게 반환합니까? 글쎄, 스택의 객체가 생성자를 던질 수 있다면 (호출자에게 반환하도록 임시 구성), 당신은 데이터를 잃지 않을 것이라고 보장 할 수 없습니다 (감소 스택 포인터, value의 copy 생성자를 사용하여 반환 값을 생성하십시오) 스택, 던지고 이제 아이템을 잃어 버린 스택이 생겼습니다)! 이것이 std :: stack :: pop이 값을 반환하지 않는 이유이며 std :: stack :: top을 호출해야합니다.

이 문제는 잘 설명되어 있습니다 here , 예외 안전 코드 작성, 항목 10을 확인하십시오.

1
Don Neufeld

OO에서 일반적인 계약은 객체 메소드가 실제로 작동한다는 것입니다.

따라서, 좀비 객체를 생성자/초기 값으로 반환하지 않으려면.

좀비가 작동하지 않으며 내부 구성 요소가 누락되었을 수 있습니다. 널 포인터 예외가 발생하기를 기다리고 있습니다.

몇 년 전에 Objective C에서 좀비를 처음 만들었습니다.

모든 경험 법칙과 마찬가지로 "예외"가 있습니다.

특정 인터페이스는 예외를 처리 할 수있는 "초기화"메소드가 존재한다는 계약이있을 수 있습니다. 이 인터페이스를 채우는 객체는 초기화가 호출 될 때까지 속성 설정자를 제외한 모든 호출에 올바르게 응답하지 않을 수 있습니다. 부팅 과정에서 OO 운영 체제의 장치 드라이버에 이것을 사용했으며 작동 가능했습니다.

일반적으로 좀비 개체는 원하지 않습니다. become 인 스몰 토크와 같은 언어에서는 상황이 약간 어지럽지만 become을 과도하게 사용하는 것도 나쁜 스타일입니다. Become을 사용하면 현장에서 객체를 다른 객체로 변경할 수 있으므로 envelope-wrapper (Advanced C++) 또는 전략 패턴 (GOF)이 필요하지 않습니다.

1
Tim Williscroft

OP의 질문에 "언어에 구애받지 않는"태그가 있습니다.이 질문에 모든 언어/상황에 대해 같은 방식으로 안전하게 대답 할 수는 없습니다.

다음 C # 예제의 클래스 계층 구조는 클래스 B의 생성자를 던져서 메인의 using 종료시 클래스 A의 IDisposeable.Dispose에 대한 즉각적인 호출을 건너 뛰고 클래스 A의 리소스를 명시 적으로 폐기하지 않습니다.

예를 들어, 클래스 A가 구성시 네트워크 리소스에 연결된 Socket을 만든 경우에도 using 블록 (상대적으로 숨겨진 예외)이 발생한 경우 일 수 있습니다.

class A : IDisposable
{
    public A()
    {
        Console.WriteLine("Initialize A's resources.");
    }

    public void Dispose()
    {
        Console.WriteLine("Dispose A's resources.");
    }
}

class B : A, IDisposable
{
    public B()
    {
        Console.WriteLine("Initialize B's resources.");
        throw new Exception("B construction failure: B can cleanup anything before throwing so this is not a worry.");
    }

    public new void Dispose()
    {
        Console.WriteLine("Dispose B's resources.");
        base.Dispose();
    }
}
class C : B, IDisposable
{
    public C()
    {
        Console.WriteLine("Initialize C's resources. Not called because B throws during construction. C's resources not a worry.");
    }

    public new void Dispose()
    {
        Console.WriteLine("Dispose C's resources.");
        base.Dispose();
    }
}


class Program
{
    static void Main(string[] args)
    {
        try
        {
            using (C c = new C())
            {
            }
        }
        catch
        {           
        }

        // Resource's allocated by c's "A" not explicitly disposed.
    }
}
1
Ashley

나는 단지 Objective C를 배우기 때문에 실제로 경험을 할 수는 없지만 Apple의 문서에서 이것에 대해 읽었습니다.

http://developer.Apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/chapter_3_section_6.html

요청한 질문을 처리하는 방법을 알려줄뿐만 아니라 설명하는 데 도움이됩니다.

0
Scott Swezey

예외에 대해 내가 본 가장 좋은 조언은 대안이 사후 조건을 충족시키지 않거나 불변을 유지하지 못하는 경우에만 예외를 던지는 것입니다.

이 조언은 불명확 한 주관적 결정 (좋은 아이디어))을 이미 결정한 설계 결정 (불변 및 사후 조건)에 기반한 기술적이고 정확한 질문으로 대체합니다.

컨스트럭터는 그 조언에 대한 특별한 것이지만 특별한 것은 아닙니다. 문제는 클래스에 어떤 불변 값이 있어야합니까? 시공 후에 불려질 별도의 초기화 메소드를 옹호하는 사람들은 클래스가 두 개 이상의 작동 모드를 가지며, 시공 후 준비되지 않음 모드와 하나 이상의 준비 모드, 초기화 후 입력됩니다. 그것은 추가적인 합병증이지만 클래스에 여러 작동 모드가있는 경우 허용됩니다. 클래스에 작동 모드가 없으면 합병증이 어떻게 가치가 있는지 알기가 어렵습니다.

별도의 초기화 방법으로 설정을 푸시한다고해서 예외가 발생하지 않도록 할 수는 없습니다. 생성자가 던질 수있는 예외는 이제 초기화 메소드에 의해 발생합니다. 초기화되지 않은 객체에 대해 호출되면 클래스의 모든 유용한 메소드에서 예외를 throw해야합니다.

또한 생성자에 의해 예외가 발생할 가능성을 피하는 것은 번거롭고 많은 표준 라이브러리에서 많은 경우 불가능입니다. 그 라이브러리의 디자이너는 생성자에서 예외를 던지는 것이 좋은 생각이라고 믿기 때문입니다. 특히, 공유 가능하지 않거나 유한 한 자원 (예 : 메모리 할당)을 확보하려는 모든 조작이 실패 할 수 있으며 실패는 일반적으로 예외를 발생시켜 OO 언어 및 라이브러리에 표시됨)입니다.

0
Raedwald

Java 관점에서 엄밀히 말하면, 잘못된 값으로 생성자를 초기화 할 때마다 예외가 발생하므로 잘못된 상태로 생성되지 않습니다.

0
scubabbl

나에게는 다소 철학적 인 디자인 결정입니다.

Ctor 시간부터 존재하는 한 유효한 인스턴스를 갖는 것이 매우 좋습니다. 사소한 경우가 많지 않은 경우 메모리/자원 할당을 수행 할 수없는 경우 ctor에서 예외를 발생시켜야 할 수 있습니다.

다른 접근법으로는 자체 문제가있는 init () 메소드가 있습니다. 그중 하나는 실제로 init ()가 호출되도록 보장하는 것입니다.

변형은 접근 자/뮤 테이터가 처음 호출 될 때 자동으로 init ()를 호출하기 위해 게으른 접근 방식을 사용하지만 잠재적 호출자는 객체가 유효한지 걱정해야합니다. ( '존재하므로 유효한 철학이다').

이 문제를 해결하기 위해 제안 된 다양한 디자인 패턴을 보았습니다. ctor를 통해 초기 객체를 만들 수는 있지만 init ()를 호출하여 accesors/mutators를 사용하여 초기화 된 포함 된 객체를 가져와야합니다.

각 접근 방식에는 기복이 있습니다. 나는이 모든 것을 성공적으로 사용했다. 생성 된 순간부터 즉시 사용 가능한 객체를 만들지 않으면 사용자가 init () 전에 상호 작용하지 않도록 많은 양의 단언 또는 예외를 권장합니다.

부록

나는 C++ 프로그래머의 관점에서 썼다. 또한 예외가 발생했을 때 해제되는 리소스를 처리하기 위해 RAII 관용구를 올바르게 사용한다고 가정합니다.

0
nsanders

모든 객체 생성에 팩토리 또는 팩토리 메소드를 사용하면 생성자에서 예외를 발생시키지 않고 유효하지 않은 객체를 피할 수 있습니다. 작성 메소드는 요청 된 오브젝트를 작성할 수 있으면 리턴하거나 그렇지 않으면 널을 리턴해야합니다. null을 반환하면 객체 생성에서 무엇이 잘못되었는지 알려주지 않기 때문에 클래스 사용자의 구성 오류 처리에있어 약간의 유연성이 손실됩니다. 그러나 객체를 요청할 때마다 여러 예외 처리기의 복잡성과 처리하지 않아야 할 예외를 잡을 위험을 추가하지 않아도됩니다.

0
Tegan Mulholland