2008-11-24 8 views
1

내가 몇 가지 코드와의 desgin 문제의 비트에 실행 한을 작업 한 것을이 같은내부 글로벌 속성 .. 나쁜 냄새? 나는

내 코드 기본적인 외모 :

홈페이지 COM 래퍼 :

public class MapinfoWrapper 
{ 
    public MapinfoWrapper() 
    { 
     Publics.InternalMapinfo = new MapinfoWrapper(); 
    } 

    public void Do(string cmd) 
    { 
     //Call COM do command 
    } 

    public string Eval(string cmd) 
    { 
     //Return value from COM eval command 
    } 
} 

래퍼에 대한 내부 참조를 유지하는 public static 클래스 :

internal static class Publics 
{ 
    private static MapinfoWrapper _internalwrapper; 
    internal static MapinfoWrapper InternalMapinfo 
    { 
     get 
     { 
      return _internalwrapper;  
     } 
     set 
     { 
      _internalwrapper = value; 
     } 
    } 
} 
내부 래퍼 인스턴스를 사용하여

코드 :

public class TableInfo 
    { 
     public string Name { 
      get { return Publics.InternalMapinfo.Eval("String comman to get the name"); } 
      set { Publics.InternalMapinfo.Do("String command to set the name"); } 
     } 
    } 

이 사람에게 나쁜이 냄새를합니까? 기본 래퍼 객체에 대한 참조를 보유하기 위해 내부 속성을 사용해야합니까? 아니면 다른 디자인을 사용해야합니까?

참고 : MapinfoWrapper 객체는 외부 세계에서 사용되므로 실제로 싱글 톤을 만들고 싶지는 않습니다.

답변

2

MapInfoWrapper를 클래스 자체에 삽입하지 않아도 TableInfo 클래스의 테스트 가능성이 감소합니다. 이러한 MapInfoWrapper 클래스의 전역 캐시를 사용할지 여부는 클래스에 따라 달라지며 필요 여부를 결정해야하지만 래퍼를 TableInfo로 전달하고 직접 전역 복사본을 참조하지 않고 사용할 수 있습니다. TableInfo 메서드 내부. 인터페이스 정의 (예 : '리팩토링 인터페이스와 인터페이스')와 함께 사용합니다.

MapInfoWrapper의 생성자에서 객체를 설정하지 않고 이미 작성되지 않은 객체가 있는지 확인하기 위해 Publates getter에서 지연 인스턴스화를 수행합니다. 당신이 TableInfo 방법을 테스트 할 때

public class TableInfo 
{ 
    private IMapinfoWrapper wrapper; 

    public TableInfo() : this(null) {} 

    public TableInfo(IMapinfoWrapper wrapper) 
    { 
      // use from cache if not supplied, could create new here 
      this.wrapper = wrapper ?? Publics.InternalMapInfo; 
    } 

    public string Name { 
     get { return wrapper.Eval("String comman to get the name"); } 
     set { wrapper.Do("String command to set the name"); } 
    } 
} 

public interface IMapinfoWrapper 
{ 
    void Do(string cmd); 
    void Eval(string cmd); 
} 

public class MapinfoWrapper 
{ 
    public MapinfoWrapper() 
    { 
    } 

    public void Do(string cmd) 
    { 
     //Call COM do command 
    } 

    public string Eval(string cmd) 
    { 
     //Return value from COM eval command 
    } 
} 

internal static class Publics 
{ 
    private static MapinfoWrapper _internalwrapper; 
    internal static MapinfoWrapper InternalMapinfo 
    { 
     get 
     { 
      if (_internalwrapper == null) 
      { 
       _internalwrapper = new MapinfoWrapper(); 
      } 
      return _internalwrapper;  
     } 
    } 
} 

이제, 당신은 생성자에 자신의 구현을 제공하여 쉽게 MapInfoWrapper을 조롱 할 수 있습니다.

[TestMethod] 
[ExpectedException(typeof(ApplicationException))] 
public void TestTableInfoName() 
{ 
    IMapinfoWrapper mockWrapper = new MockMapinfoWrapper(); 
    mockWrapper.ThrowDoException(typeof(ApplicationException)); 

    TableInfo info = new TableInfo(mockWrapper); 
    info.Do("invalid command"); 
} 
+0

멋지다. 시작하는 방법은 그렇게 할 예정 이었지만 어쩌면 잘못이라고 생각했지만 지금은 테스트로 구현되어 힙을 더 잘 이해할 수 있습니다. 그걸 건배! –

+0

내가 가진 유일한 문제는 사용자 관점에서 잘 읽히지 않는다는 것입니다. 나는 이것을 좋아하지 않는다. TableInfo tab = 새로운 TableInfo (Nothing); –

+0

내 마지막 코멘트에 신경 쓰지 마라, 나는 코드를 읽는다. :( –

0

내가 내 원래의 응답이를 추가하는 방법에 대한 생각,하지만 정말 다른 문제입니다 : 예는 (손 모의 가정).

캐시 된 복사본을 저장하고 사용하는 경우 MapinfoWrapper 클래스가 스레드로부터 안전해야하는지 여부를 고려할 수 있습니다. 단일 전역 복사본을 사용할 때마다 한 번에 둘 이상의 스레드가 사용할 것인지 고려하여 모든 중요한 섹션 (데이터가 변경 될 수 있거나 변경되지 않는 것으로 가정해야 함)이 스레드 기반의 스레드가되도록 고려해야합니다. 안전한. 웹 사이트에서 말하면 다중 스레드 환경을 지원해야하는 경우 클래스 작성 비용이 매우 높지 않는 한 단일 글로벌 사본 사용을 반대 할 수 있습니다. 물론 클래스가 스레드 안전하지 않은 다른 클래스에 의존한다면 클래스를 스레드로부터 안전하게 만들어야 할 수도 있습니다.