C# 和设计模式 - 需要一个优雅的解决方案来解决常见问题

标签 c# design-patterns polymorphism adapter

我有 2 个项目:第一个项目是一个结构项目,我从 xml 文件中读取它。
该项目已在其他解决方案中使用 第二个项目(其他解决方案之一)处理结构项目,其中 foreach 在组件列表上运行:

namespace FriendProject.Workers
{
    public class Worker
    {
        static void Main(string[] args)
        {
            foreach (Component component in ComponentList)
            {
                DoWork(component);
            }
        }
    }
}    

现在,DoWork 方法执行以下操作:

public void DoWork(Component component)
{
   // Doing work on component properties
   // Zip component files with open source Zipper
   if (component is DBComponent)
   {
      // work on DBComponent properties
    }
}  

现在,如果您熟悉设计模式,那么您可以看到这里有一个注入(inject)点,并且应该执行以下操作:

public class Component
    {
        public virtual void DoWork()
        {
            // Do work
        }
    }

    public class DBComponent : Component
    {
        public override void DoWork()
        {
            base.DoWork();
            // injection point - work on DBComponent properties
        }
    }

    public class Operator
    {
        static void Main(string[] args)
        {
            foreach (Component component in ComponentList)
            {
                component.DoWork();
            }
        }
    }

问题是,保存 Component 和 DBComponent 的项目是一个结构项目,用于其他解决方案和其他项目,我需要将开源 Zip dll 添加到项目中,并且它与当前项目(“FriendProject”)并且不太可用。更不用说其他项目永远不会使用这些方法(Component 和 DBComponent 中的 DoWork)

有没有更好的解决方案而不改变太多设计?我应该添加适配器吗?
如果是,请提供示例。
感谢大家

编辑:简短问题
2个项目:
第一个是一个经理项目,作用于第二个项目。
第二个是一个结构项目(从xml读取数据),它可以与其他项目重用。
我想在结构项目(第二个项目)中添加方法和引用(由于多态性)。然而,这感觉不对,因为使用它的其他项目永远不会使用这些方法和添加的引用。
是否有更好的解决方案如何做到这一点?

编辑:
删除了结构项目代码以缩短问题。这段代码是无关紧要的,因为它的类(Component 和 DBComponent)接下来出现。

最佳答案

简单(并且具有三种不同的 GOF 设计模式)。

由于我们无法对组件执行任何操作,因此我们必须使用桥接模式。

让我们定义处理程序:

public interface IHandlerOf<T> where T : Component
{
    void DoWork(T component);
}

现在我们可以为我们想要处理的每个组件类型创建一个处理程序类型。数据库组件处理程序如下所示:

public class DbComponentHandler : IHandlerOf<DbComponent>
{
    public void DoWork(DbComponent component)
    {
        // do db specific information here
    }
}

但是由于我们并不真正想要跟踪所有处理程序,因此我们需要创建一个类来为我们完成此操作。我们最终希望像您的示例中那样调用代码:

foreach (Component component in ComponentList)
{
    handler.DoWork(component);
}

但是让我们让它变得更酷一点:

//maps handlers to components
var service = new ComponentService();

// register all handlers in the current assembly
service.Register(Assembly.GetExecutingAssembly());

// fake a component
var dbComponent = new DbComponent();

// the cool part, the invoker doesn't have to know
// about the handlers = facade pattern
service.Invoke(dbComponent);

使之成为可能的服务如下所示:

public class ComponentService
{
    private readonly Dictionary<Type, IHandlerInvoker> _handlers = new Dictionary<Type, IHandlerInvoker>();

    public void Register(Assembly assembly)
    {
        foreach (var type in assembly.GetTypes())
        {
            if (type.IsInterface)
                continue;

            foreach (var interfaceType in type.GetInterfaces())
            {
                if (!interfaceType.IsGenericType || interfaceType.GetGenericTypeDefinition() != typeof(IHandlerOf<>))
                    continue;

                var componentType = interfaceType.GetGenericArguments()[0];
                var instance = Activator.CreateInstance(type);
                var method = instance.GetType().GetMethod("DoWork", new[] { componentType });

                _handlers[componentType] = new ReflectionInvoker(instance, method);
            }
        }
    }

    public void Register<T>(IHandlerOf<T> handler) where T : Component
    {
        _handlers[typeof (T)] = new DirectInvoker<T>(handler);
    }

    #region Nested type: DirectInvoker

    private class DirectInvoker<T> : IHandlerInvoker where T : Component
    {
        private readonly IHandlerOf<T> _handler;

        public DirectInvoker(IHandlerOf<T> handler)
        {
            _handler = handler;
        }

        #region IHandlerInvoker Members

        public void Invoke(Component component)
        {
            _handler.DoWork((T) component);
        }

        #endregion
    }

    #endregion

    #region Nested type: IHandlerInvoker

    private interface IHandlerInvoker
    {
        void Invoke(Component component);
    }

    #endregion

    #region Nested type: ReflectionInvoker

    private class ReflectionInvoker : IHandlerInvoker
    {
        private readonly object _instance;
        private readonly MethodInfo _method;

        public ReflectionInvoker(object instance, MethodInfo method)
        {
            _instance = instance;
            _method = method;
        }

        #region IHandlerInvoker Members

        public void Invoke(Component component)
        {
            _method.Invoke(_instance, new object[] {component});
        }

        #endregion
    }

    #endregion

    public void Invoke(Component component)
    {
        IHandlerInvoker invoker;
        if (!_handlers.TryGetValue(component.GetType(), out invoker))
            throw new NotSupportedException("Failed to find a handler for " + component.GetType());

        invoker.Invoke(component);
    }
}

请注意,接口(interface)( IHandlerOf<T> )是通用的,这意味着我们不能将其直接存储在字典中。因此我们使用适配器模式来存储所有处理程序。


完整示例:

public interface IHandlerOf<in T> where T : Component
{
    void DoWork(T component);
}


public class ComponentService
{
    private readonly Dictionary<Type, IHandlerInvoker> _handlers = new Dictionary<Type, IHandlerInvoker>();

    public void Register(Assembly assembly)
    {
        foreach (var type in assembly.GetTypes())
        {
            if (type.IsInterface)
                continue;

            foreach (var interfaceType in type.GetInterfaces())
            {
                if (!interfaceType.IsGenericType || interfaceType.GetGenericTypeDefinition() != typeof(IHandlerOf<>))
                    continue;

                var componentType = interfaceType.GetGenericArguments()[0];
                var instance = Activator.CreateInstance(type);
                var method = instance.GetType().GetMethod("DoWork", new[] { componentType });

                _handlers[componentType] = new ReflectionInvoker(instance, method);
            }
        }
    }

    public void Register<T>(IHandlerOf<T> handler) where T : Component
    {
        _handlers[typeof (T)] = new DirectInvoker<T>(handler);
    }

    #region Nested type: DirectInvoker

    private class DirectInvoker<T> : IHandlerInvoker where T : Component
    {
        private readonly IHandlerOf<T> _handler;

        public DirectInvoker(IHandlerOf<T> handler)
        {
            _handler = handler;
        }

        #region IHandlerInvoker Members

        public void Invoke(Component component)
        {
            _handler.DoWork((T) component);
        }

        #endregion
    }

    #endregion

    #region Nested type: IHandlerInvoker

    private interface IHandlerInvoker
    {
        void Invoke(Component component);
    }

    #endregion

    #region Nested type: ReflectionInvoker

    private class ReflectionInvoker : IHandlerInvoker
    {
        private readonly object _instance;
        private readonly MethodInfo _method;

        public ReflectionInvoker(object instance, MethodInfo method)
        {
            _instance = instance;
            _method = method;
        }

        #region IHandlerInvoker Members

        public void Invoke(Component component)
        {
            _method.Invoke(_instance, new object[] {component});
        }

        #endregion
    }

    #endregion

    public void Invoke(Component component)
    {
        IHandlerInvoker invoker;
        if (!_handlers.TryGetValue(component.GetType(), out invoker))
            throw new NotSupportedException("Failed to find a handler for " + component.GetType());

        invoker.Invoke(component);
    }
}

public class DbComponent : Component
{
}

public class DbComponentHandler : IHandlerOf<DbComponent>
{
    public void DoWork(DbComponent component)
    {
        // do db specific information here
        Console.WriteLine("some work done!");
    }
}


internal class Program
{
    private static void Main(string[] args)
    {
        var service = new ComponentService();
        service.Register(Assembly.GetExecutingAssembly());

        var dbComponent = new DbComponent();
        service.Invoke(dbComponent);

    }
}

关于C# 和设计模式 - 需要一个优雅的解决方案来解决常见问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12247451/

相关文章:

c# - 在 Visual Studio Code 中自动设置 C# 代码格式

c# - 特定客户的最后订单记录 - SQL

c++ - 使用指针,不调用重写的方法

c++ - 我可以使用多态性将不同的对象存储在 C++ 的数组中吗?

c# - 从我的模型类创建 Kendo 图 TableView

c# - 为近似相似的数字生成相同的哈希码

c++ - 传递基类指针

C++设计模式建议

javascript - JavaScript 中接受所有参数的通用函数

java - 在 Java 继承中隐藏字段