생성자 vs 팩토리 메소드
클래스를 모델링 할 때 선호하는 초기화 방법은 무엇입니까?
- 생성자 또는
- 공장 방법
그리고 그것들 중 하나를 사용하기 위해 고려해야 할 것은 무엇입니까?
어떤 상황에서는 객체를 생성 할 수없는 경우 null을 반환하는 팩토리 메소드를 선호합니다. 이것은 코드를 깔끔하게 만듭니다. 생성자에서 예외를 던지는 것과는 달리 대체 조치를 수행하기 전에 리턴 된 값이 널이 아닌지 간단히 확인할 수 있습니다. (나는 개인적으로 예외를 좋아하지 않는다)
id 값을 기대하는 클래스에 생성자가 있다고 가정 해보십시오. 생성자는이 값을 사용하여 데이터베이스에서 클래스를 채 웁니다. 지정된 ID를 가진 레코드가 존재하지 않는 경우 생성자는 RecordNotFoundException을 발생시킵니다. 이 경우 try..catch 블록 내에 이러한 모든 클래스의 구성을 포함해야합니다.
이와 대조적으로 레코드를 찾을 수 없으면 null을 반환하는 클래스에 정적 팩토리 메서드를 사용할 수 있습니다.
이 경우 생성자 또는 팩토리 방법 중 어떤 방법이 더 낫습니까?
디자인 패턴의 108 페이지부터 : 감마, Helm, Johnson 및 Vlisside의 재사용 가능한 객체 지향 소프트웨어 요소.
다음의 경우 팩토리 메소드 패턴을 사용하십시오.
- 클래스는 생성해야하는 객체의 클래스를 예상 할 수 없습니다
- 클래스는 서브 클래스가 생성하는 객체를 지정하기를 원합니다.
- 클래스는 여러 헬퍼 서브 클래스 중 하나에 책임을 위임하며, 어떤 헬퍼 서브 클래스가 위임인지에 대한 지식을 현지화하려고합니다.
그들이 무엇인지, 왜 우리에게 있는지 물어보십시오. 둘 다 객체의 인스턴스를 만들기 위해 존재합니다.
ElementarySchool school = new ElementarySchool();
ElementarySchool school = SchoolFactory.Construct(); // new ElementarySchool() inside
지금까지 아무런 차이가 없습니다. 이제 다양한 학교 유형이 있으며 ElementarySchool 사용에서 HighSchool (ElementarySchool에서 파생되거나 ElementarySchool과 동일한 인터페이스 ISchool을 구현 함)로 전환하려고합니다. 코드 변경은 다음과 같습니다.
HighSchool school = new HighSchool();
HighSchool school = SchoolFactory.Construct(); // new HighSchool() inside
인터페이스의 경우 :
ISchool school = new HighSchool();
ISchool school = SchoolFactory.Construct(); // new HighSchool() inside
이제이 코드가 여러 곳에있는 경우 팩토리 메소드를 변경하면 완료되었으므로 팩토리 메소드를 사용하는 것이 매우 저렴하다는 것을 알 수 있습니다 (두 번째 예제를 인터페이스와 함께 사용하는 경우).
이것이 주요 차이점과 장점입니다. 복잡한 클래스 계층을 다루기 시작하고 이러한 계층에서 클래스 인스턴스를 동적으로 만들려면 다음 코드를 얻습니다. 팩토리 메소드는 인스턴스화 할 구체적인 인스턴스를 메소드에 알려주는 매개 변수를 취할 수 있습니다. MyStudent 클래스가 있고 학생이 해당 학교의 구성원이되도록 해당 ISchool 개체를 인스턴스화해야한다고 가정 해 봅시다.
ISchool school = SchoolFactory.ConstructForStudent(myStudent);
이제 다른 IStudent 개체에 대해 인스턴스화 할 ISchool 개체를 결정하는 비즈니스 논리를 포함하는 앱의 한 위치가 있습니다.
따라서 간단한 클래스 (값 객체 등)의 경우 생성자는 훌륭하지만 (응용 프로그램을 너무 많이 엔지니어링하고 싶지는 않습니다) 복잡한 클래스 계층의 경우 팩토리 메소드가 선호되는 방법입니다.
이런 식으로 당신은 "구현이 아니라 인터페이스에 대한 프로그램"이라는 네 권의 책 에서 첫 번째 디자인 원칙을 따릅니다 .
효과적인 Java 2 항목 1 : 생성자 대신 정적 팩토리 메소드를 고려하십시오 ( 액세스 할 수있는 경우) .
정적 팩토리 메소드의 장점 :
- 그들은 이름이 있습니다.
- 호출 할 때마다 새 객체를 만들 필요는 없습니다.
- 반환 유형의 하위 유형의 객체를 반환 할 수 있습니다.
- 매개 변수화 된 유형 인스턴스 작성의 세부 사항을 줄입니다.
정적 팩토리 메소드 단점 :
- 정적 팩토리 메소드 만 제공하는 경우 공용 또는 보호 생성자가없는 클래스는 서브 클래스 화 될 수 없습니다.
- 다른 정적 메소드와 쉽게 구별 할 수 없습니다
기본적으로 생성자와 이해하기가 더 간단하므로 생성자가 선호됩니다. 그러나 클라이언트 코드에서 이해할 수 있듯이 객체의 구성 요소를 의미 적 의미에서 분리해야하는 경우 팩토리를 사용하는 것이 좋습니다.
생성자와 팩토리의 차이점은 변수 및 변수에 대한 포인터와 유사합니다. 또 다른 수준의 간접 지향이 있는데, 이는 단점입니다. 그러나 또 다른 수준의 유연성이 있기 때문에 이점이 있습니다. 따라서 선택하는 동안이 비용 대 이익 분석을 수행하는 것이 좋습니다.
"Effective Java", 2nd ed., Item 1 : 인용 : 생성자 대신 정적 팩토리 메소드 고려, p. 5 :
" 정적 팩토리 방법은 디자인 패턴의 팩토리 방법 패턴 [Gamma95, p. 107]과 동일하지 않습니다.이 항목에서 설명하는 정적 팩토리 방법은 디자인 패턴과 직접적으로 동일하지 않습니다."
생성자를 사용하여 수행 할 수없는 방식으로 객체 생성에 대한 추가 제어가 필요한 경우에만 팩토리를 사용하십시오.
예를 들어 공장은 캐싱 가능성이 있습니다.
팩토리를 사용하는 다른 방법은 구성하려는 유형을 모르는 시나리오입니다. 종종 플러그인 팩토리 시나리오에서 이러한 유형의 사용법을 볼 수 있는데, 여기서 각 플러그인은 기본 클래스에서 파생되거나 일종의 인터페이스를 구현해야합니다. 팩토리는 기본 클래스에서 파생되거나 인터페이스를 구현하는 클래스 인스턴스를 작성합니다.
In addition to "effective java" (as mentioned in another answer), another classic book also suggests:
Prefer static factory methods (with names that describe the arguments) to overloaded constructors.
Eg. don't write
Complex complex = new Complex(23.0);
but instead write
Complex complex = Complex.fromRealNumber(23.0);
The book goes as far as to suggest making the Complex(float)
constructor private, to force the user to call the static factory method.
A concrete example from a CAD/CAM application.
A cutting path would be made by using a constructor. It is a series of lines and arcs defining a path to cut. While the series of lines and arcs can be different and have different coordinates it easily handled by passing a list into a constructor.
A shape would be would be made by using a factory. Because while there is a shape class each shape would be setup differently depending on what type of shape it is. We don't know what shape we are going to be initializing until the user makes a selection.
Say, I have a constructor on a class which expects an id value. The constructor uses this value to populate the class from the database.
This process should definitely be outside a constructor.
Constructor should not access database.
The task and the reason for a constructor is to initialize data members and to establish class invariant using values passed into constructor.
For everything else a better approach is to use static factory method or in more complex cases a separate factory or builder class.
Some constructor guide lines from Microsoft:
Do minimal work in the constructor. Constructors should not do much work other than to capture the constructor parameters. The cost of any other processing should be delayed until required.
And
Consider using a static factory method instead of a constructor if the semantics of the desired operation do not map directly to the construction of a new instance.
Sometimes you have to check/calculate some values/conditions while creating an object. And if it can throw an Exception - constructro is very bad way. So you need to do something like this:
var value = new Instance(1, 2).init()
public function init() {
try {
doSome()
}
catch (e) {
soAnotherSome()
}
}
Where all additional calculations are in init(). But only you as developer realy know about this init(). And of course, after months you just forget about it. But if you have a factory - just do all you need in one method with hiding this init() from direct call - so no problems. With this approach is no problems with falling on creation and memory leaking.
Someone told you about caching. It's good. But you also have to remember about Flyweight pattern which is nice to use with Factory way.
참고URL : https://stackoverflow.com/questions/628950/constructors-vs-factory-methods
'Programing' 카테고리의 다른 글
Windows에 GREP과 같은 패턴 일치 유틸리티가 있습니까? (0) | 2020.05.26 |
---|---|
루비에서 CSV로 출력 배열 (0) | 2020.05.26 |
ggplot에서 모든 x 축 레이블 제거 (0) | 2020.05.26 |
PowerShell의 삼항 연산자 (0) | 2020.05.26 |
Java 클래스가로드 된 위치 찾기 (0) | 2020.05.26 |