2011-12-16 3 views
0

일부 데이터를 수집 한 다음 모두를 단일 응답으로 결합해야하는 Struts 1 액션 클래스 (작업은 struts 1에서 의도적으로 싱글 톤입니다)가 있습니다. 내가 좋아하는 것 는 상태 저장 의존성 및 스레드 안전

ResponseBuilder 

은 일반적으로 내가 필드로 ResponseBuilder을 넣고 (예를 들어, 테스트를 위해) 그것을위한 세터이있을 것이다라는 별도의 클래스에 모든 응답 생성 로직을 추출합니다. 이러한 구현으로

class JsonResponseBuilder implements ResponseBuilder { 
    public void addElement(String key, Object value) { 
     ... 
    } 

    public String buildResponse() { 
     // build response from data collected 
    } 
} 

을 다음과 내가 인해 여기 안전 문제를 스레드 위해 할 수 없기 때문에 내 응답 빌더 보인다.

이 디자인을 어떻게 변경하면 좋을까요? 공장 패턴이 적용 가능합니까?

ResponseBuilder builder = factory.getBuilder(); 
builder.addElement(...); 
... 
String response = builder.build(); 

보기의 설계 및 테스트 용이성 관점에서 괜찮습니다 : 나는 종속성으로

ResponseBuilderFactory 

를 사용하고 그런 식으로 호출 의미? 괜찮 으면. 이 테스트 코드를 작성하는 방법? 모의 공장? 모의 건축업자?

답변

2

공장이 작동합니다. 근본적으로 공장에서 Dependency Injection의 형태를 취하고 있습니다. 인스턴스화하거나 조치를 초기화하면 공장을 설정합니다 : 당신이 그것을 필요로 할 때

public void setResponseFactory(ResponseBuilderFactory factory) { 
    this.responseFactory = factory; 
} 

및 공장에서

, 그냥 JsonResponseBuilder의 새로운 인스턴스를 돌려줍니다. 작업에 인스턴스 변수로 JsonResponseBuilder의 인스턴스를 저장하지 않도록하십시오. 사용중인 메소드에 로컬로 남아 있거나 메소드 매개 변수로 전달되어야합니다.

테스트에서는 모의 ResponseBuilder를 반환하는 모의 팩토리로 팩토리를 쉽게 대체 할 수 있습니다. 이를 수행 할 많은 라이브러리가 있습니다 (예 : Mockito 또는 JMock). 모두 JUnit과 TestNG에서 잘 작동합니다.

편집 :

당신은 같은 인터페이스로 ResponseBuilderFactory이 필요 것 :

public interface ResponseBuilderFactory { 
    public ResponseBuilder getResponseBuilder(); 
} 

당신이 당신의 테스트를 할 때, 당신의 ResponseBuilder의 모의를 반환하는 클래스를 생성 :

@Test 
public void testMyAction() throws Exception { 
    ResponseBuilderFactory mockFactory = new ResponseBuilderFactory() { 
     public ResponseBuilder getResponseBuilder() { 
      ResponseBuilder builder = context.mock(ResponseBuilder.class); 
      // set up mock behaviour 
      return builder; 
     } 
    } 
} 

모의 팩토리를 주입하지 않으므로 모의를 반환하는 팩토리입니다.

또한 Dependency Injection vs Factory Pattern을 참조하십시오.

편집 2 :

어떻게 든 코드를 다시 관리 할 수있는 경우 JsonResponseBuilder 때문에이 상태를 유지하지 않습니다, 당신은 잠재적으로 모두 함께 전체 공장의 혼란을 피할 수 그냥 원래의 방법을 사용 . 상태를 유지하지 않는 객체는 본래 스레드로부터 안전합니다.

+0

단지 모호한 질문입니다. 모의 (공장)가 다른 모의 (건축업자)를 이상한 것으로 돌려 보내지는 않습니까? 여기에 논리적으로 보입니다. 그러나 그것이 옳은 길이면 나는 아직 알지 못합니다 (뉴비는 여기에 있습니다). – grafthez

+0

모의를 돌려주는 일반 팩토리를 만들 수 있습니다 :). 나는 내 대답을 세부 사항으로 업데이트 할 것이다. – Jonathan

+0

다른 답변에서 말했듯이 이것은 테스트 목적으로 만 디자인을 변경하는 경우와 같습니다. 말이 될 수도 있지만 너무 멀리하지 마십시오. Imho, "ResponseBuilder"또는 "ResponseBuilderFactory"에 대한 인터페이스가 없으면이 코드를 "new ResponseBuilder()"로 사용해야하며 문제는 테스트하지 않고 디자인과 관련이 있습니다. * "새로운 ResponseBuilder()"케이스 (필요한 경우)에서 작동하는 테스트 솔루션이 있거나 DI 컨테이너를 사용해야합니다. – jjmontes

2

Action Service 메소드에서 ResponseBuilder를 인스턴스화하면됩니다. 그렇게하면 (싱글 톤 클래스 멤버 대신) 로컬 변수가되고 각 스레드는 ResponseBuilder의 새로운 인스턴스를 갖게됩니다.

ResponseBuilder를 개별적으로 단위 테스트 할 수 있으며, 필요한 경우 다른 작업 (ResponseBuilder 조롱)과 마찬가지로 정상적으로 작업을 테스트 할 수 있습니다.

ResponseBuilder를 작성하기 위해 팩토리 패턴을 사용하는 것은 다른 문제입니다. 문제와 관련이 없습니다. ResponseBuilder가 비싸고 재사용 가능한 경우 ResponseBuilders의 을 사용할 수 있습니다.

+0

하지만 조치 메소드에서 "new JsonResponseBuilder()"를 사용하면이 빌더를 조롱하지 않게됩니다. 맞습니까? 아니면 여기서 중요한 것을 놓치고 있습니까? – grafthez

+0

그건 테스트 측면입니다 ... 그리고 당신이 테스트에 사용하는 라이브러리에 달려 있습니다. Powermock은 생성자를 모의 할 수 있지만, 필자는 보통 그것을 피하고 주입 된 의존성을 선호한다. 하지만 나는 테스트 목적으로 만 코드를 변경하는 것을 싫어합니다. 액션의 일부로 빌더를 고려하지 않고 생성자를 모의하지 않으려는 경우 (예 : Mockito를 사용하고 Powermock을 사용하지 않으려는 경우) 접근 방식은 완벽합니다. 그런 다음, 의지로 싱글 톤이나 새 인스턴스를 제공하도록 구성 할 수있는 Spring (또는 다른 DI 컨테이너)을 사용하는 것이 좋습니다. – jjmontes

+0

... 및 : Powermock을 사용하여 Mocking 생성자 : http://code.google.com/p/powermock/wiki/MockConstructor (이 문제를 피하려고하지만 가끔 생성자 또는 정적 메서드를 모방 할 필요가 있습니다. Powermock은 해결책이다). – jjmontes

1

ActionBuilder를 작업의 멤버 변수로 만들면 모든 요청에서 공유됩니다. 즉, 변경 가능한 상태가 스레드 안전성을 보장 할 수 있도록 동기화해야합니다.

또 다른 방법은 각 작업에 대해 호출되는 메서드 내에 새 ResponseBuilder를 만드는 것입니다. 그런 식으로 각 요청에 대한 인스턴스를 만듭니다.

+0

하나의 요청 중에 다양한 데이터를 수집하고이 데이터에서 응답을 준비하는 것이 책임있는 경우 그런 일을 동기화하기가 어려울 수 있습니다. 다른 모든 스레드가이 작업을 실행하지 못하도록하여 안전한 것으로 설정해야한다고 가정합니다. – grafthez

+0

정확 하 게 - 데이터 회원으로 원하지 않는 이유입니다. – duffymo

1

세션에서 응답 빌더를 저장할 수 있습니다. getter/setter를 사용하여 Action 클래스의 '속성'으로 표시하지만 실제 필드는 정의하지 마십시오. 대신 다음과 같이하십시오.

protected ResponseBuilder getResponseBuilder() { 
    ResponseBuilder builder = (ResponseBuilder) session.getAttribute("ATTR_RESPONSE_BUILDER"); 
    if(builder == null) { 
     builder = new ResponseBuilder(); 
     session.setAttribute("ATTR_RESPONSE_BUILDER", builder); 
    } 

    return builder; 
} 

protected void setResponseBuilder(ResponseBuilder builder) { 
    session.setAttribute("ATTR_RESPONSE_BUILDER", builder); 
} 

Action 클래스 호출 사이에서 빌더를 지울 필요가 있습니다.