使用通用接口实施Factory

时间:2018-12-10 12:40:03

标签: c# generics design-patterns interface

我正在从合同中提取依赖关系以解除依赖关系

// Abstraction -- This can be published as contract where implementation needs to implement method. Most importantly 3rd party types are not present/tied to contract

public interface ISourceFactory {
   T GetSource<T>();
}


// Implementation which depends on specific source like 3rd party

public class SourceFactory {
    public T GetSource<T>()   //Unable to make this work
    {
        Type listType = typeof(T);
        if (listType == typeof(SomeBase))
        {
            return _connection.GetSource<T>();
        }
        if(listType == typeof(ExternalBase)){
            return _exconnection.GetSource<T>();
        }
        throw new Exception("Not supported");
    }

    private Connection _connection;
    private ExternalConnection _exconnection;
}

// 3rd party implementation
public class Connection {
    public T GetSource<T> where T : SomeBase
}

// 3rd party implementation
public class ExternalConnection {
    public T GetSource<T> where T : ExternalBase
}

但是我无法使SourceFactory.GetSource正常工作,因为它无法将T用作通用参数。

有人可以建议解决此问题的通用方法是什么吗?

3 个答案:

答案 0 :(得分:1)

正如评论中所说,这本身是不可能的,因为连接必须有一些共同点才能抽象为通用。您正在使用3.第三方组件,但是当您尝试在一个工​​厂中使用它们时,我想您想从它们中获取相同的数据。因此,我建议定义一个接口,在其中定义所需的公共部分:

public interface IConnection{
string GetConnectionString();
...
}

比起包装3d派对关系:

public class SomebaseWrapper : IConnection{
public SomebaseWrapper(SomeBase b){
}
...
}

比您要在工厂中包装的东西要多。

public interface ISourceFactory {
   T GetSource<T>() where T is IConnection;
}

最后,您需要提供SomebaseWrapper并像这样实现它:

public T GetSource<T>() where T : IConnection
        {
            Type listType = typeof(T);
            if (listType == typeof(SomebaseWrapper))
            {
                return new SomebaseWrapper(_connection.GetSource<SomeBase>());
            }
            if (listType == typeof(ExternalBaseWrapper))
            {
                return new (ExternalBaseWrapper)(_exconnection.GetSource<T>());
            }
            throw new Exception("Not supported");
        }

答案 1 :(得分:0)

  

我正在从合同中提取依赖关系以解除依赖关系

您粘贴的代码甚至没有编译,我指的不是Generic T,但是SomeBaseExternalBase是类,其语法不正确。至少未查询的部分应采用可编译的形式

  

以下是使用您的代码的有效版本,并做了少量补充,以确保我们可以使用通用版本和约束:

public interface ISourceFactory
{
  T GetSource<T>() where T:IBase;
}

public class SourceFactory : ISourceFactory
{
    public T GetSource<T>()   where T:IBase
    {
        Type listType = typeof(T);

        if (listType.IsAssignableFrom(typeof(SomeBase)))
        {
            return _connection.GetSource<T>();
        }

        if (listType.IsAssignableFrom(typeof(ExternalBase)))
        {
            return _exconnection.GetSource<T>();
        }

        throw new Exception("Not supported");
    }

    private Connection _connection;

    private ExternalConnection _exconnection;
}

// 3rd party implementation
public class Connection
{
    public T GetSource<T>() where T : IBase
    {
        return default(T);
    }
}

// 3rd party implementation
public class ExternalConnection
{
    public T GetSource<T>() where T : IBase
    {
        return default(T);
    }
}

public interface IBase {}

public class SomeBase : IBase {}

public class ExternalBase : IBase {}
  

修改:

  • 介绍了基本接口IBase,可以将其用作通用约束来编译代码
  

建议的设计,以下是我希望的设计

// Source Factory Interface
public interface ISourceFactory<T>
where T:IBase
{
    ISourceFactory<T> GetSource();
}


// Implementation which depends on specific source like 3rd party

public class SourceFactory<T> : ISourceFactory<T>
where T:IBase
{
    public ISourceFactory<T> GetSource()   //Unable to make this work
    {
        if(connectionDictionary.ContainsKey(typeof(T)))
          return connectionDictionary[typeof(T)]();

        throw new Exception("Not supported");
    }


    private static Dictionary<Type, Func<ISourceFactory<T>>> connectionDictionary = 
    new Dictionary<Type, Func<ISourceFactory<T>>>()
    {
      [typeof(SomeBase)] = () =>
      {
        var _connection = new Connection<T>();
          return _connection.GetSource();
      },

        [typeof(ExternalBase)] = () =>
        {
            var _exconnection = new ExternalConnection<T>();
            return _exconnection.GetSource();
        },

    }
}

// 3rd party implementation
public class Connection<T> : ISourceFactory<T> 
where T : IBase
{
    public ISourceFactory<T> GetSource() 
    {
        return new Connection<T>();
    }
}

public class ExternalConnection<T> : ISourceFactory<T> 
where T : IBase
{
    public ISourceFactory<T> GetSource()
    {
        return new ExternalConnection<T>();
    }
}

public interface IBase { }

public class SomeBase : IBase { }

public class ExternalBase : IBase { }
  

修改:

引入了重要的修改:

  1. 将基本接口IBase用作通用约束
  2. ISourceFactory<T>用作ConnectionExternalConnection类的基本接口,这些类使用SourceFactory<T>具有相同的基本接口返回
  3. 引入了一个带有Type key的字典,其值为Func delegate,该字典可以在执行时返回ISourceFactory<T>的派生类型

您可以根据特定的用例来改进设计

答案 2 :(得分:-1)

您必须为此使用反射。如果通过反射根本存在诸如便利之类的东西,那么最便捷的方法是使用泛型类型,即MakeGenericType并适当地转换结果。这还有一个优点,您只需对每种类型执行一次检查。

public class SourceFactory {
    private class SourceGetter<T> {
        public static readonly SourceGetter<T> Instance;
        static SourceGetter() {
            Type listType = typeof(T);
            if (listType == typeof(SomeBase))
            {
                Instance = (SourceGetter<T>)Activator.CreateInstance(typeof(CollectionGetter<>).MakeGenericType(listType));
            }
            else if(listType == typeof(ExternalBase)){
                Instance = ...            }
            else {
                Instance = new SourceGetter<T>();
            }
        }
        public virtual T GetSource(SourceFactory sourceFactory) {
            throw new Exception();
        }
    }
    private class CollectionGetter<T> : SourceGetter<T> where T : SomeBase {
        public override T GetSource(SourceFactory sourceFactory) {
            return sourceFactory._connection.GetSource<T>();
        }

    }
    ...
    public T GetSource<T>()
    {
        return SourceGetter<T>.Instance.GetSource(this);
    }

    private Connection _connection;
    private ExternalConnection _exconnection;
}

编辑:修改了代码,使其易于扩展。