정적 팩토리 메소드 란 무엇입니까?


261

"정적 팩토리"방법은 무엇입니까?


다른 정적 팩토리 메소드는 종속성 주입을 사용하는 것입니다.
CMCDragonkai

1
@ThangPham Jason Owen의 대답은 팩토리 메소드 패턴에 대해 이야기하기 때문에 결함이 있습니다. 이는 실제로 이야기하는 정적 팩토리 메소드 패턴과는 매우 다릅니다. 따라서 실제 질문에 대답하는 것이 좋지만 현재 상태에서 받아 들일 수 있다고 생각하지 않습니다. 비 관련 패턴을 가져오고 두 패턴의 차이점에 대해 이미 매우 일반적인 혼란을 증가시키기 때문입니다.
Theodore Murdock

@ CMCDragonkai 의존성 주입과 정적 팩토리가 다르다고 생각합니다. 의존성을 주입하기 위해 의존성 주입의 경우에도 정적 팩토리가 필요할 수 있습니다.
Karan Khanna

답변:


126

데이터베이스 연결은 리소스를 많이 사용하기 때문에 데이터베이스 연결에 직접 액세스 할 수 없습니다. 따라서 우리 getDbConnection가 한계 미만이면 연결을 만드는 정적 팩토리 메소드 를 사용합니다. 그렇지 않으면 "예비"연결을 제공하려고하지만 예외가 없으면 실패합니다.

public class DbConnection{
   private static final int MAX_CONNS = 100;
   private static int totalConnections = 0;

   private static Set<DbConnection> availableConnections = new HashSet<DbConnection>();

   private DbConnection(){
     // ...
     totalConnections++;
   }

   public static DbConnection getDbConnection(){

     if(totalConnections < MAX_CONNS){
       return new DbConnection();

     }else if(availableConnections.size() > 0){
         DbConnection dbc = availableConnections.iterator().next();
         availableConnections.remove(dbc);
         return dbc;

     }else {
         throw new NoDbConnections();
     }
   }

   public static void returnDbConnection(DbConnection dbc){
     availableConnections.add(dbc);
     //...
   }
}

내가 올바르게 이해한다면, returnDbConnection (DbConnection db) 메소드에 availableConnections.add (db)를 추가 할 수 있습니까?
Haifeng Zhang

@ haifzhan, 그것은 실제로 완전하지는 않지만 괜찮습니다.
Matthew Flaschen

연결이 다시 반환되는 동안 @MatthewFlaschen도 totalConnections를 줄여야합니까?
롤링 스톤

@Sridhar, 아니, 그것은 순환하는 숫자가 아니라 존재하는 연결 수입니다 (MAX_CONNS 이상이 생성되지 않도록 추적 됨).
Matthew Flaschen

@MatthewFlaschen 당신은 DbConnection이 닫을 수 있다면 sonarcube가 blocker-bug로 잡힐 것입니다.
Awan Biru

477

정적 팩토리 메소드 패턴 캡슐 오브젝트 작성하는 방법이다. 팩토리 메소드가 없으면 클래스의 생성자를 직접 호출하면 됩니다 Foo x = new Foo(). 이 패턴을 사용하면 팩토리 메소드를 대신 호출합니다 Foo x = Foo.create(). 생성자는 private으로 표시되므로 클래스 내부를 제외하고는 호출 할 수 없으며 팩토리 메소드는 static먼저 객체가 없어도 호출 할 수 있도록 표시됩니다 .

이 패턴에는 몇 가지 장점이 있습니다. 하나는 팩토리가 많은 서브 클래스 (또는 인터페이스의 구현 자) 중에서 선택하여이를 리턴 할 수 있다는 것입니다. 이런 식으로 호출자는 잠재적으로 복잡한 클래스 계층을 알거나 이해할 필요없이 매개 변수를 통해 원하는 동작을 지정할 수 있습니다.

Matthew와 James가 지적했듯이 연결과 같은 제한된 리소스에 대한 액세스를 제어하는 ​​또 다른 이점이 있습니다. 이 방법 은 재사용 가능한 객체의 풀 을 구현하는 방법 입니다. 객체를 생성 , 사용 및 분해하는 대신 구성 및 파괴가 고가의 프로세스 인 경우 한 번 구축하여 재활용하는 것이 더 합리적입니다. 팩토리 메소드는 기존의 사용되지 않는 인스턴스화 된 오브젝트가있는 경우이를 리턴하거나 오브젝트 수가 임계 값보다 낮은 null경우이를 생성 하거나 예외를 발생 시키거나 상위 임계 값을 초과하면 리턴 할 수 있습니다.

Wikipedia의 기사에 따르면 여러 팩토리 메소드는 유사한 인수 유형에 대한 다른 해석을 허용합니다. 일반적으로 생성자는 클래스와 이름이 동일하므로 주어진 서명을 가진 생성자를 하나만 가질 수 있습니다 . 팩토리는 제한이 없으므로 동일한 인수 유형을 허용하는 두 가지 다른 메소드를 가질 수 있습니다.

Coordinate c = Coordinate.createFromCartesian(double x, double y)

Coordinate c = Coordinate.createFromPolar(double distance, double angle)

Rasmus가 지적한 것처럼 가독성을 향상시키는 데에도 사용할 수 있습니다.


32
정적 팩토리 메소드는 디자인 패턴의 팩토리 메소드 패턴과 다릅니다 [Gamma95, p. 107]. 이 항목에 설명 된 정적 팩토리 방법은 디자인 패턴과 직접적으로 동일하지 않습니다.
Josh Sunshine

이 답변에 대한 보상은 풀링 가능한 객체에 대한 참조입니다. 이것이 팩토리 메소드 패턴을 사용하는 방법입니다. "생성"은 풀에서 객체를 가져 오거나 풀이 비어있는 경우 새 인스턴스를 작성하여 나중에 재사용 할 수 있도록 풀로 리턴합니다.
MutantXenu 2016 년

2
이 게시물에서 "공장 방법 패턴"이라고 할 때마다 "정적 팩토리 방법 패턴"을 사용해야합니다. 잘못된 용어를 사용하고 있습니다. 또한, 당신이 말하는 패턴과 다른 패턴으로 Wikipedia 기사에 링크하고 있습니다. 팩토리 메소드 패턴은 팩토리 객체를 수락하여 단일 알고리즘으로 인터페이스 인스턴스를 생성하고 작업 할 수 있도록 팩토리 인터페이스를 구현하는 여러 팩토리 객체를 사용하므로 팩토리 인터페이스 패턴이라고합니다. 원하는 특정 하위 클래스를 생성 할 수 있습니다.
Theodore Murdock

8
@TheodoreMurdock에 동의합니다. 잘못된 용어를 사용하고 있습니다. 당신이 말하는 것은 Joshua Bloch의 "정적 팩토리 메소드"이지만 GoF의 "팩토리 메소드 패턴"이라는 용어를 사용합니다. 다른 사람들이 오해하지 않도록 편집하십시오.
emeraldhieu

1
생성자가 비공개 일 필요는 없습니다. 클래스는 공개 정적 팩토리 메소드와 생성자를 모두 제공 할 수 있습니다.
Kevin

171

노트! " 정적 팩토리 메소드팩토리 메소드 패턴 과 동일 하지 않습니다 "(c) Effective Java, Joshua Bloch.

팩토리 메소드 : "객체를 생성하기위한 인터페이스를 정의하지만 인터페이스를 구현하는 클래스가 인스턴스화 할 클래스를 결정하게합니다. 팩토리 메소드는 클래스가 서브 클래스에 대한 인스턴스화를 연기하게합니다"(c) GoF.

"정적 팩토리 메소드는 단순히 클래스의 인스턴스를 리턴하는 정적 메소드입니다." (c) 효과적인 Java, Joshua Bloch. 일반적으로이 메소드는 특정 클래스 내에 있습니다.

차이점:

정적 팩토리 메소드의 핵심 아이디어는 객체 생성을 제어하고 생성자에서 정적 메소드로 위임하는 것입니다. 생성 될 객체의 결정은 메소드 외부에서 만들어진 추상 팩토리 (일반적인 경우는 아니지만)에서와 같습니다. Factory Method의 핵심 (!) 아이디어는 Factory Method 내에서 생성 할 클래스의 인스턴스에 대한 결정을 위임하는 것입니다. 예를 들어 고전적인 Singleton 구현은 정적 팩토리 메소드의 특별한 경우입니다. 일반적으로 사용되는 정적 팩토리 메소드의 예 :

  • valueOf
  • getInstance
  • newInstance

new A ()와 A.newInstance ()의 차이점을 말해 줄 수 있습니까? A.newInstance 내에서 어떻게해야합니까?
Shen

69

정적 팩토리 메소드로 가독성을 향상시킬 수 있습니다.

비교

public class Foo{
  public Foo(boolean withBar){
    //...
  }
}

//...

// What exactly does this mean?
Foo foo = new Foo(true);
// You have to lookup the documentation to be sure.
// Even if you remember that the boolean has something to do with a Bar
// you might not remember whether it specified withBar or withoutBar.

public class Foo{
  public static Foo createWithBar(){
    //...
  }

  public static Foo createWithoutBar(){
    //...
  }
}

// ...

// This is much easier to read!
Foo foo = Foo.createWithBar();

따라서 예제를 구현하려고 시도했지만 이것이 어떻게 작동하는지 잘 모르겠습니다. 두 메소드 createWithBar 및 createWithoutBar는 Foo 클래스 내에서 2 개의 개인 생성자를 호출해야합니까?
Essej

@Baxtex : 예. 각각은 개인 생성자를 호출합니다. 아마 똑같은 것 : private Foo(boolean withBar){/*..*/} public static Foo createWithBar(){return new Foo(true);} public static Foo createWithoutBar(){return new Foo(false);}
Rasmus Faber

나는 이것이 "규모"가 아니라고 생각합니다. 세 개 이상의 매개 변수가있는 경우이 아이디어를 어떻게 사용하고 메소드의 좋은 이름을 만들 수 있습니까?
Dherik

@Dherik : 그렇다면 빌더 패턴을 대신 사용해야합니다. new FooBuilder (). withBar (). withoutFoo (). withBaz (). build ();
Rasmus Faber

21
  • 코드를 명확히 할 수있는 생성자와 달리 이름이 있습니다.
  • 호출 할 때마다 새 객체를 만들 필요가 없습니다. 필요한 경우 객체를 캐시하고 재사용 할 수 있습니다.
  • 반환 유형의 하위 유형을 반환 할 수 있습니다. 특히 구현 클래스가 호출자에게 알려지지 않은 개체를 반환 할 수 있습니다. 이것은 인터페이스를 정적 ​​팩토리 메소드의 리턴 유형으로 사용하는 많은 프레임 워크에서 매우 유용하고 널리 사용되는 기능입니다.

에서 http://www.javapractices.com/topic/TopicAction.do?Id=21


18

그것은 모두 유지 보수성으로 귀결됩니다. 이것을 넣는 가장 좋은 방법은 new키워드를 사용하여 객체를 만들 때마다 구현하는 코드를 연결하는 것입니다.

팩토리 패턴을 사용하면 객체를 만드는 방식과 객체를 만드는 방식을 분리 할 수 ​​있습니다. 생성자를 사용하여 모든 객체를 만들면 본질적으로 객체를 사용하는 코드를 해당 구현에 연결해야합니다. 객체를 사용하는 코드는 해당 객체에 "종속"됩니다. 이것은 표면 상 큰 문제가 아닌 것처럼 보일 수 있지만 객체가 변경 될 때 (생성자의 서명을 변경하거나 객체를 서브 클래 싱하는 것을 고려할 때) 돌아가서 모든 곳에서 사물을 다시 연결해야합니다.

오늘날 공장은 자체 유지하기가 약간 어려운 보일러 플레이트 코드가 많이 필요하기 때문에 Dependency Injection을 선호하여 크게 솔질되었습니다. Dependency Injection은 기본적으로 팩토리와 동일하지만 구성 또는 주석을 통해 객체를 선언적으로 연결하는 방법을 지정할 수 있습니다.


3
공장에서 구제 금융이 필요하다고 말하는가? ;)
John Farrell

4
ㅋ! 이 시대에 우리 모두 구제 금융을 사용할 수 있다고 생각합니다 ... :)
cwash

11

클래스의 생성자가 private 인 경우 클래스 외부에서 클래스의 객체를 만들 수 없습니다.

class Test{
 int x, y;
 private Test(){
  .......
  .......
  }
}

우리는 클래스 외부에서 객체를 만들 수 없습니다. 따라서 클래스 외부에서 x, y에 액세스 할 수 없습니다. 그렇다면이 수업의 용도는 무엇입니까?
답변 : FACTORY 메소드 는 다음과 같습니다 .
위 클래스에서 아래 메소드를 추가하십시오.

public static Test getObject(){
  return new Test();
}

이제 외부에서이 클래스에 대한 객체를 만들 수 있습니다. 길처럼 ...

Test t = Test.getObject();

따라서 개인 생성자를 실행하여 클래스의 객체를 반환하는 정적 메서드를 FACTORY 메서드 라고
합니다.


1
왜 "솔루션"이라고 부르나요? 개인 생성자를 선언하는 것은 "문제"가 아니라 디자인 패턴입니다.
Ojonugwa Jude Ochalifu

1
업데이트되었습니다. 어쨌든 감사합니다
Santhosh

안녕하세요 @Santhosh입니다. 개인 생성자를 공개하지 않는 이유는 무엇입니까? 서브 클래스를 만들고 싶지 않다면 괜찮지 만 개인 생성자를 만들지 않으려면 Static Factory Method공공 생성자보다 이점이 있습니까?
Shen

9

나는 내가 아는 것에 대해이 게시물에 약간의 빛을 추가 할 것이라고 생각했습니다. 우리는이 기술을 광범위하게 사용했습니다 recent android project. 대신 클래스를 인스턴스화하는 데 creating objects using new operator사용할 수도 있습니다 static method. 코드 목록 :

//instantiating a class using constructor
Vinoth vin = new Vinoth(); 

//instantiating the class using static method
Class Vinoth{
  private Vinoth(){
  }
  // factory method to instantiate the class
  public static Vinoth getInstance(){
    if(someCondition)
        return new Vinoth();
  }
}

정적 메소드는 조건부 객체 생성을 지원합니다 . 생성자를 호출 할 때마다 객체가 생성되지만 원하지 않을 수도 있습니다. 조건 만 확인한 다음 새 객체를 생성한다고 가정합니다. 조건이 충족되지 않으면 매번 Vinoth의 새 인스턴스를 생성하지 않습니다.

Effective Java 에서 가져온 또 다른 예 .

public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
}

이 메소드는 부울 프리미티브 값을 부울 오브젝트 참조로 변환합니다. 이 Boolean.valueOf(boolean)방법은 우리를 설명하며 결코 객체를 생성하지 않습니다. static factory methods반복에서 동일한 객체를 반환하는 기능을 invocations통해 클래스는 언제든지 존재하는 인스턴스를 엄격하게 제어 할 수 있습니다.

Static factory methods와 달리 모든 반환 유형 중 하나를 constructors반환 할 수 있습니다 . 이러한 유연성의 한 가지 적용은 API가 클래스를 공개하지 않고도 객체를 반환 할 수 있다는 것입니다. 이러한 방식으로 구현 클래스를 숨기면 매우 컴팩트 한 API가됩니다.objectsubtype

Calendar.getInstance ()는 그것은 로케일에 따라 생성하고, 위의 좋은 예입니다 BuddhistCalendar, JapaneseImperialCalendar또는 기본 하나 Georgian.

내가 생각할 수있는 또 다른 예는 Singleton pattern생성자를 비공개로 만들면 getInstance항상 사용할 수있는 인스턴스가 하나뿐이라는 고유 한 메서드를 만듭니다.

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

4

팩토리 메소드는 객체의 인스턴스화를 추상화하는 메소드입니다. 일반적으로 팩토리는 일부 인터페이스를 구현하는 클래스의 새 인스턴스가 필요하지만 구현 클래스를 모르는 경우 유용합니다.

이것은 관련 클래스의 계층 구조로 작업 할 때 유용합니다. 이에 대한 좋은 예는 GUI 툴킷입니다. 각 위젯의 구체적인 구현을 위해 생성자에 대한 호출을 단순히 하드 코딩 할 수 있지만 한 툴킷을 다른 툴킷으로 바꾸고 싶다면 변경해야 할 곳이 많이 있습니다. 팩토리를 사용하면 변경해야 할 코드의 양이 줄어 듭니다.


팩토리가 인터페이스 유형을 반환한다고 가정하고 처리하는 구체적인 클래스는 아닙니다.
Bill Lynch

1
이 답변은 정적 팩토리 메소드가 아닌 팩토리 메소드 디자인 패턴 에 관한 것입니다 . 정적 팩토리 메소드는 단순히 클래스의 인스턴스를 리턴하는 공용 정적 메소드입니다. 자세한 내용은 효과적인 Java의 2 장을 참조하십시오.
Josh Sunshine

4

정적 팩토리의 장점 중 하나는 API가 클래스를 공개하지 않고 객체를 반환 할 수 있다는 것입니다. 이는 매우 컴팩트 한 API로 이어집니다. 자바에서 이것은 32 개의 클래스를 숨기고 Collection API를 매우 컴팩트하게 만드는 Collections 클래스에 의해 달성됩니다.


2

정적 팩토리 메소드는 하나의 단일 인스턴스 만 사용할 구체적 클래스를 리턴하도록하려면 좋습니다.

예를 들어, 데이터베이스 연결 클래스에서 하나의 클래스 만 데이터베이스 연결을 작성하도록 할 수 있으므로 Mysql에서 Oracle로 전환하기로 결정한 경우 하나의 클래스에서 논리를 변경할 수 있으며 나머지 응용 프로그램은 새로운 연결을 사용하십시오.

데이터베이스 풀링을 구현하려는 경우 나머지 응용 프로그램에 영향을주지 않고 수행됩니다.

출고시 변경 될 수있는 나머지 응용 프로그램을 보호합니다. 이것이 목적입니다.

정적 인 이유는 제한된 리소스 (소켓 연결 또는 파일 핸들 수)를 추적하려는 경우이 클래스는 얼마나 많은 시간이 경과 및 반환되었는지 추적 할 수 있기 때문에 제한된 자원.


2

개인 생성자가있는 정적 팩토리 메소드의 장점 중 하나 (인스턴스가 외부에서 작성되지 않도록하기 위해 외부 클래스에 대해 오브젝트 작성이 제한되어야 함)는 인스턴스 제어 클래스를 작성할 수 있다는 것 입니다. 그리고 예를 제어 클래스는 두 개의 동일한 별개의 인스턴스 (존재하지 않는 것을 보장 a.equals (b)는 == B 전용 경우 경우 ) 프로그램 중 그 방법을 실행하는 당신이 개체의 평등을 확인하실 수 있습니다 == 대신 운영자 동등의 방법 효과적인 java에 따르면.

정적 팩토리 메소드가 반복 호출에서 동일한 오브젝트를 리턴하는 기능을 통해 클래스는 언제든지 존재하는 인스턴스를 엄격하게 제어 할 수 있습니다. 이를 수행하는 클래스는 인스턴스 제어라고합니다. 인스턴스 제어 클래스를 작성해야하는 몇 가지 이유가 있습니다. 인스턴스 제어를 통해 클래스는 단일 항목 (항목 3)이거나 인스턴스화 할 수 없음 (항목 4)임을 보장 할 수 있습니다. 또한 불변 클래스 (항목 15)는 두 개의 동일한 인스턴스가 존재하지 않음을 보장 할 수 있습니다. a. == b 인 경우에만 a.equals (b). 클래스가 이러한 보증을하는 경우, 클라이언트는 equals (Object) 메소드 대신 == 연산자를 사용하여 성능을 향상시킬 수 있습니다. 열거 형 (항목 30)은이 보증을 제공합니다.

효과적인 Java에서 Joshua Bloch (항목 1, 6 페이지)


1

공전

키워드 'static'으로 선언 된 멤버입니다.

공장 방법

새로운 객체를 생성하고 반환하는 메소드.

자바로

프로그래밍 언어는 '정적'의 의미와 관련이 있지만 '공장'의 정의와는 관련이 없습니다.


@Victor Responses에는 인수가 포함될 필요가 없습니다. 사실이면 충분합니다. 논쟁의 여지가있는 경우에는 논쟁이나 인용으로 뒷받침 될 수 있습니다. 나는 여기에 논쟁의 여지가있는 것을 모른다.
Lorne의 후작

나는 대답이 너무 짧았고 첫 번째, 두 번째 및 세 번째 모습에서 잘 이해하지 못했기 때문에 어쨌든 다시 물어 봐야하는 OP에 달려 있습니다. 신경 쓰지 마라, 나는 내 마음을 바꿨고 downvote를 제거하려고 노력했지만 할 수는 없다.
빅터

@Victor '너무 짧게'정의하십시오. 때로는 실제 대답은 '예', '아니오', '없음'또는 '둘 다'입니다. '너무 짧다'는 전적으로 주관적입니다. 여전히 원래 의견을 이해하지 못합니다. 정의를 지원하기 위해 인수가 필요하지 않습니다. 정의에 의해. 내 답변을 수정하여 이제 downvote를 제거 할 수있게되었습니다.
Lorne의 후작

물론 주관적입니다 ... 모든 stackoverflow 답변은 주관적입니다. 사실 그들은 저자 수준의 지식과 관련이 있으며 대답 할 의지가 있습니다. 나는 당신의 대답을 이해하지 못했을 것입니다. 아마 짧았습니다.
Victor

@ 빅터 쓰레기. 사실의 문제는 주관적이지 않으며, 사실이나 공리에서 추론 적으로 도출 될 수있는 문제도 아닙니다. 반면에 '너무 짧다'는 주관적이며 이미 언급했습니다. 이 답변은 본질적으로 당신이 코멘트하지 않은 this 와 동일 합니다. 귀하의 의견은 모호합니다.
Lorne의 후작

1

Java 구현에는 java.util.Arraysjava.util.Collections 유틸리티 클래스가 포함되어 있습니다. 둘 다 정적 팩토리 메소드 , 예제 및 사용 방법을 포함합니다.

또한 java.lang.String 클래스에는 정적 팩토리 메소드가 있습니다 .

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