Как зайти в Даркнет?!
25th January, 01:11
8
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
899
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
952
0
Очень долго работает Update запрос Oracle
27th January, 09:58
916
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
907
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
942
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1727
0
период по дням
25th October, 10:44
3957
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3722
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4614
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4382
0
Помогите пожалуйста решить задачи
24th November, 23:53
6087
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4352
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4400
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Абстрактный Шаблон Дизайна Фабрики
Я работаю над внутренним проектом для своей компании, и часть проекта заключается в том, чтобы иметь возможность анализировать различные "Tasks" из файла XML в коллекцию задач, которые будут выполняться позже.
Поскольку каждый тип задачи имеет множество различных связанных полей, я решил, что лучше всего представлять каждый тип задачи с отдельным классом.
Для этого я построил абстрактный базовый класс:
public abstract class Task
{
public enum TaskType
{
// Types of Tasks
}
public abstract TaskType Type
{
get;
}
public abstract LoadFromXml(XmlElement task);
public abstract XmlElement CreateXml(XmlDocument currentDoc);
}
Каждая задача наследуется от этого базового класса и включает в себя код, необходимый для создания себя из переданного в XmlElement, а также сериализации себя обратно в XmlElement.
Основные примеры:
public class MergeTask : Task
{
public override TaskType Type
{
get { return TaskType.Merge; }
}
// Lots of Properties / Methods for this Task
public MergeTask (XmlElement elem)
{
this.LoadFromXml(elem);
}
public override LoadFromXml(XmlElement task)
{
// Populates this Task from the Xml.
}
public override XmlElement CreateXml(XmlDocument currentDoc)
{
// Serializes this class back to xml.
}
}
Затем синтаксический анализатор будет использовать код, подобный этому, чтобы создать коллекцию задач:
XmlNode taskNode = parent.SelectNode("tasks");
TaskFactory tf = new TaskFactory();
foreach (XmlNode task in taskNode.ChildNodes)
{
// Since XmlComments etc will show up
if (task is XmlElement)
{
tasks.Add(tf.CreateTask(task as XmlElement));
}
}
Все это прекрасно работает и позволяет мне передавать задачи по кругу, используя базовый класс, сохраняя при этом структуру наличия отдельных классов для каждой задачи.
Однако я не доволен своим кодом для TaskFactory.CreateTask. Этот метод принимает значение XmlElement, а затем возвращает экземпляр соответствующего класса задач:
public Task CreateTask(XmlElement elem)
{
if (elem != null)
{
switch(elem.Name)
{
case "merge":
return new MergeTask(elem);
default:
throw new ArgumentException("Invalid Task");
}
}
}
Поскольку мне нужно разобрать XMLElement, я использую огромный (10-15 случаев в реальном коде) переключатель, чтобы выбрать, какой дочерний класс создать. Я надеюсь, что есть какой-то полиморфный трюк, который я могу сделать здесь, чтобы очистить этот метод.
Какой-нибудь совет?
Для этого я использую рефлексию. Вы можете создать фабрику, которая в основном расширяется без необходимости добавлять какой-либо дополнительный код.
убедитесь, что у вас есть "using System.Reflection", поместите следующий код в свой метод создания экземпляра.
public Task CreateTask(XmlElement elem)
{
if (elem != null)
{
try
{
Assembly a = typeof(Task).Assembly
string type = string.Format("{0}.{1}Task",typeof(Task).Namespace,elem.Name);
//this is only here, so that if that type doesn't exist, this method
//throws an exception
Type t = a.GetType(type, true, true);
return a.CreateInstance(type, true) as Task;
}
catch(System.Exception)
{
throw new ArgumentException("Invalid Task");
}
}
}
Еще одно наблюдение заключается в том, что вы можете сделать этот метод статическим и повесить его в классе задач, чтобы вам не пришлось заново создавать TaskFactory, а также сохранить себе движущуюся часть для поддержания.
Создайте экземпляр "Prototype" каждого класса и поместите их в хэш-таблицу внутри фабрики , используя строку, которую вы ожидаете в XML в качестве ключа.
так что CreateTask просто находит правильный прототип объекта, по get() ing из хеш-таблицы.
тогда позвоните на него LoadFromXML.
вы должны предварительно загрузить классы в хеш-таблицу,
Если вы хотите, чтобы это было более автоматическим...
Вы можете создать классы "self-registering", вызвав метод статического регистра на фабрике.
Поместите вызовы для регистрации (с конструкторами) в статические блоки на подклассах задач. Тогда все, что вам нужно сделать, это "mention" классы, чтобы запустить статические блоки.
Тогда статического массива подклассов задач будет достаточно для "mention" из них. Или используйте отражение, чтобы упомянуть классы.
Как вы относитесь к инъекции зависимости? Я использую Ninject, и поддержка контекстной привязки в нем идеально подходит для этой ситуации. Посмотрите на эту запись в блоге о том, как можно использовать контекстную привязку при создании контроллеров с IControllerFactory, когда они запрашиваются. Это должно быть хорошим ресурсом о том, как использовать его для вашей ситуации.
@jholland
Я не думаю, что тип enum необходим, потому что я всегда могу сделать что-то вроде этого:
Перечисление?
Я признаю,что это кажется мне банальным. Сначала отражение кажется грязным, но как только вы приручите зверя, вы будете наслаждаться тем, что он позволяет вам делать. (Помните рекурсию, она кажется грязной, но это хорошо)
Хитрость заключается в том, чтобы понять, что вы анализируете метаданные, в данном случае строку, предоставленную из xml, и превращаете ее в поведение во время выполнения. Вот в чем лучше всего проявляется рефлексия.
BTW: оператор is-это тоже отражение.
http://en.wikipedia.org/wiki/Reflection_(вычислительная_техника)#использует
@Tim, я в конечном итоге использовал упрощенную версию вашего подхода и ChanChans, вот код:
public class TaskFactory
{
private Dictionary<String, Type> _taskTypes = new Dictionary<String, Type>();
public TaskFactory()
{
// Preload the Task Types into a dictionary so we can look them up later
foreach (Type type in typeof(TaskFactory).Assembly.GetTypes())
{
if (type.IsSubclassOf(typeof(CCTask)))
{
_taskTypes[type.Name.ToLower()] = type;
}
}
}
public CCTask CreateTask(XmlElement task)
{
if (task != null)
{
string taskName = task.Name;
taskName = taskName.ToLower() + "task";
// If the Type information is in our Dictionary, instantiate a new instance of that task
Type taskType;
if (_taskTypes.TryGetValue(taskName, out taskType))
{
return (CCTask)Activator.CreateInstance(taskType, task);
}
else
{
throw new ArgumentException("Unrecognized Task:" + task.Name);
}
}
else
{
return null;
}
}
}
@ChanChan
Мне нравится идея отражения, но в то же время я всегда стеснялся использовать отражение. Это всегда поражало меня как "hack", чтобы обойти что-то, что должно быть проще. Я действительно рассматривал этот подход, а затем решил, что оператор switch будет быстрее для того же количества запаха кода.
Вы действительно заставили меня думать, что я не думаю, что перечисление типа необходимо, потому что я всегда могу сделать что-то вроде этого:
if (CurrentTask is MergeTask)
{
// Do Something Specific to MergeTask
}
Возможно, мне следует снова открыть свою книгу GoF Design Patterns, но я действительно думал, что есть способ полиморфно создать правильный класс.
@Dale
Я не рассматривал nInject близко, но из моего высокого уровня понимания инъекции зависимостей я полагаю, что это будет выполнять то же самое, что и ChanChans предложение, только с большим количеством слоев cruft (er абстракции).
В единичной ситуации, когда мне просто нужно это здесь, я думаю, что использование какого-то ручного кода отражения-это лучший подход, чем иметь дополнительную библиотеку для связи и вызывать ее только в одном месте...
Но, может быть, я не понимаю, какое преимущество дает мне здесь nInject.
Некоторые фреймворки могут полагаться на отражение там, где это необходимо, но большую часть времени вы используете boot - strapper, если хотите, чтобы настроить, что делать, когда экземпляр объекта необходим. Это обычно хранится в универсальном словаре. Я использовал свой собственный до недавнего времени, когда я начал использовать Ninject.
С Ninject, главное, что мне понравилось в нем, это то, что когда ему нужно использовать отражение, он этого не делает. вместо этого он использует возможности генерации кода .NET, которые делают его невероятно быстрым. Если вы чувствуете, что отражение будет быстрее в контексте, который вы используете, это также позволяет настроить его таким образом.
Я знаю, что это может быть излишним для того, что вам нужно в данный момент, но я просто хотел указать на инъекцию зависимости и дать вам пищу для размышлений на будущее. Посетите dojo для получения урока.