2014-06-06 5 views
0

공장을 만들려고합니다. 클라이언트가 create 메소드에 코드를 전송하여 '유형'의 해당 유형을 처리하는 데 사용되는 클래스를 인스턴스화하는 데 사용됩니다.개폐가 닫힌 상태입니까?

코드 목록은 변경해서는 안되기 때문에 클래스 목록에 포함되어 있습니다. 하지만, 더 많은 테스트 할 수 있도록하기 위해 나는 codeMap 배열에 대한 setter를 추가했다.

열린 닫힌 원칙을 어기는가요? 그렇다면이 테스트를 올바르게 테스트 할 수있는 방법은 무엇입니까?

<?php 

class My_ThingFactory 
{ 
    /** 
    * @var array 
    */ 
    private $codeMap = array(
     'A111' => 'My_Thing_ConcreteA' 
    ); 

    public function create($code) 
    { 
     if (isset($this->codeMap[$code])) { 
      return new $this->codeMap[$code]; 
     } 
    } 

    public function setCodeMap(array $codeMap) 
    { 
     $this->codeMap = $codeMap; 
    } 
} 
+0

개방형/폐쇄 형 원칙을 위반하는 것에 대해서는 다음을 읽어보십시오. [종속성 주입의 끝 - 누가 종속성을 생성합니까?] (http://www.deadschool.com/article/end-dependency-injection-who-creates- 의존성) –

답변

0

개방/폐쇄 원칙은 클래스의 핵심 동작 (즉, 소스 코드를 편집하지 않음)을 수정하지 않고 기능을 추가하기 위해 일부 코드를 확장하는 것과 관련이 있습니다. 클래스는 자체적으로 내부를 유지하고 상호 작용할 수있는 명확한 공용 인터페이스를 제공합니다. 이 관점에서 볼 때 개방/폐쇄 원칙을 어긴 것은 아닙니다. 적어도 액면 그대로.

그러나 내가 말한 바에 따르면 귀하의 개인용 $codeMap 어레이에 대한 설정자를 가지고 있다면 원리가 깨지는 지 궁금하다는 인상을 받았습니다. 직접 구현하지는 않지만, 다른 개발자가 $codeMap 어레이에 대한보다 미세 조정 된 액세스를 원할 경우 수정하는 것이 매력적입니다. 기본적으로이 배열을 즉시 업데이트하는 유일한 방법은이를 지우고 setCodeMap()으로 다시 설정하는 것입니다. 맵에 단일 코드를 추가하는 메커니즘을 제공하지 않습니다. 이지도에 대한보다 세분화 된 액세스가 필요하면 즉시 공개/폐쇄 원칙을 위반하게됩니다.

다른 개발자가 코드를 사용하고 있고 $codeMap 배열이 20 또는 30 개의 요소로 충분하다고 가정 해 보겠습니다. 그들은 배열에 더 나은 액세스를 제공하기 위해 핵심 코드를 해킹해야합니다. 단일 코드를 추가 할 방법이 없으므로 현재 $codeMap 배열과 추가 할 요소를 포함하는 setCodeMap()으로 전달할 새 배열을 만들어야합니다.

class AnotherThingFactory extends My_ThingFactory 
{  
    public function addCodes(array $newCodes) 
    { 
     $this->setCodeMap(array_merge($this->getCodeMap(), $newCodes)); 
    }  
} 
:
public function getCodeMap() 
{ 
    return $this->codeMap; 
} 

그런 다음 자신의 확장 클래스에서 그들이 뭔가를 할 수 있습니다 :처럼 뭔가를 My_ThingFactory를 열고 추가하지 않고이 작업을 수행하는 (원래의 배열을 하드 코딩 이외의) 다른 방법이 없습니다

하지만 이것은 다시 클래스에 들어가 필요한 기능을 추가하기 만하면 열기/닫기 원칙을 어기는 경우에만 가능합니다. $codeMap 속성을 protected으로 만듦으로써 이것을 바로 잡은 다음 확장 클래스가 코드를 해킹하지 않고 필요한 것을 할 수 있습니다. 또한 확장 클래스는 올바르게 조작하고 있음을 보장해야합니다.

개방형/폐쇄 형 질문에 대답하려면 : $codeMap을 의도적으로 잠글려고하고 다른 방법으로 사용하지 않으려면 괜찮습니다. 그러나 위에 설명한대로 $codeMap 어레이를 더 잘 제어해야하는 경우이를 수행하기위한 원칙을 위반해야합니다. 제 제안은 당신이 당신의 클래스에 내장하고 싶어하는 팩토리의 관리를 브레인 스토밍하여 클래스 코어 기능의 일부로 만드는 것입니다.

테스트의 경우이 코드를 그대로 테스트 할 수없는 이유는 알 수 없습니다. 코드 맵을 설정 한 다음 create() 메서드를 사용하여 반환 된 해당 인스턴스를 테스트 할 수 있습니다.

class FactoryTest extends PHPUnit_Framework_TestCase 
{ 

    private $factory; 

    public function setUp() 
    { 
     $this->factory = new My_ThingFactory(); 
    } 

    public function tearDown() 
    { 
     $this->factory = null; 
    } 

    public function testMadeConcreteA() 
    { 
     $this->assertInstanceOf('My_Thing_ConcreteA', $this->factory->create('A111')); 
    } 

    public function testMadeStealthBomber() 
    { 
     $this->factory->setCodeMap(array('B-52', 'StealthBomber')); //Assume the class exists. 
     $this->assertInstanceOf('StealthBomber', $this->factory->create('B-52')); 
    } 

    public function testDidntMakeSquat() 
    { 
     $this->assertNotInstanceOf('My_Thing_ConcreteA', $this->factory->create('Nada')); 
    } 

} 
0

개방 폐쇄 원리는 보편적이지 않다. 변경 될 가능성이있는 부분 (열린 부분)과 그렇지 않은 부분 (닫힌 부분)에 대해 가정해야합니다.

공장을 사용하고 있으므로 닫힌 부분은 create 서비스입니다 (공장에서는이 부분을 폐쇄합니다). 열린 부분은 공장에서 만들 것입니다. 공장에서 나중에 이러한 것들을 확장 할 수 있습니다.

당신의 패턴이 GoF 팩토리가 아니라, 작지만 중요한 점은 Simple Factory입니다. 그래서 아마도 공개 된 원칙을 이용하기위한 가장 강력한 형태의 공장이 아닙니다. 즉, 새로운 물건을 추가하여 만들면 클래스 ($codeMap 배열)를 수정해야합니다.

코드의 목록은 클래스의 멤버, 그들은 변경해서는 안됩니다 이후 : 귀하의 질문에 서 무엇

당신이 말할 때 개방성의 원칙을 부정하는 것이다.

팩토리를 사용하는 경우 코드 목록은 이고 변경하려면이 필요합니다.

귀하의 set 함수는 public 메소드이므로 정의가 닫혀 있습니다 (그렇지 않으면 공개하지 말아야 함). 반면에 구현의 세부 정보가 노출됩니다 (Crackertastic's answer에서 언급). 이 방법으로 캡슐화를 위반하는 것에 대해 더 우려 할 수 있습니다.

더 쉬운 해결책 (PHP에서는 확실하지 않지만)은 다른 클래스에 의해 생성 된 $codeArray으로 공장을 초기화하는 것입니다. 나는 이것이 당신의 질문에 대한 그의 코멘트에서 카말 위카 마나 야크가 언급 한 것이라고 생각합니다. 또 다른 솔루션은 요소를 추가/삭제하는 서비스 (닫힌 상태)입니다 (이는 사용자의 $codeArray에 새로운 항목을 추가하지만 숨겨진 방법으로 종결됩니다).