内部全球财产......气味?

时间:2008-11-24 06:05:34

标签: c# design-patterns com wrapping

我遇到了一些我一直在研究的代码的设计问题:

我的代码基本看起来像这样:

主要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
    }
}

将内部参考保存到包装器的公共静态类

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 个答案:

答案 0 :(得分:2)

通过不将MapInfoWrapper注入类本身,您正在降低TableInfo类的可测试性。是否使用这些MapInfoWrapper类的全局缓存取决于类 - 您需要决定是否有必要,但它会改进您的设计以将包装器传递给TableInfo并在那里使用它而不是直接引用全局副本在TableInfo方法中。与接口的定义(即“接口的重构”)一起执行此操作。

我还会在Publics的getter中进行延迟实例化,以确保该对象在尚未创建的情况下可用,而不是在MapInfoWrapper的构造函数中设置它。

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;     
        }
    }
}

现在,当您测试TableInfo方法时,您可以通过向构造函数提供自己的实现来轻松地模拟MapInfoWrapper。 Ex(假设手模拟):

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

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

答案 1 :(得分:0)

我考虑将此添加到我原来的回复中,但这确实是一个不同的问题。

如果存储和使用缓存副本,您可能需要考虑MapinfoWrapper类是否需要是线程安全的。无论何时使用单个全局副本,您都需要考虑它是否会被多个线程一次使用并构建它以便任何关键部分(任何数据可能被更改或必须假设不会更改)都是线程 - 安全。如果必须支持多线程环境 - 比如在网站中 - 那么这可能会反对使用单个全局副本,除非创建类的成本非常高。当然,如果你的类依赖于其他也不是线程安全的类,那么无论如何你可能需要使你的类成为线程安全的。