依赖注入,类需要相同类型接口的字典是一个好主意吗?

时间:2013-07-14 17:09:44

标签: c# dependency-injection factory-pattern constructor-injection

虽然我已经在Code Review中提出了这个问题,但原始代码现在正在蔓延。是的,我也是清洁代码谈话的忠实粉丝,只是观看了那些精彩的视频,我也看到了another question。这与我最初遇到的问题相同。

我有一个人说人。基于Travel方法中某些决定的人可以调用Horse,Camel或Ship To Travel,也可以询问所有人(在某些情况下)旅行。

所有的Horse,Camel,Ship都有ITransport界面,当然这个界面也有Travel方法。

最初的问题是,在我的项目的生命周期中,我可能会得到一些新的交通工具,如飞机,火箭,潜艇等。

所以我不能简单地在构造函数中传递它们作为ITransport船,ITransport马.....等等,因为我的构造函数参数将继续膨胀。

所以我找到了一个解决方案(我认为),HumanFactory应该有一个事件,并且该事件应该在Human类的构造函数中传递。

虽然我已经以某种方式删除了我的大型交通清单,但是你知道接口可以有很多方法。所以现在我需要传递许多代理,每个委托对应一个接口和方法的方法。

我甚至试图通过创建一个Human Mapper类来解决这个问题,该类的唯一职责是映射到正确的传输,调用正确的事件。这有效!

现在,因为这是一个虚构的例子,在现实世界的例子中,接口的方法接受参数,那么我将如何处理它呢?

我认为我的方向是制造维护梦魇。

我正在粘贴代码以供快速参考。

interface ITransport
{
    void Travel();
}

我的运输工厂如下:

public class TransportFactory
{
....
    internal ITransport ProvideTransport(TransportTypes transportType)
    {
        switch (transportType)
        {
            case TransportTypes.Camel: return new Camel();
            case TransportTypes.Horse: return new Horse();
            case TransportTypes.Ship: return new Ship();
            default:
                return null;
        }
    }
}
建议后,我的人类课程变为:

public class Human
{
    Action<Human, string> _transportRequested;

    public Human(Action<Human, string> transportRequested)
    {
        _transportRequested = transportRequested;
    }

    public void Travel()
    {
        if (_transportRequested != null)
        {
            var ev = _transportRequested;
            ev.Invoke(this, GroundTypes.Plains.ToString());
        }
    }
}

我现在有一个人类工厂,如下所示:

public class HumanFactory
{
    ITransport camel;
    ITransport ship;
    ITransport horse;
    Human _human;
    Dictionary<string, ITransport> _availableTransports;
    event Action<Human, string> transportRequested;

    public HumanFactory(TransportFactory tFactory)
    {

        horse = tFactory.ProvideTransport(TransportTypes.Horse);
        camel = tFactory.ProvideTransport(TransportTypes.Camel);
        ship = tFactory.ProvideTransport(TransportTypes.Ship);
    }

    public Human ConfigureHuman()
    {
        if (_availableTransports == null)
        {
            _availableTransports = new Dictionary<string, ITransport>();
            _availableTransports.Add(GroundTypes.Desert.ToString(), camel);
            _availableTransports.Add(GroundTypes.Sea.ToString(), ship);
            _availableTransports.Add(GroundTypes.Plains.ToString(), horse);
        }

        transportRequested += new Action<Human, string>(_human_transportRequested);
        _human = new Human(transportRequested);

        return _human;
    }

    void _human_transportRequested(Human human, string groundType)
    {
        if (_availableTransports.ContainsKey(groundType))
        {
            ITransport suitableTransport = _availableTransports[groundType];
            suitableTransport.Travel();
        }
        else
        {
            //code for handling below conditions goes here
            //I don't know what to do for this type of plain?
        }
    }
}

我谈到了一个Mapper类,它将正确的传输映射到正确的方法(它看起来很难看,但那是我提出的最好的:) :):

class Human_Transport_MethodMapper
{
    Dictionary<GroundTypes, ITransport> _availableTransports;
    List<EventTypes> _availableEvents;
    event Action<Human, GroundTypes, EventTypes> transportRequested;
    internal Action<Human, GroundTypes, EventTypes> transportRequesteddel;
    public Human_Transport_MethodMapper(Dictionary<GroundTypes, ITransport> availableTransports, List<EventTypes> availableEvents)
    {
        _availableEvents = availableEvents;
        _availableTransports = availableTransports;
        transportRequested += human_OnAnyEventReceived;
        transportRequesteddel = transportRequested;
    }
    internal void human_OnAnyEventReceived(Human human, GroundTypes groundType, EventTypes eventType)
    {
        if (_availableTransports.ContainsKey(groundType))
        {
            ITransport suitableTransport = _availableTransports[groundType];
            switch (eventType)
            {
                case EventTypes.Travel: suitableTransport.Travel();
                    break;
                default:
                    break; //meaning interface's correct method has not been mapped.
            }

        }
        else
        {
            //code for handling below conditions goes here
            //I don't know what to do for this type of plain?
        }
    }
}

现在看到在这个事件中,如果有两个参数,For Travel方法,那么委托签名就会改变,如果ITransport界面中有四个或五个方法,那么上帝可以帮助我。

我希望我在这里解释了我的问题。 感谢

编辑:我正在从这个问题中删除一些明显的代码,以使其更具可读性,并且它也变得更加冗长

1 个答案:

答案 0 :(得分:0)

首先,您处理标准事件的方式令人困惑且过于复杂。您不需要将事件传递给构造函数请参阅上一个主题中关于如何简化它的上一次编辑。

另外,正如我在上一个主题中提到的,在大型应用程序中处理复杂事件网络的常用方法是实现EventsAggregator模式(http://codebetter.com/jeremymiller/2009/07/22/braindump-on-the-event-aggregator-pattern/)。网上有各种各样的实现,所以我可以选择一个。我将使用此接口作为示例目的:

interface IEventsAggregator
{
    //sends message to network
    void Publish(object message);
    //adds object to the list of handlers
    void Subscribe(object listener);
    //removes object from the list of handlers
    void Unsubscribe(object listener);
} 

//listeners should implement this interface
interface IListener<TMessage>
{
    //handling logic for particular message
    void Handle(TMessgae message);
}

然后您的代码可以重构:

//you do not need human factory in this example
public class Human
{  
    private readonly IEventsAggregator _events;

    //see Handle implementation for details
    //public ITransport Transport { get; set; }

    public Human(IEventsAggregator events)
    {
        _events = events;
    }

    public void Travel(GroundTypes type)
    {
        _events.Publish(new TransportRequest(this, type));
        //see Handle implementation for details
        //if (Transport != null) Transport.Travel();
    }
}

public class TransportRequest
{
    public Human Sender { get; set; }
    public GroundTypes Ground { get; set; }

    public TransportRequest(Human sensder, GroundTypes ground)
    {
        Sender = sender;
        Ground = ground;
    }
}

public class TravelAgency : IListener<TransportRequest>, IDisposable
{
    private readonly IEventsAggregator _events;
    private readonly TransportFactory _tFactory;

    public TravelAgency(IEventsAggregator events, TransportFactory tFactory)
    {
        _events = events;
        _events.Subscribe(this);
        _tFactory = tFactory;
    }

    public void Handle(TransportRequest request)
    {
        var transort = _tFactory.ProvideTransport(...);
        //insert the handling logic here
        //there are two ways to handle this message:
        //1) you give human no control over (and no knowledge of) Transport 
        //and simply call transport.Travel(request.Sender); here
        //2) or you add ITransport property to Human class
        //and do only the assignation here 
        //request.Sender.Transport = transport;
        //and then let the human manage Transport object
    }

    public void Dispose()
    {
        _events.Unsubscribe(this);
    }
}

如果我出于某种原因需要严格地分离逻辑,那么这可能是我会这样做的。对于这样简单的任务来说,这可能有点过于复杂,但我认为它对于更大的应用来说是一个很好的基础。 :)可能还有其他方法。