생성자에서 많은 일이 나쁜가요?
모든 필드를 만드는 것은 final
일반적으로 좋은 생각이지만 때로는 생성자에서 모든 작업을 수행합니다. 최근 에 속성 파일 읽기 및 데이터베이스 액세스를 포함하여 생성자에서 실제로 모든 작업을 수행하는 클래스가 생겼습니다 .
한편으로는 이것이 클래스의 목적이며 읽은 데이터를 캡슐화하고 완전히 초기화 된 객체를 만드는 것을 좋아합니다. 생성자는 대부분의 작업을 위임하므로 전혀 복잡하지 않으므로 괜찮아 보입니다.
반면에 조금 이상하게 느껴집니다. 더욱이 17:58에 대한 이 강연 에는 생성자에서 많은 작업을하지 않는 좋은 이유가 있습니다. 생성자 인수로 적절한 더미를 전달하여 문제를 해결할 수 있다고 생각합니다.
질문은 남아 있습니다. 생성자에서 많은 작업 (또는 모든 작업)을 수행하는 것이 나쁘나요?
"생성자에서 작업하기"는 괜찮다고 생각합니다 ...
... SRP (Single Responsibility Principle)를 위반하지 않고 DI (Dependency Injection) 사용을 고수하는 한 .
나는 너무 최근에 스스로이 질문을하고있다. 그리고 내가 찾은 생성자에서 작업을 수행하는 것에 대한 동기는 다음 중 하나입니다.
- 테스트하기 어렵게 만듭니다.
- 내가 본 모든 예는 DI 가 사용되지 않은 곳 입니다. 실제로 실제 작업을하는 생성자의 잘못이 아닙니다.
- 생성자가 계산하는 모든 결과가 필요하지 않아 처리 시간이 낭비되고 개별적으로 테스트하기가 어려울 수 있습니다.
- 이것은 기본적으로 SRP 위반이며 생성자가 말에 따라 작업을 수행하는 결함이 아닙니다.
- 오래된 컴파일러는 생성자에서 발생하는 예외로 인해 문제가 발생했습니다. 따라서 생성자에서 필드를 할당하는 것 외에 다른 작업을 수행해서는 안됩니다.
- 역사적인 컴파일러 결함을 고려하여 새로운 코드를 작성하는 것은 좋은 생각이 아니라고 생각합니다. 우리는 C ++ 11과 좋은 모든 것을 함께 제거하는 것이 좋습니다.
제 의견은 ...
... 당신의 생성자가 준수하는 일을 할 필요가있는 경우 자원 수집되어 초기화 (RAII) 과 클래스가없는 위반하지 SRP를 하고 DI가 적절하게 사용된다 그런 다음 생성자에서 작업하는 것은 A-Okay! 사용자에게 반환 값을 확인하는 대신 초기화가 완전히 실패한 클래스 객체의 사용을 방지하려는 경우 예외를 throw 할 수도 있습니다.
이것은 매우 개방적인 질문이므로 가능한 한 일반적으로 대답하려고 노력할 것입니다.
생성자에서 작업을 수행하는 것은 예외 처리가 오늘날처럼 널리 보급되지 않았고 발전해 왔던 수년 전처럼 "나쁜"것은 아닙니다. Google Tech 강연은 주로 테스트 관점에서 생성자를 살펴 봅니다. 생성자는 역사적으로 디버그하기가 매우 어려웠으므로 생성자에서 가능한 한 적은 작업을 수행하는 것이 더 낫다는 스피커가 정확합니다.
즉, 생성자를 복잡하게 만드는 것으로 악명 높은 종속성 주입 / 제공자 패턴도 다루고 있음을 알 수 있습니다. 이러한 경우 생성자에 공급자 / DI 코드 만 남겨 두는 것이 좋습니다. 다시 말하지만, 대답은 사용중인 패턴과 코드가 어떻게 "적합"되는지에 따라 달라집니다.
생성자를 사용하는 전체 점 이다 즉시 사용 가능한 깔끔하고 객체를 생성하는 단계; 즉 new Student("David Titarenco", "Senior", 3.5)
. david.initialize()
완전히 어리석은 일이므로 할 필요가 없습니다 .
다음은 내 프로덕션 코드 중 일부입니다.
Config Conf = new Config();
Log.info("Loading server.conf");
Conf.doConfig();
위의 경우 생성자 (비어 있음)로 아무것도하지 않기로 결정했지만 doConfig()
모든 디스크 I / O를 수행 하는 메서드가 있습니다. 나는 종종 doConfig()
방법이 무의미하고 생성자에서 모든 것을해야한다고 생각했습니다 . (결국 구성 파일을 한 번만 확인합니다.)
나는 그것이 당신의 코드에 전적으로 의존한다고 생각하며 생성자에 "stuff"를 넣는 것이 나쁜 것이라고 생각해서는 안됩니다. 그것이 생성자가하는 이유입니다! 때때로 우리는 OOP와 흥분 ( getThis
, setThat
, doBark
) 때 정말 모든 클래스의 요구를 수행하는 것은 부하 설정 파일입니다. 그런 경우에는 모든 것을 생성자에 넣고 하루라고 부릅니다!
일반적으로 객체에 복잡한 생성 알고리즘이있는 경우 Builder 또는 Factory를 사용하여 단순화 할 수 있습니다. 특히 개체를 빌드하기 위해 유효성을 검사해야하는 전제 조건이있는 경우.
일단 빌더와 팩토리를 사용하여 객체를 빌드하기 시작하면 사전 및 사후 조건을 검증하고 코드의 클라이언트가 완전히 초기화 된 객체에만 액세스 할 수 있는지 확인하고 반만 만들어진 객체에는 액세스 할 수 있는지 확인 합니다. 유행하는 유창한 인터페이스에서 객체를 만들고 멋지게 보이게하십시오 .D
new EmailMessage()
.from("demo@guilhermechapiewski.com")
.to("destination@address.com")
.withSubject("Fluent Mail API")
.withBody("Demo message")
.send();
이것은 빌더를 사용하지 않기 때문에 분명히 당신의 경우는 아니지만 생성자의 작업을 줄이고 코드를 더 간단하게 만들기 위해 빌드 할 수있는 것과 매우 유사합니다.
생성자에 너무 많은 코드를 입력하면 다음과 같은 문제가 발생했습니다.
- 그 클래스의 다른 메서드에 대한 단위 테스트를 작성하는 것은 어려웠습니다. 생성자에서 많은 작업을 수행하고 싶었 기 때문에 유효한 많은 것을 설정하거나 최소한 모의 (DB, 파일 등)를 설정해야했습니다. 가장 간단한 단위 테스트를 위해.
- 생성자 자체에 대한 단위 테스트를 작성하기가 어려웠습니다. 어쨌든 다양한 책임을 가진 많은 코드를 하나의 블록에 넣는 것은 심지어 나쁜 생각입니다. ( 단일 책임 원칙 .)
- 이전의 이유로 그 클래스를 사용하기가 어려웠습니다. 예를 들어 생성자를 호출하는 순간에 모든 것이 필요했기 때문에 지연된 init 메서드를 구현하는 것을 완전히 방해했습니다. 좋아, lazy init 메서드를 생성자에 쓸 수 있습니다.
- Sooner or later I realized that it had sense to reuse some code parts which are placed in the constructor. Well, when I first wrote the constructor I also thought that those code parts will be used just there, forever.
- When I wanted to extend that class and insert some logic before or into the super constructor's logic, it simply didn't work because the first thing to do in the extended class' constructor is to invoke the super's one.
So yes, doing a lot in constructor is a bad idea in my opinion.
Generally I just put some field initializations into the constructor and make an init method to invoke when everybody is on board.
Having constructors and destructors in my opinion is good, but not to do too much work in them. Especially not file/database access unless its very very specific to the class. You want to keep your constructors/destructors light to keep your program feeling fluid. Sometimes like already you have come to a case where the constructor does essentially all the work. There is a way to make things lighter. The concept/paradigm is called lazy evaluation. The idea is to accept inputs and do nothing (e.g. in constructor) but make use of the inputs when you need a calculation requested.
Example: Lets say you have a class that reads a file, parses it and tells you info like the sum of all numbers in the file. You can do this all in the constructor. Using lazy evaluation you will merely open the file, and have a getTotalSum() function. When called it will do the parsing and give you the result. This way you can also have getBestFit() to get best fit line. Sometimes you dont want to get best fit and for some inputs you do. This way the user will not be waiting for the constructor to do the calculations before the user decides what to do.
또 다른 예 : 20 개의 이미지를로드하는 뷰가 있다고 가정 해 보겠습니다. 그러나 5 개만 표시되며 생성자는 표시 할 이미지 배열을 사용합니다. 생성자에서 모두로드 할 수 있지만 사용자 관점에서 보면 처음에는 느리게 느껴질 것입니다. 또는 1 개의 "로딩"사진을로드하고 한 번에 1 개의 이미지를로드 할 수 있습니다. 그리고 사용자가 스크롤 할 때 표시 / 필요에 따라 더 많은 이미지를로드합니다.
물론 한 가지 문제는 생성자 대신 나중에 잘못된 그림과 같은 오류를 발견한다는 것입니다. 입력을 어느 정도 사전 검증하기 위해 언제든지 간단한 검사를 수행 할 수 있습니다 (예 : 올바른 암호 확인).
참고 URL : https://stackoverflow.com/questions/7048515/is-doing-a-lot-in-constructors-bad
'IT박스' 카테고리의 다른 글
JAXB : 맵을 마샬링하는 방법 (0) | 2020.11.21 |
---|---|
SQL의 이중 콜론 (: :) 표기법 (0) | 2020.11.21 |
Jenkins는 사용자 당 작업보기를 제한합니다. (0) | 2020.11.21 |
Spinner가 텍스트를 줄 바꿈하지 않습니다. Android 버그입니까? (0) | 2020.11.21 |
ScrollView 내에서 TableView의 스크롤을 자연스럽게 만드는 방법 (0) | 2020.11.21 |