【问题标题】:Multiple Implementation Attempts多次实施尝试
【发布时间】:2012-01-18 00:07:46
【问题描述】:

我正在开发一种解决方案,该解决方案将连接到各种服务器以读取数据并执行操作。有许多变量会使可靠通信复杂化,例如防火墙、停止/失败的服务、身份验证差异和各种软件配置。我可以使用一些方法来解决这些问题,但在执行时还不知道哪种方法会成功。

我的目标是创建一个可用于执行操作的接口和实现。第一个方法调用将是适用于大多数设备的最快实现,然后是其他可以处理前面列出的问题的调用。

在一个完美的世界中,将编写该过程以快速确定哪种方法会成功,但在我的测试中,处理时间与捕获异常一样多。虽然性能始终是一个考虑因素,但最终成功完成任务更为重要。

下面是我创建的一个示例,它演示了迭代实现列表的最坏情况。虽然这适用于一种方法,但在 20 种或更多不同操作中使用时,它不遵循 DRY 原则。一种可能的解决方案是 Unity 和 Interception,但我发现调用处理程序中的调用方法使用已解决的实现,而不是可能实现的列表。除非我遗漏了什么,否则这似乎不是一个选择。另外,我需要为几个接口遵循这个模式,所以最好创建一个可以迭代实现列表的通用处理程序。

如有任何关于如何完成此任务的建议,我们将不胜感激!

界面

public interface IProcess
{
    int ProcessItem(string workType);
}

实现

public class ProcessImplementation1 : IProcess
{
    public int ProcessItem(string workType)
    {
        throw new TimeoutException("Took too long");
    }
}

public class ProcessImplementation2 : IProcess
{
    public int ProcessItem(string workType)
    {
        throw new Exception("Unexpected issue");
    }
}

public class ProcessImplementation3 : IProcess
{
    public int ProcessItem(string workType)
    {
        return 123;
    }
}

特殊实现循环通过其他实现,直到一个成功无一例外

public class ProcessImplementation : IProcess
{
    public int ProcessItem(string workType)
    {
        List<IProcess> Implementations = new List<IProcess>();
        Implementations.Add(new ProcessImplementation1());
        Implementations.Add(new ProcessImplementation2());
        Implementations.Add(new ProcessImplementation3());
        int ProcessId = -1;
        foreach (IProcess CurrentImplementation in Implementations)
        {
            Console.WriteLine("Attempt using {0} with workType '{1}'...",
                CurrentImplementation.GetType().Name, workType);
            try
            {
                ProcessId = CurrentImplementation.ProcessItem(workType);
                break;
            }
            catch (Exception ex)
            {
                Console.WriteLine("  Failed: {0} - {1}.",
                    ex.GetType(), ex.Message);
            }
            Console.WriteLine();

            if (ProcessId > -1)
            {
                Console.WriteLine("  Success: ProcessId {0}.", ProcessId);
            }
            else
            {
                Console.WriteLine("Failed!");
            }
            return ProcessId;
        }
    }
}

【问题讨论】:

  • 我打算建议复合模式,但仔细阅读后我意识到你已经实现了它。
  • @Maxim - 你为什么要删除你的责任链模式答案?我打算投票赞成!

标签: c# unity-container failover interface-implementation


【解决方案1】:

您可以在第二个界面中使用TryParse 模式:

public interface IProcess
{
    int ProcessItem(string workType);
}

internal interface ITryProcess
{
    bool TryProcessItem(string workType, out int result);
}

public class ProcessImplementation1 : ITryProcess
{
    public bool TryProcessItem(string workType, out int result)
    {
        result = -1;
        return false;
    }
}

public class ProcessImplementation : IProcess
{
    public int ProcessItem(string workType)
    {
        var implementations = new List<ITryProcess>();
        implementations.Add(new ProcessImplementation1());
        // ...
        int processId = -1;
        foreach (ITryProcess implementation in implementations)
        {
            if (implementation.TryProcessItem(workType, out processId))
            {
                break;
            }
        }
        if (processId < 0)
        {
            throw new InvalidOperationException("Unable to process.");
        }
        return processId;
    }
}

【讨论】:

  • 我最终选择了这条路线。虽然这不是提出的最干燥的解决方案,但它确实解决了我在整个解决方案中大量异常处理时一直面临的问题。
【解决方案2】:

您可以将处理操作实现为通用扩展方法,您可以传递一个对单个项目进行处理的方法:

public static int ProcessItems<T>(this IEnumerable<T> items, Func<T, int> processMethod)
{
    foreach (var item in items)
    {
        try
        {
            return processMethod(item);
        }
        catch(Exception) {}
    }
    return -1;
}

现在您已经确定了项目的实际类型以及您使用的处理方法。唯一“固定”的是泛型方法的结果类型,它是一个整数。

对于您当前的示例,您可以这样称呼它:

List<IProcess> implementations = ...;
int processResult = items.ProcessItems(x => x.ProcessItem(workType));

【讨论】:

    【解决方案3】:

    如果你想要一些非常简单和“通用”的东西,这里有一个类似于 @BrokenGlass 创建的解决方案。

    public void TryAllImplementations<TService>(
        IEnumerable<TService> services,
        Action<TService> operation,
        Action<Exception> exceptionHandler = null)
    {
        int dummy = 0;
        TryAllImplementations(
            services, 
            svc => { operation(svc); return dummy; },
            exceptionHandler);
    }
    
    public TReturn TryAllImplementations<TService, TReturn>(
        IEnumerable<TService> services, 
        Func<TService, TReturn> operation,
        Action<Exception> exceptionHandler = null)
    {
        foreach (var svc in services)
        {
            try
            {
                return operation(svc);
            }
            catch (Exception ex)
            {
                if (exceptionHandler != null)
                    exceptionHandler(ex);
            }
        }
        throw new ProgramException("All implementations have failed.");
    }
    

    由于我看到了 Unity 标记,因此您可以使用服务接口在容器上使用 ResolveAll&lt;TService&gt;() 来获取所有实现。结合此代码,您可以在IUnityContainer 上执行类似扩展方法的操作:

    public static class UnityContainerExtensions
    {
        public static void TryAllImplementations<TService>(
            this IUnityContainer container,
            Action<TService> operation,
            Action<Exception> exceptionHandler = null)
        {
            int dummy = 0;
            container.TryAllImplementations<TService, int>(
                svc => { operation(svc); return dummy; },
                exceptionHandler);
        }
    
        public static TReturn TryAllImplementations<TService, TReturn>(
            this IUnityContainer container,
            Func<TService, TReturn> operation,
            Action<Exception> exceptionHandler = null)
        {
            foreach (var svc in container.ResolveAll<TService>())
            {
                try
                {
                    return operation(svc);
                }
                catch (Exception ex)
                {
                    if (exceptionHandler != null)
                        exceptionHandler(ex);
                }
            }
            throw new ProgramException("All implementations have failed.");
        }
    }
    

    以下是它的使用方法:

    IUnityContainer container;
    
    // ...
    
    container.RegisterType<IProcess, ProcessImplementation1>();
    container.RegisterType<IProcess, ProcessImplementation2>();
    container.RegisterType<IProcess, ProcessImplementation3>();
    
    // ...
    
    container.TryAllImplementations(
        (IProcess svc) => svc.ProcessItem(workType),
        ex => Console.WriteLine(
            "  Failed: {0} - {1}.",
            ex.GetType(), 
            ex.Message));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-30
      相关资源
      最近更新 更多