Смотря как реализовать простой полиморфизм
У меня очень основной вопрос (я не знаю, почему я не могу думать прямо).
Я пытаюсь сделать некоторый полиморфизм.
У меня интерфейс выглядит так:
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
}
}
}
Итак, у меня есть основной класс, и я хотел бы вызвать плагин all
НО я хотел бы сохранить OCP (принцип Open-Closed), поэтому, если я добавлю в будущем еще один класс плагинов, основной класс не изменится.
Это основной класс
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
}
}
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");
}
}
}
}
Конечно, для создания конкретного Iplugin
вам нужно знать тип реализации. Посмотрите на фабричный образец.
Для подобных случаев мне нравится использовать 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;
}
Вы можете добавить коллекцию для хранения ваших плагинов. Коллекция может быть заполнена в одном месте, а затем передана другому методу, который просто перебирает все плагины и вызывает их. Таким образом, он полностью не зависит от того, какие типы плагинов есть в коллекции.
И как уже упоминалось @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 Method или Builder.