C#&设计模式 - 需要针对常见问题的优雅解决方案

时间:2012-09-03 11:58:45

标签: 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
    }
}  

现在,如果您熟悉设计模式,那么您可以看到这里有一个注入点,应该完成以下操作:

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)

在不改变设计的情况下是否有更好的解决方案?我应该添加一个adpater吗? 如果是,请提供和示例 感谢所有

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

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

4 个答案:

答案 0 :(得分:2)

如果您确定要拆分数据结构和数据操作,请创建单独的工作类。

public interface IWorker
{
    void DoWork();
}

public abstract Worker<T>: IWorker where T: Component
{
     private T _component;
     protected Worker(T component) {_component = component;}
     public abstract void DoWork();
}

public class DbWorker: Worker<DbComponent>
{
     public DbWorker(DbComponent component): base(component) {}
     public override DoWork() {...}
}

并实施一些工厂来创建特定组件的特定工作人员。

答案 1 :(得分:2)

简单(以及三种不同的GOF设计模式)。

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

让我们定义处理程序:

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

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

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

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


完整示例:

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

    }
}

答案 2 :(得分:0)

您是否考虑过将OperatorComponent打包在一起,并将DBComponent打包在第二个项目中,与解决方案的其他项目一样?然后,您可以使用诸如Spring.net之类的轻型容器来配置.exe并加载相关的程序集。

答案 3 :(得分:0)

给出Component行为是正确的,而不是让worker类操纵它的属性。

如果您不希望其他项目看到doWork方法,请将其隐藏在公共接口后面,并从公共接口创建适配器到内部接口。

public interface ComponentPublic {
    void sharedMethod();
}

public class ComponentPublicAdapter implement ComponentPublic {
    private Component component;
    void sharedMethod() {
        // Do something, may be call doWork()
    }
}

将ComponentPublic接口打包到不同的项目/命名空间中,因此其他项目可能会在不知道内部接口的情况下与其进行交互。使用依赖注入框架或反射来实例化适配器和组件。