|
|
|
|
Статья описывает универсальную фабрику объектов, требующую минимального изменения кода при добавлении нового класса в список создаваемых экземпляров
автор: Астраханцев Дмитрий ВведениеКогда возникает необходимость создавать экземпляры-потомки базового класса по некоторым идентификаторам часто обращаются к т.н. "фабрикам", представляющим собой чаще всего статические классы, создающие в соответствии с некоторыми правилами требуемые экземпляры. Правила описываются в текстовом виде (чаще всего xml представление, где внешнему идентификатору соответствует имя класса создаваемого экземпляра) либо числовом виде, где по каждому внешнему числовому идентификатору фабрика выполняет код, создающий соответствующий экземпляр. Использование фабрикНиже приведены примеры фабрики, использующей в качестве идентификаторов имена классов, описываемые в xml виде и числовой фабрики, где каждому внешнему числовому идентификатору соответствует некоторый создаваемый экземпляр. public class Factory { public static BaseClass CreateObject(string name) { BaseObect newInstance = (BaseObect) System.Activator.CreateInstance(System.Reflection.Assembly.GetExecutingAssembly().FullName, name); public class Factory { public static BaseClass CreateObject(int id) { BaseObect newInstance = null; switch(id) { case (id_FirstChild) newInstance = new FirstChild(); break; case (id_SecondChild) newInstance = new SecondChild(); break; ... } return newInstance; } } ... int id = GetNextElementId(someSource); BaseClass instance = Factory.CreateObject(id); Все возможные реализации фабрики содержат следующие неудобные моменты:
РеализацияВ результате поиска было найдено решение, позволяющее обойти эти недостатки. Стоит заметить, что в новом варианте реализации фабрики несколько усложнилось понимание логики работы, а также несколько возросло использование памяти и ресурсов цпу. Также, в предлагаемом решении предполагается, что новый экземпляр будет инициализироваться после создания, конструктор же вызывается без параметров. Но эти недостатки покрываются гораздо меньшим временем разработки приложения, так как приходится изменять код только в одном месте - в самом классе экземпляра потомка. Это особенно заметно, когда количество экземпляров-потомков (и их идентификаторов) является большим и не спроектировано заранее. Для реализации требуемой логики потребуется в статическом конструкторе базового класса создать хэш-таблицу, ключами которой будут являться идентификаторы потомков, а значениями - тип соответствующего экземпляра; каждый класс потомка должен реализовать статическое поле определенного типа, возвращающее идентификатор класса. Производя анализ этого поля у всех производных и не абстрактных классов, можно в базовом классе построить необходимую таблицу соответствий - идентификатор-тип потомка. Данная таблица используется в статическом методе - конструкторе экземпляров, который по идентификатору возвращает созданный объект. Рассмотрим пример реализации универсальной фабрики: Нумератор - требуется для четкого сопоставление значения идентификатора и его типа (!): public enum ObjectTypes : byte { FirstObjectType = 0, SecondObjectType = 1, ThirdObjectType = 2, ... } Статическая функция, определяющая является ли данный тип класса потомком от заданного: static bool isChild(System.Type thisClass, System.Type ofClass) { if(thisClass.BaseType==ofClass) { return true; } if((thisClass.BaseType==null)||(thisClass.BaseType==typeof(System.Object))) return false; else return isChild(thisClass.BaseType, ofClass); } Хэш-таблицы, которые содержат требуемую индексацию: static System.Collections.Hashtable typesEnumerator; static System.Collections.Hashtable instancesEnumerator; Инициализация хэш-таблиц соответствий, вызываемая из статического конструктора базового (для потомков) класса: System.Type baseType = typeof(BaseClass); System.Collections.ArrayList childTypes = new System.Collections.ArrayList(50); System.Type[] allTypes = baseType.Assembly.GetTypes(); for(int i=0; i Статический метод, который создает новый экземпляр по его идентификатору: public static BaseClass CreateInstance(ObjectTypes id) { System.Type typeOfInstance = instancesEnumerator[id] as System.Type; if(typeOfInstance==null) throw new System.Exception("Wrong ID of Child Class"); BaseClass newInstance = (BaseClass) System.Activator.CreateInstance(typeOfInstance, null); return newInstance; } Все, что необходимо сделать в классе-потомке, это определить статическую функцию (с любым именем), возвращающую его идентификатор: public class FirstObject : BaseClass { public static ObjectTypes GetInternalType() { return ObjectTypes.FirstObjectType; } ... } Приведенный код легко обфусцируется и имеет достаточно высокие скоростные характеристики, т.к. при создании экземпляра необходим лишь индексированный поиск его типа в хэш-таблице. В примере также приведена обратная хэш-таблица, в которой ключом является сам тип экземпляра, а значением - его идентификатор. ЗаключениеВ рассмотренной статье описан способ, позволяющий создать простую и универсальную конструкцию для получения экземпляров объектов по их идентификаторам. Приведенная реализация существенно облегчает процесс добавления новых объектов в систему, экономя время создания новых классов и не требуя изменения кода фабрики. При необходимости, логика работы фабрики может быть легко расширена. Например, если требуется счетчик объектов или инициализация объекта на этапе создания, т.е. использование конструкторов с параметрами и тому подобное. Приведенный в статье исходный код (C#) с легкостью может быть переведен на другие языки высокого уровня .Net. Литература
|


