init () 메소드는 코드 냄새가 있습니까?


20

init()유형에 대한 메소드를 선언 할 목적이 있습니까?

나는 우리가 생성자를 선호init() 해야하는지 또는 선언을 피하는 방법을init() 묻지 않습니다 .

이 경우 내가 부탁 해요 어떤 선언 뒤에 이론적 init()방법 (그것이 얼마나 일반적인보고를) 또는 코드 냄새의 경우 피해야한다.


init()관용구는 매우 일반적이지만, 나는 실제 혜택을 볼 아직.

메소드를 통한 초기화를 장려하는 유형에 대해 이야기하고 있습니다.

class Demo {
    public void init() {
        //...
    }
}

프로덕션 코드에서 언제 사용됩니까?


생성자가 객체를 완전히 초기화하지 않아 부분적으로 생성 된 객체가 생성되기 때문에 코드 냄새가 날 수 있다고 생각합니다. 상태가 설정되지 않은 경우 개체가 존재하지 않아야합니다.

이것은 엔터프라이즈 응용 프로그램의 의미에서 생산 속도를 높이는 데 사용되는 일종의 기술의 일부라고 생각합니다. 그런 관용구가 있다고 생각할 수있는 유일한 논리적 인 이유입니다. 그렇다면 어떻게 유익한 지 잘 모르겠습니다.


1
"... 얼마나 흔한 지 보자 ...": 일반적입니까? 몇 가지 예를 들어 줄 수 있습니까? 아마도 초기화와 구성을 분리 해야하는 프레임 워크를 다루고있을 것입니다.
에서 오는

메소드가 기본 클래스 또는 파생 클래스 또는 둘 다에 있습니까? (또는 : 상속 계층 구조에 속하는 클래스에서 메소드가 발견 되었습니까? 기본 클래스가 init()파생 클래스 에서 on을 호출합니까 , 아니면 그 반대입니까?) "는 가장 파생 된 클래스의 생성이 완료된 후에 만 ​​실행될 수 있습니다. 다단계 초기화의 예입니다.
rwong

인스턴스화 시점에서 초기화하지 않으려면 둘을 분리하는 것이 좋습니다.
JᴀʏMᴇᴇ

당신은 관심이있을 수 있습니다. is-a-start-run-or-execute-method-a-good-practice
Laiv

답변:


39

예, 코드 냄새입니다. 코드 냄새는 반드시 제거해야하는 것은 아닙니다. 다시 한 번 살펴 보는 것입니다.

여기에는 기본적으로 다른 두 가지 상태 인 사전 초기화 및 사후 초기화라는 객체가 있습니다. 그러한 국가들은 서로 다른 책임, 부름을받을 수있는 다른 방법 및 행동이 다릅니다. 효과적으로 두 개의 다른 클래스입니다.

물리적으로 두 개의 개별 클래스를 만들면 잠재적 인 버그의 전체 클래스를 정적으로 제거 할 수 있습니다. 대신 모델이 "실제 모델"과 거의 일치하지 않을 수 있습니다. 당신은 일반적으로 첫 번째 이름 Config또는 Setup그런이나 뭐.

다음에, construct-init 이디엄을 2 클래스 모델로 리팩토링하고 그것이 어떻게 당신에게 적합한 지보십시오.


6
2 클래스 모델을 사용해 보라는 제안이 좋습니다. 코드 냄새를 해결하기위한 구체적인 단계를 제안하는 것이 유용합니다.
Ivan

1
이것이 지금까지 가장 좋은 대답입니다. 한 가지 사실은 저를 괴롭 힙니다. " 이러한 국가마다 다른 책임, 다른 방법으로 불려질 ​​수있는 행동 및 다른 행동이 있습니다 "-이러한 책임을 분리하지 않으면 SRP에 위배됩니다. 소프트웨어의 목적이 모든 측면에서 실제 시나리오를 복제하는 것이면이 방법이 적합합니다. 그러나 프로덕션 환경에서 개발자는 소프트웨어 관리 환경에 더 잘 맞도록 필요한 경우 모델을 수정하고 관리하기 쉬운 코드를 작성합니다 (다음 의견에서 계속)
Vince Emigh

1
실제 세계는 다른 환경입니다. 복제를 시도하는 프로그램은 대부분의 프로젝트에서 설명하지 않았거나 도메인 고유의 것으로 보이지 않습니다. 도메인 특정 프로젝트에 관해서는 찡그린 많은 것들이 받아 들여지기 때문에 가능한 한 일반적인 것을 유지하려고 노력하고 있습니다 (예를 들어 this생성자를 호출 하는 것이 코드 냄새가 나고 오류가 발생하기 쉬운 코드를 초래할 수있는 방법) 프로젝트가 속한 도메인에 관계없이 권장하지 않습니다).
빈스 에미 그

14

때에 따라 다르지.

init방법은 생성자로부터 분리 된 객체의 초기화가 필요없는 경우 코드 냄새이다. 이러한 단계를 분리하는 것이 적절한 경우가 있습니다.

빠른 Google 검색 으로이 예를 얻었습니다 . 객체 할당 (생성자) 중에 실행 된 코드가 초기화 자체와 더 잘 분리 될 수있는 더 많은 경우를 쉽게 상상할 수 있습니다. 레벨 시스템이 있고 할당 / 구성은 레벨 X에서 발생하지만 레벨 Y에서만 초기화가 발생합니다. Y 만 필요한 매개 변수를 제공 할 수 있기 때문입니다. "init"는 비용이 많이 들고 할당 된 오브젝트의 서브 세트에 대해서만 실행되어야하며 해당 서브 세트의 결정은 레벨 Y에서만 수행 할 수 있습니다. 생성자로 수행 할 수없는 클래스. 레벨 X는 상속 트리에서 할당 된 객체를 제공하지만 레벨 Y는 구체적인 인터페이스 (어디에서나init 아마도 정의).

물론 내 경험에 따르면이 사례는 생성자에서 직접 모든 초기화를 수행 할 수있는 표준 사례의 일부에 불과하며 별도의 init방법 을 볼 때마다 필요성에 대해 의문을 제기하는 것이 좋습니다.


2
그 링크의 대답은 생성자의 작업량을 줄이는 데 유용하지만 부분적으로 생성 된 객체를 권장합니다. 작은 생성자는 그렇게 나에게 (객체를 떠나, 필요한 모든 초기화 메소드를 호출하는 것을 잊지 가능성이 오류가 발생하기 쉬운) 답변이 새로운 문제를 만드는 것과 코드 냄새 범주에 해당 분해를 통해 achived 할 수 있습니다
빈스 Emigh

@VinceEmigh : 좋아, 내가, 남동쪽 platfform 여기에 아마 최고의 하나를 찾을 수있는 첫 번째 예,하지만 거기에 있는 별도의 합법적 인 사용 사례 init방법. 그러나 그러한 방법을 볼 때마다 그 필요성에 대해 자유롭게 질문하십시오.
Doc Brown

필자는 그것이 필요한 상황이 없다고 생각하기 때문에 모든 사용 사례에 대해 질문하고 / 도전하고 있습니다. 나에게 그것은 객체 생성의 타이밍이 좋지 않으며 적절한 디자인을 통해 피할 수있는 오류의 후보이기 때문에 피해야합니다. init()방법을 올바르게 사용했다면 목적에 대해 배우면 도움이 될 것입니다. 내 무지를 실례합니다, 나는 그것을 사용하는 데 어려움을 겪고있어서 피해야 할 일을 고려하지 못하도록해서 놀랐습니다.
Vince Emigh

1
@VinceEmigh : 그런 상황을 생각할 수 없으면 상상력을 발휘해야합니다. ;-). 아니면 내 대답을 다시 읽고 "할당"으로 줄이지 마십시오. 또는 여러 공급 업체의 더 많은 프레임 워크로 작업하십시오.
Doc Brown

1
@DocBrown 입증 된 방법 대신 상상력을 사용하면 이중 괄호 초기화와 같은 펑키 한 코드가 생깁니다. 영리하지만 비효율적이므로 피해야합니다. 나는 벤더가 그것을 사용한다는 것을 알고 있지만 그것이 사용이 정당하다는 것을 의미하지는 않습니다. 당신이 그것을 느끼고 있다면, 내가 알아 내려고 노력했던 이유를 알려주십시오. 당신은 다소 유익한 목적을 가진 데 어려움을 겪었지만, 좋은 디자인을 장려하는 모범을 보이는 데 어려움을 겪고있는 것처럼 들립니다. 어떤 상황에서 사용할 있는지 알고 있지만 사용해야 합니까?
빈스 에미 그

5

내 경험은 두 그룹으로 나뉩니다.

  1. 실제로 init ()가 필요한 코드. 수퍼 클래스 또는 프레임 워크가 클래스 생성자가 생성 중에 모든 종속성을 얻지 못하는 경우에 발생할 수 있습니다.
  2. init ()가 사용되었지만 피할 수있는 코드.

내 개인적인 경험에서 나는 단지 (1)의 몇 가지 사례를 보았지만 (2)의 더 많은 사례를 보았습니다. 결과적으로, 나는 보통 init ()가 코드 냄새라고 가정하지만 항상 그런 것은 아닙니다. 때때로 당신은 단지 주위를 돌아 다닐 수 없습니다.

빌더 패턴을 사용하면 init ()가 필요 / 욕망을 제거하는 데 도움이 될 수 있습니다.


1
수퍼 클래스 나 프레임 워크가 타입이 생성자를 통해 필요한 의존성을 얻도록 허용하지 않는다면, init()메소드를 추가하면 어떻게 해결할 수 있습니까? 이 init()메소드에는 종속성을 허용하는 매개 변수가 필요하거나 메소드 에서 종속성을 인스턴스화 init()해야하며 생성자에서도 수행 할 수 있습니다. 예를 들어 주시겠습니까?
빈스 에미 그

1
@VinceEmigh : init ()를 사용하여 외부 소스에서 구성 파일을로드하거나 데이터베이스 연결을 열거 나 해당 파일을 열 수 있습니다. 아파치 크런치 프레임 워크의 DoFn.initialize () 메소드가이 방식으로 사용됩니다. 직렬화 할 수없는 내부 필드를로드하는 데에도 사용할 수 있습니다 (DoFns는 직렬화 가능해야 함). 여기서 두 가지 문제점은 (1) initialize 메소드가 호출되도록 보장해야하는 대상과 (2) 객체가 해당 자원을 어디로 가져갈 것인지 (또는 어떻게 구축 할 것인지) 알아야한다는 것입니다.
Ivan

1

Init 메소드가 유용한 일반적인 시나리오는 변경하려는 구성 파일이 있고 응용 프로그램을 다시 시작하지 않고 변경 사항을 적용하는 경우입니다. 물론 이것은 Init 메소드가 생성자와 별도로 호출되어야한다는 것을 의미하지는 않습니다. 생성자에서 Init 메소드를 호출 한 다음 구성 매개 변수가 변경 될 때 나중에 호출 할 수 있습니다.

요약하면 : 코드 냄새인지 여부에 관계없이 대부분의 딜레마는 상황과 상황에 따라 다릅니다.


구성 업데이트 및이 재설정 객체를 요구하는 경우 / 당신은이 같은 개체의 행동이 더 좋을 것이라고 생각하지 않는다, 구성에 따라 그것의 상태를 변경 관찰자 방향을 Config?
빈스

@Vince Emigh necesarilly가 아닙니다. 구성이 변경되는 정확한 순간을 알면 관찰자가 작동합니다. 그러나 구성 데이터가 응용 프로그램 외부에서 변경 될 수있는 파일에 보관 된 경우 실제로는 다른 방법이 없습니다. 예를 들어, 일부 파일을 구문 분석하고 데이터를 내부 모델로 변환하는 프로그램이 있고 별도의 구성 파일에 누락 된 데이터의 기본값이 포함되어있는 경우 기본값을 변경하면 다음에 실행할 때 다시 읽습니다. 파싱. 이 경우 내 응용 프로그램에 Init 메소드를 사용하는 것이 매우 편리합니다.
블라디미르 Stokic

설정 파일은 외부에서 런타임에 수정 된 경우,없는 사람들 변수를 다시로드 할 수있는 방법이 없습니다 일부 는 (초기화 호출하는 데 필요한 응용 프로그램 알리는 알림의 종류 update/ reload실제로 그 변경 사항을 등록에 아마 행동 이런 종류의 더 자세한 설명이 될 것이다는) . 이 경우 해당 알림으로 인해 응용 프로그램의 구성 값이 내부적으로 변경 될 수 있습니다. 구성을 관찰 할 수 있으면 관찰자에게 구성 값 중 하나를 변경하라는 메시지가 표시되면 알릴 수 있다고 생각합니다. 아니면 당신의 모범을 잘못 이해하고 있습니까?
빈스 에미 그

응용 프로그램은 일부 외부 파일 (일부 GIS 또는 다른 시스템에서 내 보낸)을 가져 와서 해당 파일을 구문 분석하여 앱이 사용하는 시스템 인 일부 내부 모델로 변환합니다. 해당 프로세스 중에 일부 데이터 간격이 발견 될 수 있으며 해당 값을 기본값으로 설정하여 해당 데이터 간격을 채울 수 있습니다. 이러한 기본값은 변환 프로세스 시작시 읽을 수있는 구성 파일에 저장 될 수 있으며, 변환 할 새 파일이있을 때마다 호출됩니다. 그것이 바로 Init 메소드가 유용 할 수있는 곳입니다.
블라디미르 스토 키

1

사용 방법에 따라 다릅니다.

비디오 게임을 만들고 성능을 유지해야 할 때와 같이 가비지 수집기의 성능을 저하시킬 때와 같이 힙에 객체를 계속 할당하지 않으려는 경우 Java / C #과 같은 가비지 수집 언어에서 해당 패턴을 사용합니다. 생성자를 사용하여 필요한 다른 힙 할당 init을 만들고 재사용 할 때마다 기본 유용한 상태를 만듭니다. 이것은 객체 풀의 개념과 관련이 있습니다.

초기화 명령의 공통 서브 세트를 공유하는 여러 생성자가있는 경우에도 유용하지만이 경우 init개인용입니다. 그렇게하면 가능한 한 각 생성자를 최소화 할 수 있으므로 각 생성자는 고유 한 지침과 init나머지 작업을 수행 하는 단일 호출 만 포함합니다 .

그러나 일반적으로 코드 냄새입니다.


하지 않을까요 reset()방법은 첫 번째 문에 대한 더 자세한 설명이 될? 두 번째 (많은 생성자) 많은 생성자가있는 것은 코드 냄새입니다. 객체에 여러 목적 / 책임이 있다고 가정하여 SRP 위반을 제안합니다. 객체에는 하나의 책임이 있어야하며 생성자는 해당 하나의 책임에 필요한 종속성을 정의해야합니다. 선택적 값으로 인해 여러 생성자가있는 경우 망원경 (코드 냄새이기도 함) 대신 빌더를 사용해야합니다.
빈스 에미 그

@VinceEmigh init을 사용하거나 재설정 할 수 있습니다. 결국 이름입니다. Init은 처음 사용했을 때 설정되지 않은 객체를 재설정하는 것이 의미가 없기 때문에 사용하는 경향이 있습니다. 생성자 문제는 생성자가 많지 않도록 노력하지만 때로는 도움이됩니다. 모든 언어의 string생성자 목록과 수많은 옵션을 살펴보십시오 . 저에게는 보통 최대 생성자 수는 3 명이지만 초기화와 같은 명령의 공통 부분 집합은 코드를 공유하지만 어떤 식 으로든 차이가있을 때 의미가 있습니다.
Cody

이름은 매우 중요합니다. 클라이언트가 계약서를 읽지 않아도 수행되는 동작을 설명합니다. 잘못된 이름은 잘못된 가정으로 이어질 수 있습니다. 의 여러 생성자에 대해서는 String문자열 생성을 분리하여 해결할 수 있습니다. 결국 a String는 a String이며 생성자는 필요에 따라 수행하는 데 필요한 내용 만 수락해야합니다. 이러한 생성자의 대부분은 생성자를 오용하는 변환 목적으로 노출됩니다. 생성자는 논리를 수행 하지 않아야합니다. 그렇지 않으면 초기화에 실패하여 쓸모없는 개체가 남게됩니다.
빈스 에미 그

JDK는 끔찍한 디자인으로 가득 차 있습니다. 나는 머리 꼭대기에서 약 10 개를 나열 할 수 있습니다. 많은 언어의 핵심 측면이 공개 된 이후 소프트웨어 디자인이 발전했으며, 코드를 현대적으로 재 설계해야 할 경우 코드가 깨질 가능성이 있습니다.
빈스 에미 그

1

init()메소드는 다른 오브젝트가 동시에 사용하는 외부 자원이 필요한 오브젝트 (예 : 네트워크 연결)가있는 경우 상당히 의미가 있습니다. 객체 수명 기간 동안 리소스를 사용하지 않으려는 경우가 있습니다. 이러한 상황에서는 자원 할당이 실패 할 때 생성자에서 자원을 할당하지 않을 수 있습니다.

특히 임베디드 프로그래밍에서는 결정적인 메모리 풋 프린트를 원하므로 생성자를 조기에 호출하거나 정적 일 수도 있고 특정 조건이 충족되는 경우에만 초기화하는 것이 일반적입니다.

그러한 경우를 제외하고는 모든 것이 생성자로 들어가야한다고 생각합니다.


유형에 연결이 필요한 경우 DI를 사용해야합니다. 연결을 만드는 데 문제가 있으면 연결이 필요한 개체를 만들면 안됩니다. 클래스 내에서 연결을 생성하면 객체를 생성하게되며 해당 객체는 객체의 종속성 (연결)을 인스턴스화합니다. 종속성의 인스턴스화에 실패하면 사용할 수없는 개체가 생겨 리소스를 낭비하게됩니다.
빈스 에미 그

반드시 그런 것은 아닙니다. 모든 목적에 일시적으로 사용할 수없는 물체가 생깁니다. 이러한 경우, 객체는 리소스를 사용할 수있을 때까지 대기열 또는 프록시 역할을 할 수 있습니다. 완전히 비난하는 init방법이 너무 제한적입니다 (IMHO).
tofro

0

일반적으로 기능적 인스턴스에 필요한 모든 인수를받는 생성자를 선호합니다. 그것은 그 객체의 모든 의존성을 분명히합니다.

반면에 매개 변수가없는 공용 생성자가 필요한 간단한 구성 프레임 워크와 종속성 및 구성 값을 주입하기위한 인터페이스를 사용합니다. 그 후에 구성 프레임 워크 init는 객체 의 메소드를 호출 합니다. 이제 내가 가진 모든 것을 받았으며 작업 준비를 위해 마지막 단계를 수행하십시오. 그러나 참고 : 그것은 자동으로 init 메소드를 호출하는 구성 프레임 워크이므로 호출하는 것을 잊지 마십시오.


0

init () 메서드가 의미 적으로 객체의 상태 수명주기에 포함 된 경우 코드 냄새가 없습니다.

객체를 일관된 상태로 만들기 위해 init ()를 호출해야하는 경우 코드 냄새입니다.

이러한 구조가 존재하는 몇 가지 기술적 이유가 있습니다.

  1. 프레임 워크 훅
  2. 객체를 초기 상태로 재설정 (중복 방지)
  3. 테스트 중 무시 가능성

1
그러나 소프트웨어 엔지니어가 수명주기에이를 포함시킬까요? 정당화 할 수 있고 (부분적으로 생성 된 객체를 권장하는 것으로 간주) 더 효율적인 대안으로 격추 될 수없는 목적이 있습니까? 수명주기에 그것을 포함시키는 것은 코드 냄새 일 것이고, 더 나은 객체 생성 타이밍으로 대체되어야한다고 생각합니다 (그 당시에 사용하지 않으려는 경우 객체에 메모리를 할당하는 이유는 무엇입니까? 객체를 만들기 전에 실제로 필요할 때까지 기다릴 수있을 때 부분적으로 생성 된 객체?)
Vince Emigh

요점은 init 메소드를 호출하기 전에 객체를 사용할 수 있어야한다는 것입니다. 어쩌면 그것이 호출 된 후와 다른 방법 일 수도 있습니다. 상태 패턴을 참조하십시오. 부분적인 객체 구성의 의미에서 그것은 코드 냄새입니다.
oopexpert

-4

init 이름은 때때로 불투명 할 수 있습니다. 자동차와 엔진을 봅시다. 자동차를 시동하려면 (전원 만 켜고 라디오를 들으려면) 모든 시스템이 준비되었는지 확인하십시오.

따라서 엔진, 문, 바퀴 등을 구성하면 화면에 엔진 = 꺼짐이 표시됩니다.

엔진 등은 모두 비싸므로 모니터링을 시작할 필요가 없습니다. 그런 다음 키를 돌리면 엔진-> 시동이라고합니다. 모든 고가의 프로세스를 실행하기 시작합니다.

이제 엔진 =이 보입니다. 그리고 점화 과정이 시작됩니다.

엔진을 사용할 수 없으면 자동차가 켜지지 않습니다.

복잡한 계산으로 엔진을 대체 할 수 있습니다. 엑셀 셀처럼. 모든 셀이 항상 모든 이벤트 핸들러로 활성화 될 필요는 없습니다. 셀에 집중하면 시작할 수 있습니다. 그런 식으로 성능을 향상시킵니다.

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