【问题标题】:looking how to implemant simple polymorphism寻找如何实现简单的多态性
【发布时间】:2012-04-23 14:03:40
【问题描述】:

我有一个非常基本的问题(我不知道为什么我无法思考)。

我正在尝试做一些多态性。

我的界面是这样的:

Public interface Iplugin
{

   void doSomthing(string _str);
}

我也有一些实现这个接口的插件类

public class plugin1:Iplugin
{
    void doSomthing(string _str)
    {
        if (_str=="1")
        {
           // do somthing 1
        }
     }
 }

 public class plugin2:Iplugin
{
    void doSomthing(string _str)
    {
        if (_str=="2")
        {
           // do somthing 2
        }
     }
 }

public class plugin3:Iplugin
{
    void doSomthing(string _str)
    {
        if (_str=="3")
        {
           // do somthing 3
        }
     }
 }

所以我有主类,我希望它调用所有插件

但我希望它保存 OCP(开闭原则),所以如果我将来添加另一个插件类,主类不会改变。

这是主类

public class mainApp
{
  Iplugin _context;
  mainApp()
  {
     _context= new //new what??
  }
  bool func(string str)
  {
      _context.doSomthing(str);//I would like it to invoke all the plug in and also a future plugins that I will add
  }
} 

【问题讨论】:

  • 不确定真正的问题是什么?您肯定需要在其中进行一些继承并使 doSomething 成为虚拟的吗?
  • 制作插件并不是一项微不足道的编程任务,这通常是您不/不能遵循许多标准实践的原因。如果你只是想找一个例子来学习编程,我建议你远离插件,至少现在是这样。如果这不仅仅是一个学术练习,请随意忽略我。

标签: c# interface polymorphism implementation open-closed-principle


【解决方案1】:

当然,要创建特定的Iplugin,您需要知道实现类型。看Factory Pattern

【讨论】:

    【解决方案2】:

    对于这种情况,我喜欢使用Factory Pattern。您可以轻松地将其与一些属性和反射魔法结合起来构建可用插件的存储库

    [PluginAttribute("myPlugin")]
    class MyPlugin : IPlugin
    

    现在工厂最初检查所有加载的程序集中的所有类并搜索属性并将类型和插件字符串标识符存储在字典中。

    class PluginFactory
    {
        static Iplugin CreatePlugin(string aName)
        {
            return Activator.CreateInstance( sRegisteredPlugins[aName]);
        }
    
        private static Dictionary<string, Type> sRegisteredPlugins;
    }
    

    【讨论】:

    • 你好,我需要 PluginAttribute 做什么?我应该在里面放什么
    • @MoShe 我使用该属性来标记工厂的类。这样工厂需要检查所有加载的程序集中的每个类,并查看它是否实现了 IPlugin 接口并具有该属性。一个抽象插件可能只有接口,但不应该有属性。如果两者都为真,则将属性中的名称和类型存储在字典中。当然,您也可以创建一个注册方法并手动完成,例如 Péter Töröks 的回答。但我更喜欢这种自动方式。
    • 你觉得我的回答怎么样?我认为它可以节省 ocp
    • @MoShe 这取决于使用情况,我不会在加载时创建所有插件。例如,如果我支持不同的视频编解码器,我只会在用户真的想使用它时创建视频编解码器插件。同样使用 GetExecutingAssembly 可能还不够,也许您还想加载其他 dll。您还假设您的插件始终位于一个命名空间中,这有点严格,这再次阻碍了通过添加新 dll 来添加新插件。但这是一个好的开始。但我不确定您为什么认为您的解决方案保存了开放/封闭原则。
    【解决方案3】:

    您可以添加一个集合来存储您的插件。集合可以在一个地方填充,然后传递给另一个方法,该方法只是遍历所有插件并调用它们。这样,它就完全独立于集合中的插件类型。

    正如@Ian 所说,您需要声明doSomthing virtual 才能正常工作。

    public class mainApp
    {
      mainApp()
      {
        List<Iplugin> plugins = new ArrayList<Iplugin>;
    
        ...
    
        plugins.add(new plugin1());
        ...
        plugins.add(new plugin3());
        ...
        func(plugins, "1");
        ...
        func(plugins, "7");
      }
    
      bool func(List<IPlugin> plugins, string str)
      {
        foreach (IPlugin plugin in plugins) {
          plugin.doSomthing(str);
        }
      }
    }
    

    这是Dependency Injection 的一个简单示例,它是多态性的一个众所周知的应用(好吧,要使其成为真正的DI,您应该将func 放入不同的类)。为了使您的代码更加灵活,并将插件的创建与它们的使用分离,您还可以使用例如Factory MethodBuilder

    【讨论】:

      【解决方案4】:

      好的,谢谢大家的帮助。

      我接受了您的所有建议,并做了以下事情:

       namespace Plugins
      {
      
      
      
      
       public class plugin1 : Iplugin
        {
          void doSomthing(string _str)
          {
              if (_str == "1")
              {
                  // do somthing 1
              }
          }
      
      
      }
      
      public class plugin2 : Iplugin
      {
          void doSomthing(string _str)
          {
              if (_str == "2")
              {
                  // do somthing 2
              }
          }
      
      
      }
      
      public class plugin3 : Iplugin
      {
          void doSomthing(string _str)
          {
              if (_str == "3")
              {
                  // do somthing 3
              }
          }
      
      
       }
      }
      

      所以这是包含所有插件的名称空间

      现在在主应用中

      namespace Factory
      {
        public interface Iplugin
        {
      
          void doSomthing(string _str);
        }
      
      
      
      class Program
      {
          static void Main(string[] args)
          {
              string @namespace = "Plugins";
      
              var q = from t in Assembly.GetExecutingAssembly().GetTypes()
                      where t.IsClass && t.Namespace == @namespace
                      select t;
              q.ToList();
      
              List<Iplugin> myList = new List<Iplugin>();
              foreach (var item in q)
              {
                  Iplugin temp=(Iplugin)Activator.CreateInstance(item);
                  myList.Add(temp);// a list with all my plugins
      
              }
      
              foreach (var item in myList)
              {
                 item.doSomthing("string");
      
              }
      
          }
      }
      

      }

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-02-09
        • 1970-01-01
        • 2014-06-01
        • 1970-01-01
        • 2012-05-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多