0

虽然我在Code Review中问过这个问题,但原始代码现在正在蔓延。是的,我也是清洁代码讲座的忠实粉丝,刚刚看了那些很棒的视频,我也看到了另一个问题。这与我最初遇到的问题相同。

我有一门课说人类。人类基于其 Travel 方法中的某些决定可以调用 Horse、Camel 或 Ship To Travel,也可以要求所有这些人(在某些情况下) 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 接口中有四五个方法,那么请上帝帮助我。

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

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

4

1 回答 1

0

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

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

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

如果我出于某种原因需要严格分离逻辑,这可能就是我会这样做的方式。对于这种微不足道的任务来说可能有点太复杂了,但我认为它对于更大的应用程序来说是一个很好的基础。:) 可能还有其他方法。

于 2013-07-15T09:29:12.450 回答