공장 패턴이 개방 / 폐쇄 원칙을 위반합니까?


14

이 ShapeFactory가 인스턴스화 할 오브젝트를 판별하기 위해 조건문을 사용하는 이유는 무엇입니까? 나중에 다른 클래스를 추가하려면 ShapeFactory를 수정하지 않아도됩니까? 이것이 공개 폐쇄 원칙을 위반하지 않는 이유는 무엇입니까?

공장 패턴 디자인

ShapeFactory 디자인


2
어떤“공장 패턴”을 정확하게 언급하고 있습니까? 일반적으로 팩토리는 객체를 인스턴스화하는 데 사용되는 객체 또는 메소드입니다. 그런 다음 추상 팩토리 패턴과 같은이 일반적인 아이디어의 특정 변형이 있습니다. 여기서 각 팩토리 인스턴스는 일반적으로 조건이 아닌 서브 클래 싱을 통해 관리되는 특정 선택 팔레트를 나타냅니다.
amon


3
그 정보에 감사드립니다. 이는 팩토리 패턴의 예이지만 팩토리 패턴과 일반적으로 연관된 추상 팩토리 패턴은 아닙니다. 이 기사의 코드는 매우 의심 스럽습니다. 실제 코드에서는 이와 같은 것을 보지 않을 것입니다.
amon

@ArmonSafai :이 블로그 게시물을 많이 링크하고 있지만 실제로 그 이유를 설명하지는 않습니다. 우리는 어떻게 든 패턴을 무지합니까? 우리도 당신처럼 Google을 가지고 있습니다.
Robert Harvey

1
@RobertHarvey이 블로그 게시물을 링크하여 해당 페이지의 팩토리 패턴이 조건을 사용하는 방법을 보여줍니다
Armon Safai

답변:


20

기존의 객체 지향적 지혜는 if문장 을 피하고 이를 추상 클래스의 서브 클래스에서 재정의 된 메소드의 동적 디스패치로 대체하는 것입니다. 여태까지는 그런대로 잘됐다.

그러나 팩토리 패턴의 요점은 개별 서브 클래스에 대해 알 필요가없고 추상 수퍼 클래스로만 작업해야한다는 것 입니다. 아이디어는 팩토리가 인스턴스화 할 특정 클래스보다 공장을 더 잘 알고 있으며 수퍼 클래스가 게시 한 메소드로만 작업하는 것이 좋습니다. 이것은 종종 사실이며 귀중한 패턴입니다.

따라서 팩토리 클래스를 작성하는 것이 if진술을 포기할 수있는 방법은 없습니다 . 그것은이다 호출자에 특정 클래스를 선택하는 부담이 이동할 것입니다 정확히 패턴을 방지하기 위해 가정 무엇을. 모든 원칙 (사실, 절대 어떤 원리는 절대 없다), 당신은이 패턴을 사용하는 경우 당신은 그것에서 이익이를 사용하지의 이익보다 큰 것으로 가정 것입니다 if.


2
많은 ifs 없이 팩토리 패턴을 작성하는 것이 완벽하게 가능합니다 . 이를 달성하는 간단한 예는 @ BЈовић의 답변을 참조하십시오. 공감.
David Arno


11
@DavidArno 당연히 구체적인 클래스를 선택하는 다른 방법이 있습니다. Service Locator는 하나이며 구성 가능한 IoC 컨테이너는 또 다른 것입니다. 그것들은 단지 구현 세부 사항입니다. 그들은 Killian의 주요 메시지를 방해하지 않습니다. 즉, Factory는 호출자가 인스턴스화해야 할 구체적인 클래스를 결정하지 않아도된다는 것입니다. 세부 사항에 빠져들지 마십시오.
Robert Harvey

1
결코 질문에 답하지 않는 훌륭한 진술.
Martin Maat

1
@ R.Schmitz 당신의 가정이 틀렸다고 생각합니다. 많은 사람들이 OP에서이 질문을 놓쳤다 고 생각합니다. "나중에 다른 클래스를 추가하려면 ShapeFactory를 수정해야합니까?" 분명히, OP는 새로운 기능을 추가하기 위해 기존 코드를 수정해야하기 때문에이 패턴이 OCP를 위반한다고 혼동합니다. 이 질문에 대한 정답은 내 대답에서 찾을 수 있습니다. 짧은 대답 : 당신은 그 코드를 그대로두고 기존 기능을 확장하지 않고 Abstract Factory 패턴을 적용합니다. 이 Kilian의 답변으로 인해 질문을 다루지 않습니다.
hfontanez

5

이 예는 조건문이 가장 단순하기 때문에 아마도 조건문을 사용합니다. 보다 복잡한 구현은 맵이나 구성을 사용하거나 클래스가 스스로 등록 할 수있는 일종의 레지스트리를 사용할 수 있습니다. 그러나 클래스 수가 적고 드물게 변경되는 경우 조건부 사용에 아무런 문제가 없습니다.

미래에 새로운 하위 클래스에 대한 지원을 추가하기 위해 조건부를 확장하는 것은 실제로 개방 / 폐쇄 원칙을 위반하는 것입니다. "올바른"솔루션은 동일한 인터페이스로 새 팩토리를 작성하는 것입니다. 즉, O / C 원칙 준수는 항상 KISS 및 YAGNI와 같은 다른 설계 원칙과 비교되어야합니다.

즉, 표시된 코드는 공장의 개념을 보여주기 위해 설계된 코드의 예입니다. 예를 들어 예제와 같이 null을 반환하는 것은 실제로 나쁜 스타일이지만 더 정교한 오류 처리는 요점을 모호하게합니다. 예제 코드는 프로덕션 품질 코드가 아니므로 예상하지 마십시오.


맵 / 구성 / 레지스트리 작동 방식을 설명해 주시겠습니까?
Armon Safai

@ArmonSafai : 예를 들면 다음과 같습니다. jkfill.com/2010/12/29/self-registering-in-c-sharp
JacquesB

자체 등록 팩토리는 AFAIK이며, 정적 라이브러리 내에서 C ++에서는 사용할 수없는 (예 : odr-used) 전역 변수가 툴체인에 의해 삭제되므로 불가능합니다.
void.pointer

@ArmonSafai 더 이해하기 위해 이것을 읽으십시오 goo.gl/RYuNSM
AZ_

2

패턴 자체는 OCP (Open / Closed Principle)를 위반하지 않습니다. 그러나 패턴을 잘못 사용하면 OCP를 위반하게됩니다.

이 질문에 대한 간단한 대답은 다음과 같습니다.

  1. 팩토리 메소드 패턴을 사용하여 기본 기능을 작성하십시오 .
  2. EXTEND 사용하여 기능을 추상 팩토리 패턴

제공된 예제에서 기본 기능은 Circle, Rectangle 및 Square의 세 가지 모양을 지원합니다. 향후 Triangle, Pentagon 및 Hexagon을 지원해야한다고 가정하십시오. OCP를 위반 하지 않고이 작업을 수행하려면 새 모양 (이라고 함 AdvancedShapeFactory) 을 지원하는 추가 팩토리를 생성 한 다음 AbstractFactory 를 사용 하여 필요한 모양을 생성하기 위해 생성해야하는 팩토리를 결정해야합니다.


자체 등록 팩토리 솔루션을 선호합니다 (실제로 구성 가능한 IoC 컨테이너가없는 경우 가장 좋습니다). 그렇지 않으면 제안에서 얻는 것이 기본적으로 팩토리 팩토리 이므로 상황이 지나치게 복잡해지기 때문입니다.
Nom1fan

1

Abstract Factory 패턴에 대해 이야기하고 있다면 의사 결정은 종종 Factory 자체가 아니라 응용 프로그램 코드에 있습니다. 그것은 팩토리가 생성 한 객체를 사용할 클라이언트 코드로 인스턴스화하고 전달할 구체적인 팩토리를 선택하는 코드입니다. https://en.wikipedia.org/wiki/Abstract_factory_pattern 에서 Java 예제의 끝을 참조하십시오.

의사 결정이 반드시 if진술을 의미하지는 않습니다 . 구성 파일에서 구체적 팩토리 유형을 읽고 맵 구조 등에서 파생 할 수 있습니다.



발신자가 어떤 구체적인 클래스를 인스턴스화해야하는지 결정하고 있다면 왜 Abstract Factory와 관련이 있습니까?
Robert Harvey

"발신자"를 정의하십시오. 내 대답에서 설명하는 것처럼 전역 응용 프로그램 코드가 있으며 팩토리를 사용하여 객체를 생성 해야하는 코드가 있습니다. 후자는 실제로 인스턴스화하기 위해 구체적인 클래스를 알 필요는 없지만, 다른 문맥 코드 그것에 대해 알고 새로운 코드 를 알아야한다.
guillaume31

0

이 팩토리의 클래스 수준에서 Open-Close에 대해 생각하면 시스템을 Close-Close로 다른 클래스를 만듭니다. 예를 들어 하나의 Shape를 취하는 다른 클래스가 있고 면적을 계산하는 경우 (일반적인 예)이 클래스는 OpenClose 수정하지 않고 새로운 유형의 도형에 대한 면적을 계산할 수 있습니다. 그런 다음 모양을 그리는 또 다른 클래스, N 모양을 취하고 더 큰 클래스를 반환하는 다른 클래스가 있으며 일반적으로 모양을 처리하는 시스템의 다른 클래스는 (적어도 모양에 대해) 닫힘이라고 생각할 수 있습니다. 설계를 살펴보면 공장에서 나머지 시스템을 개방 및 폐쇄 할 수 있으며 공장 자체는 개방되지 않습니다.

물론 어떤 종류의 동적 로딩을 통해이 팩토리를 오픈 클로즈로 만들 수 있으며 전체 시스템은 오픈 클로즈가 될 수 있습니다 (예를 들어 클래스 경로에 항아리를 떨어 뜨리는 새로운 모양을 추가 할 수 있음). 모든 시스템이 플러그 가능한 기능을 필요로하는 것은 아니며 모든 시스템이 완전 개방형 일 필요는 없습니다.


0

Liskov 대체 원칙 인 공개 폐쇄 원칙은 클래스 계층 구조, 상속 계층 구조에 적용됩니다. 예제에서 팩토리 클래스는 인스턴스화하는 클래스의 가계도에 없으므로 이러한 규칙을 위반할 수 없습니다. GetShape (또는보다 적절한 이름의 CreateShape)가 Shape 기본 클래스에서 구현 된 경우 위반이 발생합니다.


-2

그것은 모두 구현 방법에 달려 있습니다. std::map객체를 생성하는 함수에 대한 함수 포인터를 보유 하는 데 사용할 수 있습니다 . 그런 다음 개방 / 폐쇄 원칙을 위반하지 않습니다. 또는 스위치 / 케이스.

어쨌든 팩토리 패턴이 마음에 들지 않으면 항상 의존성 주입을 사용할 수 있습니다.


6
조건부보다 스위치 / 케이스가 어떻게 더 좋습니까? 실제로 다른 구현의 레지스트리가 필요한 경우 (예 : 일부 DI 컨테이너 구현) 맵 / dict / table을 사용하여 코드를 데이터로 표시하는 것이 좋습니다. 그러나 대부분의 공장에서 동일한 유형의 다른 콜백을 가질 필요는 없습니다! 나는 당신이 왜 그것을 제안하는지 잘 모르겠습니다. 또한 많은 DI 컨테이너는 팩토리 객체 측면에서 구현되므로 팩토리 대신 DI를 사용하는 것이 다소 순환 적 인 것처럼 보입니다.
amon

1
@amon 저는 공장이 아닌 다른 유형의 DI를 사용하려고했습니다.
BЈовић


1
공장은 어떤 포인터를 사용할지 어떻게 결정합니까? 결국 결정을 내려야합니다.
whatsisname

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