Java / Java EE에서와 유사한 복잡성을 관리하면서 다른 인기있는 언어가 팩토리 패턴을 사용하지 않아도되는 방법은 무엇입니까? 의 사용 사례가 있다면

팩토리 패턴 (또는 적어도의 사용 FactoryFactory..)은 here 과 같은 많은 농담의 엉덩이입니다 .

RequestProcessorFactoryFactory.RequestProcessorFactory 와 같이 상세하고 “창의적인”이름을 갖는 것 외에도 Java / C ++로 프로그래밍해야하고 Abstract_factory_pattern 의 사용 사례가 있다면 팩토리 패턴에 근본적으로 잘못된 것이 있습니까?

다른 유명한 언어 (예 : Ruby 또는 Scala )가 유사한 복잡성을 관리하면서 언어 를 사용하지 않아도되는 방법은 무엇입니까?

내가 묻는 이유는 주로 Java / Java EE 생태계와 관련하여 언급 된 공장에 대한 비판을 볼 수 있지만 다른 언어 / 프레임 워크가 어떻게이를 해결하는지 설명하지는 않기 때문입니다.



답변

귀하의 질문은 “Java”로 태그가 지정됩니다. Factory 패턴이 왜 조롱되는지 묻는 것은 놀라운 일이 아닙니다. Java 자체에는 해당 패턴에 대한 패키지 악용이 포함되어 있습니다.

예를 들어 파일에서 XML 문서를로드하고 XPath 쿼리를 실행하십시오. 팩토리와 빌더를 설정하려면 10 줄의 코드가 필요합니다.

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder(); 

Document xmlDocument = builder.parse(new FileInputStream("c:\\employees.xml"));
XPath xPath =  XPathFactory.newInstance().newXPath();

xPath.compile(expression).evaluate(xmlDocument);

이 API를 디자인하는 사람들이 개발자로 일한 적이 있는지 또는 책을 읽고 물건을 던지는 지 궁금합니다. 나는 그들이 파서를 직접 작성하고 싶지 않고 다른 사람들에게 과제를 맡기고 싶지만 여전히 추한 구현을 만든다는 것을 이해합니다.

대안이 무엇인지 묻기 때문에 C #으로 XML 파일을로드하는 중입니다.

XDocument xml = XDocument.Load("c:\\employees.xml");
var nodes = xml.XPathSelectElements(expression);

새로 부화 한 Java 개발자들이 Factory의 광기를보고 의심 스럽다고 생각합니다. Java를 직접 만든 천재들이 그렇게 많이 사용한다면 말입니다.

공장 및 기타 패턴은 각각 특정 작업에 적합한 도구입니다. 작업에 적용하면 적합하지 않은 코드가 있어야합니다.


답변

종종 사람들은 무슨 일이 일어나고 있는지 (그리고 많은 웃음을 포함하여) 오해합니다.
많은 사람들이 그것을 사용하는 방식만큼 나쁘다는 것은 그 자체가 팩토리 패턴이 아닙니다.

그리고 의심 할 여지없이 프로그래밍 (및 패턴)을 가르치는 방식에서 비롯됩니다. Schoolkids (종종 “학생”이라고 함)는 “패턴 Y를 사용하여 X 만들기”라는 메시지를 받고 몇 번의 반복 후에는 프로그래밍 문제에 접근 할 수있는 방법이라고 생각합니다.
그래서 그들은 적절하든 그렇지 않든 학교에서 좋아하는 특정 패턴을 모든 것에 대해 적용하기 시작합니다.

그리고 슬프게도 소프트웨어 디자인에 관한 책을 쓰는 대학 교수도 포함됩니다.
이것의 절정은 내가 만든 것 중 하나에 의해 유지되어야하는 명백한 불만을 가진 시스템이었다. ).
그것은 각 계층 자체가 3 계층 시스템 (분리해야 함)과 함께 3 계층 패턴을 기반으로했습니다. 각 계층 세트 사이의 인터페이스에는 양쪽에 데이터를 다른 계층으로 전송할 오브젝트를 생성하는 팩토리와 수신 된 오브젝트를 수신 계층에 속하는 오브젝트로 변환하는 오브젝트를 생성하는 팩토리가있었습니다.
각 팩토리마다 추상 팩토리가있었습니다 (누군가가 팩토리를 변경해야 할 수도 있고 호출 코드를 변경하지 않으려는 것을 알고 있습니다 …).
그리고 그 모든 혼란은 물론 완전히 문서화되지 않았습니다.

시스템은 데이터베이스를 5 번째 정규 형식으로 정규화했습니다.

기본적으로 수신 및 발신 문서를 기록하고 추적하는 데 사용할 수있는 주소록 이상의 시스템 인 C ++에는 100MB 코드베이스와 50 개가 넘는 데이터베이스 테이블이있었습니다. 소프트웨어를 실행하는 컴퓨터에 직접 연결된 프린터를 사용하여 500 개의 양식 편지를 인쇄하는 데 72 시간이 걸릴 수 있습니다.

그것이 사람들이 패턴을 비웃는 이유, 특히 사람들이 단일의 특정 패턴에 독특하게 집중하는 이유입니다.


답변

일부 상황에서 공장은 우아한 애플리케이션 설계를 가능하게하는 많은 장점이 있습니다. 하나는 팩토리를 만들어 나중에 생성하려는 객체의 속성을 한 곳에서 설정 한 다음 해당 팩토리를 넘길 수 있다는 것입니다. 그러나 종종 실제로 그렇게 할 필요는 없습니다. 이 경우 팩토리를 사용하면 실제로 대가로 아무것도주지 않고 복잡성을 추가합니다. 이 공장을 예로 들어 보겠습니다.

WidgetFactory redWidgetFactory = new ColoredWidgetFactory(COLOR_RED);
Widget widget = redWidgetFactory.create();

팩토리 패턴의 대안 중 하나는 매우 유사한 빌더 패턴입니다. 가장 큰 차이점은 팩토리가 생성 될 때 팩토리가 생성 한 객체의 속성이 설정되는 반면 빌더는 기본 상태로 초기화되고 모든 속성이 나중에 설정된다는 것입니다.

WidgetBuilder widgetBuilder = new WidgetBuilder();
widgetBuilder.setColor(COLOR_RED);
Widget widget = widgetBuilder.create();

그러나 오버 엔지니어링이 문제인 경우 팩토리를 빌더로 교체해도 크게 개선되지는 않습니다.

두 패턴 중 가장 간단한 대체는 물론 new연산자를 사용하여 간단한 생성자로 객체 인스턴스를 만드는 것 입니다.

Widget widget = new ColoredWidget(COLOR_RED);

그러나 생성자는 대부분의 객체 지향 언어에서 결정적인 단점이 있습니다. 해당 클래스의 객체를 반환해야하며 하위 유형을 반환 할 수 없습니다.

런타임에 하위 유형을 선택해야하지만 완전히 새로운 빌더 또는 팩토리 클래스를 작성하지 않으려는 경우 팩토리 메소드를 대신 사용할 수 있습니다. 이것은 해당 클래스 또는 하위 클래스 중 하나의 새 인스턴스를 반환하는 클래스의 정적 메서드입니다. 내부 상태를 유지하지 않는 팩토리는 종종 다음과 같은 팩토리 방법으로 교체 할 수 있습니다.

 Widget widget = Widget.createColoredWidget(COLOR_RED); // returns an object of class RedColoredWidget

Java 8의 새로운 기능 은 상태 비 저장 팩토리에서와 마찬가지로 메소드를 전달할 수 있는 메소드 참조 입니다. 편리하게도 메소드 참조를 받아들이는 것은 동일한 기능 인터페이스를 구현하는 모든 객체를 허용하며, 내부 상태를 가진 본격적인 팩토리 일 수도 있으므로 나중에 이유를 알면 팩토리를 쉽게 소개 할 수 있습니다.


답변

어떤 종류의 공장은 적절한 상황에서 거의 모든 객체 지향 언어로 발견됩니다. 때로는 문자열과 같은 간단한 매개 변수를 기반으로 만들 개체 종류를 선택하는 방법이 필요할 때가 있습니다.

어떤 사람들은 그것을 너무 멀리 가져 와서 공장 내부를 제외하고 생성자를 호출 할 필요가 없도록 코드를 설계하려고합니다. 공장 공장이 있으면 상황이 말도 안되기 시작합니다.

스칼라를 배웠을 때 충격을 받았지만 루비를 알지 못했지만 거의 같은 방식이라고 생각합니다. 언어가 표현력이 충분하기 때문에 프로그래머가 항상 “배관 작업”을 외부 구성 파일로 푸시하려고하지는 않습니다. . 스칼라에서는 특성이 혼합 된 객체를 사용하여 여러 구성으로 클래스를 연결하는 팩토리 팩토리를 작성하려고하지 않고, 종종 Java로 과도하게 엔지니어링되는 작업에 대해 언어로 간단한 DSL을 작성하는 것이 상대적으로 쉽습니다.

또한 다른 의견과 답변에서 지적했듯이 클로저와 일류 함수는 많은 패턴이 필요하지 않습니다. 이런 이유로 C ++ 11과 Java 8이 널리 채택되면서 많은 안티 패턴이 사라지기 시작할 것이라고 생각합니다.


답변

일반적으로 Java 프로그램이 엄청나게 과도하게 엔지니어링되는 경향이 있습니다 [인용 필요]. 공장을 많이 보유하는 것은 과잉 엔지니어링의 가장 흔한 증상 중 하나입니다. 그렇기 때문에 사람들이 재미를 느끼게됩니다.

특히, Java가 팩토리에서 갖는 문제는 Java에서 a) 생성자가 함수가 아니며 b) 함수가 일류 시민이 아니라는 것입니다.

이와 같은 것을 작성할 수 있다고 상상해보십시오 ( JREFunction 의 잘 알려진 인터페이스가 되십시오 )

// Framework code
public Node buildTree(Function<BranchNode, Node, Node> branchNodeFactory) {
    Node current = nextNode();
    while (hasNextNode()) {
        current = branchNodeFactory.call(current, nextNode());
    }
    return current
}

// MyDataNode.java
public class MyBranchNode implements BranchNode {

    public MyBranchNode(Node left, Node right) { ... }
}

// Client code
Node root = buildTree(MyBranchNode::new);

팩토리 인터페이스 또는 클래스가 없음을 참조하십시오. 많은 동적 언어에는 일류 기능이 있습니다. 아아, 이것은 Java 7 또는 이전 버전에서는 불가능합니다 (공장으로 구현하는 것은 독자에게 연습으로 남습니다).

따라서 대안은 “다른 패턴을 사용하는 것”이 ​​아니라 “더 나은 객체 모델을 가진 언어를 사용하는 것”또는 “간단한 엔지니어링을하지 않는 간단한 문제”입니다.