OOP 코딩 스타일 : 생성자의 모든 것을 초기화합니까?


14

나는 여전히 자신을 도제 프로그래머라고 생각하기 때문에 항상 전형적인 프로그래밍을위한 "더 나은"방법을 배우려고합니다. 오늘날 동료는 코딩 스타일이 불필요한 작업을 수행한다고 주장했으며 다른 사람들의 의견을 듣고 싶습니다. 일반적으로 OOP 언어 (일반적으로 C ++ 또는 Python)로 클래스를 디자인 할 때 초기화를 두 개의 다른 부분으로 분리합니다.

class MyClass1 {
public:
    Myclass1(type1 arg1, type2 arg2, type3 arg3);
    initMyClass1();
private:
    type1 param1;
    type2 param2;
    type3 param3;
    type4 anotherParam1;
};

// Only the direct assignments from the input arguments are done in the constructor
MyClass1::myClass1(type1 arg1, type2 arg2, type3 arg3)
    : param1(arg1)
    , param2(arg2)
    , param3(arg3)
    {}

// Any other procedure is done in a separate initialization function 
MyClass1::initMyClass1() {
    // Validate input arguments before calculations
    if (checkInputs()) {
    // Do some calculations here to figure out the value of anotherParam1
        anotherParam1 = someCalculation();
    } else {
        printf("Something went wrong!\n");
        ASSERT(FALSE)
    }
}

(또는 파이썬 동등)

class MyClass1:

    def __init__(self, arg1, arg2, arg3):
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3
        #optional
        self.anotherParam1 = None

    def initMyClass1():
        if checkInputs():
            anotherParam1 = someCalculation()
        else:
            raise "Something went wrong!"

이 접근법에 대한 귀하의 의견은 무엇입니까? 초기화 과정을 나누지 말아야합니까? 이 질문은 C ++ 및 Python에만 국한된 것이 아니라 다른 언어에 대한 답변도 높이 평가됩니다.




1
그리고 파이썬의 경우 : stackoverflow.com/questions/20661448/…
Doc Brown

왜 일반적으로합니까? 습관? 그럴 이유가 있었습니까?
JeffO

@JeffO MFC 라이브러리로 GUI를 만들 때이 습관을 얻었습니다. CApp, CWindow, CDlg 등과 같은 대부분의 UI 관련 클래스에는 덮어 쓸 수있는 OnInit () 함수가 있으며, 이는 해당 메시지에 응답합니다.
Caladbolgll

답변:


28

때로는 문제가 있지만 생성자의 모든 것을 초기화하면 많은 이점이 있습니다.

  1. 오류가 발생하면 가능한 한 빨리 발생하며 진단하기가 가장 쉽습니다. 예를 들어 null이 잘못된 인수 값이면 생성자에서 테스트하고 실패합니다.
  2. 개체는 항상 유효한 상태입니다. 동료 는 실수를 할 수 없으며 전화가 initMyClass1()없기 때문에 전화 하는 것을 잊지 않습니다 . "가장 저렴하고 빠르며 안정적인 구성 요소는없는 구성 요소입니다."
  3. 의미가 있다면 객체를 변경할없어 많은 장점이 있습니다.

2

사용자에게 제공하는 추상화에 대해 생각해보십시오.

한 번에 수행 할 수있는 작업을 두 개로 분할하는 이유는 무엇입니까?

추가 초기화는 API를 사용하는 프로그래머가 기억해야 할 추가 사항이며, 올바르게 수행하지 않으면 더 많은 잘못을 제공하지만이 추가 부담으로 인해 어떤 가치가 있습니까?

당신은 죽은 단순하고 사용하기 쉽고 잘못된 추상화를 제공하기를 원합니다. 기억해야 할 것들이 많지 않고 뛰어 넘을 정도로 프로그래밍이 어렵다. API 사용자 (자신의 API를 사용하더라도) 가 성공구덩이에 빠지기를 원합니다 .


1

빅 데이터 영역을 제외한 모든 것을 초기화합니다. 정적 분석 도구는 생성자에서 초기화되지 않은 필드에 플래그를 지정합니다. 그러나 가장 생산적이고 안전한 방법은 기본 생성자를 가진 모든 멤버 변수를 가지고 기본이 아닌 초기화가 필요한 변수 만 명시 적으로 초기화하는 것입니다.


0

객체에 많은 초기화가있는 경우가 있는데 두 가지 범주로 나눌 수 있습니다.

  1. 변경 불가능하거나 재설정 할 필요가없는 속성

  2. 작업을 수행 한 후 일부 조건에 따라 원래 값 (또는 템플릿 값)으로 되돌려 야하는 속성, 일종의 소프트 리셋. 예를 들어 연결 풀의 연결.

여기에서 InitialiseObject ()와 같은 별도의 함수로 유지되는 초기화의 두 번째 부분을 ctor에서 호출 할 수 있습니다.

소프트 리셋이 필요한 경우 객체를 버리고 다시 만들 필요없이 동일한 기능을 나중에 호출 할 수 있습니다.


0

다른 사람들이 말했듯이 일반적으로 생성자를 초기화하는 것이 좋습니다.

그러나 특정한 경우에는 적용되지 않을 수도있는 이유가 있습니다.

오류 처리

많은 언어에서 생성자의 오류를 알리는 유일한 방법은 예외를 발생시키는 것입니다.

초기화에 오류가 발생할 가능성이있는 경우 (예 : IO가 포함되거나 매개 변수가 사용자 입력일 수있는 경우) 예외를 발생시키는 것이 유일한 방법입니다. 경우에 따라 원하는 방식이 아닐 수도 있으며 오류가 발생하기 쉬운 코드를 별도의 초기화 함수로 분리하는 것이 더 합리적 일 수 있습니다.

아마도 가장 일반적인 예는 프로젝트 / 조직 표준이 예외를 끄는 경우 C ++에 있습니다.

상태 머신

명시적인 상태 전이가있는 객체를 모델링하는 경우입니다. 예를 들어 열거 나 닫을 수있는 파일 또는 소켓입니다.

이 경우 객체 구성 (및 삭제)은 메모리 지향 속성 (파일 이름, 포트 등) 만 처리하는 것이 일반적입니다. 그러면 상태 전이를 구체적으로 관리하는 기능이있을 것입니다.

장점은 위와 같이 오류 처리에 있지만 구성을 초기화와 분리하는 경우가있을 수 있습니다 (파일 벡터를 작성하고 비동기 적으로 여는 등).

다른 사람들이 말했듯이 단점은 이제 클래스 사용자에게 상태 관리 부담을 가중한다는 것입니다. 건설만으로 관리 할 수 ​​있다면 RAII를 사용하여이를 자동으로 수행 할 수 있습니다.

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