생성자 함수 대 팩토리 함수


150

누군가 Javascript에서 생성자 함수와 팩토리 함수의 차이점을 명확히 할 수 있습니까?

다른 하나 대신 사용할 때?

답변:


149

기본적인 차이점은 생성자 함수가 new키워드 와 함께 사용된다는 것입니다 (JavaScript는 새 객체를 자동으로 생성 this하고 함수 내에서 해당 객체로 설정 하여 객체를 반환합니다)

var objFromConstructor = new ConstructorFunction();

팩토리 함수는 "일반적인"함수와 같습니다.

var objFromFactory = factoryFunction();

그러나 "공장"으로 간주 되려면 일부 객체의 새 인스턴스를 반환해야합니다. 부울 또는 무언가를 반환 한 경우 "공장"함수라고 부르지 않습니다. 이와 같이 자동으로 발생하지는 않지만 new경우에 따라 더 많은 유연성을 허용합니다.

실제로 간단한 예에서 위에서 참조한 함수는 다음과 같습니다.

function ConstructorFunction() {
   this.someProp1 = "1";
   this.someProp2 = "2";
}
ConstructorFunction.prototype.someMethod = function() { /* whatever */ };

function factoryFunction() {
   var obj = {
      someProp1 : "1",
      someProp2 : "2",
      someMethod: function() { /* whatever */ }
   };
   // other code to manipulate obj in some way here
   return obj;
}

물론 간단한 예제보다 팩토리 기능을 훨씬 더 복잡하게 만들 수 있습니다.

팩토리 함수의 한 가지 장점은 리턴 될 오브젝트가 일부 매개 변수에 따라 여러 유형이 될 수 있다는 것입니다.


14
"(편집 : 새 기능이 없으면 기능은 계속 실행되지만 예상 한대로 작동하지 않기 때문에 문제가 될 수 있습니다." "new"를 사용하여 팩토리 함수를 호출하거나 "this"키워드를 사용하여 인스턴스에 지정하려고하는 경우에만 문제가됩니다. 그렇지 않으면 단순히 임의의 새 객체를 만들어 반환하면됩니다. 상용구가 적고 API에 인스턴스화 세부 정보가 유출되지 않고 문제가 없으며 다른 유연하고 유연한 방법으로 작업 할 수 있습니다.
Eric Elliott

6
두 경우 (생성자 기능 대 팩토리 기능)에 대한 예제가 일치해야한다고 지적하고 싶습니다. 팩토리 함수에 대한 예제는 팩토리가 someMethod반환 한 객체를 포함하지 않으며 약간 안개가납니다. 팩토리 함수 내에서 하나만 수행하면 var obj = { ... , someMethod: function() {}, ... }반환 된 각 객체가 다른 사본을 보유하게되어 someMethod원치 않을 수도 있습니다. 그것이 공장 기능을 사용 new하고 prototype내부에서 도움이 될 곳입니다.
Bharat Khatri

3
이미 언급했듯이 일부 사람들 new은 생성자 함수와 함께 사용 하는 것을 잊어 버린 버그를 남기려고하지 않기 때문에 팩토리 함수를 사용하려고 시도 합니다. 필자는 생성자를 팩토리 함수 예제 로 바꾸는 방법 을 볼 필요가 있는 곳이라고 생각했으며 그 곳에서 예제의 일관성이 필요하다고 생각했습니다. 어쨌든 대답은 충분히 유익합니다. 이것은 내가 어떤 방식 으로든 대답의 질을 낮추는 것이 아니라 내가 높이고 싶은 요점이었습니다.
Bharat Khatri

4
나에게 팩토리 함수의 가장 큰 장점은 더 나은 캡슐화데이터 숨기기 를 얻는 것이 일부 응용 프로그램에서 유용하다는 것입니다. 모든 인스턴스 속성과 메소드를 공개하고 사용자가 쉽게 수정할 수있게 만드는 데 문제가 없다면 다른 사람들처럼 "새"키워드를 싫어하지 않는 한 생성자 함수가 더 적합하다고 생각합니다.
devius

1
@Federico-팩토리 메소드는 평범한 객체를 반환 할 필요가 없습니다. 그들은 사용할 수 있습니다 new내부적으로 사용하거나 Object.create()특정 프로토 타입과 객체를 만들 수 있습니다.
nnnnnn

110

생성자 사용의 이점

  • 대부분의 책은 생성자를 사용하고 new

  • this 새로운 객체를 참조

  • 어떤 사람들은 var myFoo = new Foo();읽는 방식을 좋아합니다 .

단점

  • 인스턴스화에 대한 세부 사항이 new요구 사항을 통해 호출 API로 유출 되므로 모든 호출자가 생성자 구현에 밀접하게 연결됩니다. 공장의 추가 유연성이 필요한 경우 모든 발신자를 리팩토링해야합니다 (규칙이 아닌 예외적 인 경우 임).

  • 잊어 버리는 new것은 일반적인 버그이므로 생성자가 올바르게 호출되도록 상용구 검사를 추가하는 것이 좋습니다 ( if (!(this instanceof Foo)) { return new Foo() }). 편집 : ES6 (ES2015)를 잊지 수 없기 때문에 newclass생성자, 또는 생성자 오류가 발생합니다.

  • instanceof검사 를 수행하면 필요한지 여부에 대한 모호성이 남습니다 new. 내 의견으로는,해서는 안됩니다. new요구 사항을 효과적으로 단축했습니다. 즉, 단점 1을 지울 수 있습니다. 그러나 추가 상용구, 대문자 및 덜 유연한 컨텍스트 와 함께 name 이외의 팩토리 함수가this 있습니다.

생성자는 개방 / 폐쇄 원칙을 어기다

그러나 나의 주요 관심사는 그것이 공개 / 폐쇄 원칙을 위반한다는 것입니다. 생성자 내보내기를 시작하고 사용자가 생성자를 사용하기 시작한 다음 대신 팩토리의 유연성이 필요하다는 것을 깨달았습니다 (예 : 구현을 전환하여 객체 풀을 사용하거나 실행 컨텍스트를 통해 인스턴스화하거나 프로토 타입 OO를 사용하여 더 많은 상속 유연성을 갖습니다).

그래도 당신은 붙어 있습니다. 로 생성자를 호출하는 모든 코드를 중단하지 않고는 변경할 수 없습니다 new. 예를 들어 성능 향상을 위해 객체 풀을 사용하도록 전환 할 수 없습니다.

또한 생성자를 사용하면 instanceof실행 컨텍스트에서 작동하지 않는 속임수 를 제공 하며 생성자 프로토 타입이 스왑 아웃되면 작동하지 않습니다. this생성자에서 반환을 시작한 다음 임의의 개체 내보내기로 전환하면 생성자에서 팩토리와 같은 동작을 수행해야합니다.

공장 사용의 이점

  • 코드가 적으며 보일러 플레이트가 필요하지 않습니다.

  • 임의의 객체를 반환하고 임의의 프로토 타입을 사용할 수 있으므로 동일한 API를 구현하는 다양한 유형의 객체를 생성 할 수있는 유연성이 향상됩니다. 예를 들어 HTML5 및 플래시 플레이어의 인스턴스를 만들 수있는 미디어 플레이어 또는 DOM 이벤트 또는 웹 소켓 이벤트를 생성 할 수있는 이벤트 라이브러리가 있습니다. 또한 팩토리는 실행 컨텍스트에서 객체를 인스턴스화하고 객체 풀을 활용하며보다 유연한 프로토 타입 상속 모델을 허용 할 수 있습니다.

  • 팩토리에서 생성자로 변환 할 필요가 없으므로 리팩토링은 문제가되지 않습니다.

  • 사용에 대한 모호함이 없습니다 new. 하지마 (이것은 this나쁘게 행동 할 것입니다 , 다음 요점을보십시오).

  • this그것은 보통 때와 같이 동작합니다 - 당신이 (예를 들어 부모 개체에 액세스하는 데 사용할 수 있도록, 내부 player.create(), this을 의미한다 player. 단지 다른 모든 메소드 호출처럼, call그리고 apply또한 재 할당 this, 당신은 부모 개체에 프로토 타입을 저장하는 경우 것으로 예상한다. 동적으로 기능을 교환하고 객체 인스턴스화를 위해 매우 유연한 다형성을 가능하게하는 좋은 방법이 될 수 있습니다.

  • 자본화 여부에 대한 모호함이 없습니다. 하지마 보푸라기 도구가 불평을하고 나서 사용하려고한다면 유혹을 풀고 new위에서 설명한 이점을 취소 할 수 있습니다.

  • 어떤 사람들은 길을 좋아 var myFoo = foo();하거나 var myFoo = foo.create();읽습니다.

단점

  • new예상대로 작동하지 않습니다 (위 참조). 해결책 : 사용하지 마십시오.

  • this새 객체를 참조하지 않습니다 (대신 생성자가 도트 표기법 또는 대괄호 표기법으로 호출 된 경우 (예 : foo.bar ()- this참조 foo-다른 모든 JavaScript 메서드와 마찬가지로-이점 참조)).


2
어떤 의미에서 생성자가 호출자를 구현에 밀접하게 연결한다는 것을 의미합니까? 생성자 인수에 관한 한, 그것들은 팩토리 함수로 전달되어 그것들을 사용하고 적절한 생성자를 호출하기 위해 팩토리 함수로 전달되어야합니다.
Bharat Khatri

4
Open / Closed 위반과 관련하여 : 이것이 의존성 주입에 관한 것이 아닙니까? A에 B가 필요한 경우 A는 새로운 B () 또는 A BFactory.create ()를 호출합니다. 둘 다 커플 링을 도입합니다. 반면에 컴포지션 루트에서 A에 B의 인스턴스를 제공하면 A는 B의 인스턴스화 방법에 대해 전혀 알 필요가 없습니다. 저는 생성자와 팩토리가 모두 사용한다고 생각합니다. 생성자는 간단한 인스턴스화를위한 것이고보다 복잡한 인스턴스화를위한 팩토리입니다. 그러나 두 경우 모두 의존성을 주입하는 것이 현명합니다.
Stefan Billiet

1
DI는 상태, 구성, 도메인 개체 등을 주입하는 데 적합합니다. 다른 모든 것에는 과잉입니다.
Eric Elliott

1
문제는 요구 new가 개방 / 폐쇄 원칙을 위반한다는 것입니다. 이 의견이 허용하는 것보다 훨씬 더 큰 토론 은 medium.com/javascript-scene/… 을 참조하십시오 .
Eric Elliott

3
어떤 함수라도 JavaScript로 새로운 객체를 반환 할 수 있고, new키워드 없이도 많은 객체가 그렇게하기 때문에 new키워드가 실제로 추가적인 가독성을 제공 한다고 생각하지 않습니다 . IMO, 발신자가 더 많이 입력 할 수 있도록 농구대를 뛰어 넘는 것은 어리석은 것처럼 보입니다.
Eric Elliott

39

생성자는 호출 한 클래스의 인스턴스를 반환합니다. 팩토리 함수는 무엇이든 반환 할 수 있습니다. 임의의 값을 반환해야하거나 클래스에 큰 설정 프로세스가있는 경우 팩토리 함수를 사용합니다.


5

생성자 함수 예제

function User(name) {
  this.name = name;
  this.isAdmin = false;
}

let user = new User("Jack");
  • new프로토 타입 화 된 오브젝트를 작성하고 작성된 오브젝트를 값 으로 User.prototype호출 합니다.Userthis

  • new 피연산자의 인수 표현식을 선택적으로 처리합니다.

         let user = new User;

    원인이 new전화를 User인수없이.

  • new그것이 생성 된 객체를 반환 생성자가 객체의 값을 반환하지 않는 대신에 반환됩니다. 대부분의 경우 무시할 수있는 엣지 케이스입니다.

장점과 단점

생성자 함수로 생성 된 객체는 생성자의 prototype속성 에서 속성을 상속하고 생성자 함수 에서 instanceOf연산자를 사용하여 true를 반환합니다 .

prototype이미 생성자를 사용한 후 생성자의 속성 값을 동적으로 변경하면 위의 동작이 실패 할 수 있습니다 . 그렇게하는 것은 드문 일이며 생성자를 class키워드를 사용하여 만든 경우에는 변경할 수 없습니다 .

extends키워드를 사용하여 생성자 함수를 확장 할 수 있습니다 .

생성자 함수 null는 오류 값으로 반환 할 수 없습니다 . 객체 데이터 유형이 아니므로에 의해 무시됩니다 new.

팩토리 함수 예제

function User(name, age) {
  return {
    name,
    age,
  }
};

let user = User("Tom", 23);

여기서 팩토리 함수는없이 호출됩니다 new. 이 함수는 인수 및 반환하는 객체 유형 인 경우 직접 또는 간접적 인 사용을 전적으로 책임집니다. 이 예에서는 인수에서 일부 속성이 설정된 간단한 [Object object]를 반환합니다.

장점과 단점

호출자로부터 객체 생성의 구현 복잡성을 쉽게 숨 깁니다. 브라우저에서 기본 코드 기능에 특히 유용합니다.

팩토리 함수는 항상 같은 유형의 객체를 반환 할 필요는 없으며 null오류 표시기로 반환 될 수도 있습니다.

간단한 경우, 팩토리 기능은 구조와 의미가 단순 할 수 있습니다.

반환 된 객체는 일반적으로 팩토리 함수의 prototype속성 에서 상속되지 않으며에서 반환 false됩니다 instanceOf factoryFunction.

extends확장 객체는 팩토리 함수가 사용하는 생성자 prototypeprototype속성이 아닌 팩토리 함수 속성 에서 상속되므로 키워드를 사용하여 팩토리 함수를 안전하게 확장 할 수 없습니다 .


1
이것은 같은 주제에 대해이 질문 에 대한 답변으로 게시 된 늦은 답변입니다 .
traktor53

"null"뿐만 아니라 "new"도 생성자 함수에서 반환 한 모든 기본 데이터 형식을 무시합니다.
Vishal

2

공장은 "항상"더 좋습니다. 객체 지향 언어를 사용할 때

  1. 계약 결정 (방법 및 수행 방법)
  2. 해당 메소드를 노출하는 인터페이스를 작성하십시오 (자바 스크립트에는 인터페이스가 없으므로 구현을 확인하는 방법이 필요합니다)
  3. 필요한 각 인터페이스의 구현을 리턴하는 팩토리를 작성하십시오.

구현 (신규로 생성 된 실제 객체)은 팩토리 사용자 / 소비자에게 노출되지 않습니다. 이것은 팩토리 개발자가 계약을 위반하지 않는 한 새로운 구현을 확장하고 만들 수 있음을 의미합니다. 그리고 팩토리 소비자는 코드를 변경하지 않고도 새로운 API의 이점을 누릴 수 있습니다 ... 그들이 새로운 것을 사용하고 "새로운"구현이 나오면 "새로운"구현을 사용하기 위해 "새로운"을 사용하는 모든 행을 변경해야합니다 ... 공장에서는 코드가 변경되지 않습니다 ...

스프링 프레임 워크는이 아이디어를 기반으로 완전히 구축되었습니다.


공장은 모든 라인을 변경해야하는이 문제를 어떻게 해결합니까?
코드 이름 Jack

0

팩토리는 추상화의 계층이며, 모든 추상화와 마찬가지로 복잡합니다. 팩토리 기반 API를 만나면 주어진 API에 대한 팩토리가 무엇인지 파악하는 것이 API 소비자에게 어려울 수 있습니다. 생성자와 함께 검색 가능성은 간단합니다.

ctor와 공장을 결정할 때 이점이 복잡성을 정당화 할 것인지 결정해야합니다.

Javascript 생성자는 이것 이외의 것을 반환하거나 정의되지 않은 임의의 팩토리가 될 수 있습니다. 따라서 js에서 발견 가능한 API 및 객체 풀링 / 캐싱이라는 두 가지 이점을 모두 누릴 수 있습니다.


5
JavaScript에서는 생성자의 사용 비용이 팩토리 사용 비용보다 높습니다. JS의 함수는 새로운 객체를 반환 할 수 있기 때문입니다. 생성자는 Requiring new, Altering behavior of this, Return value 변경, prototype ref 연결, Enabling instanceof(이 목적으로 사용해서는 안 됨)으로 복잡성을 추가 합니다. 표면 상으로, 이들 모두는 "기능"입니다. 실제로는 코드 품질을 떨어 뜨립니다.
Eric Elliott

0

차이점을 위해 Eric Elliott는 명확하게 설명했습니다.

그러나 두 번째 질문은 다음과 같습니다.

다른 하나 대신 사용할 때?

객체 지향 배경에서 오는 경우 생성자 함수가 더 자연스럽게 보입니다. 이렇게하면 new키워드 를 사용하는 것을 잊지 않아야 합니다.

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