#c_sharp #ооп #наследование #generics
Здравствуйте. Прошу вашего совета вот по какому вопросу. Уменя есть акая иерархия классов enum SomeEnum { First, Second } abstract class BaseClass { private SomeEnum ActionId { get; set; } protected BaseClass(SomeEnum actionId) { ActionId = actionId; } public static T GetById(SomeEnum actionId) where T : BaseClass { switch(actionId) { case SomeEnum.First: return new DerivedFirst(actionId) as T; case SomeEnum.Second: return new DerivedSecond(actionId) as T; default: throw new ArgumentException(); } } } class DerivedFirst : BaseClass { public DerivedFirst(SomeEnum actionId) : base(actionId) { } } class DerivedSecond : BaseClass { public DerivedSecond(SomeEnum actionId) : base(actionId) { } } Проблема в том, что в абстрактном классе BaseClass есть статический метод, который возвращает экземпляр класса DerivedFirst или DerivedSecond в зависимости от типа параметра actionId. Мне кажется что я написал плохое решение. Может вы подскажете что-то получше, ничего в голову не приходит((
Ответы
Ответ 1
(Обновляю ответ в связи с изменившимся условием.) class ActionFactory { static Dictionary> activators = new Dictionary >() { { SomeEnum.First, () => new DerivedFirst(SomeEnum.First) }, { SomeEnum.Second, () => new DerivedSecond(SomeEnum.Second) } }; public static BaseClass Create(SomeEnum actionId) { Func activator; if (!activators.TryGetValue(actionId, out activator)) throw new ArgumentException("Unsupported action id", "actionId"); return activator(); } } Нет никакого смысла делать Create генерик-функцией, так как это требует знания типа возвращаемого значения на этапе компиляции, а если этот тип известен, вы можете непосредственно вызвать конструктор. Ответ 2
Не знаю, почему никто не обратил особого внимания на присутствие "духа" паттерна Factory в программном коде. Если вам не нравится ваш код, то, возможно, вы не ошибаетесь. Я решил переписать ваш код в семантике Aбстрактной фабрики: ... enum SomeEnum{First,Second} abstract class Factory { private SomeEnum ActionId; protected Factory(SomeEnum actionId) { ActionId = actionId; } public static Factory GetById(SomeEnum actionId) { switch (actionId) { case SomeEnum.First: return new FactoryFirst(actionId); case SomeEnum.Second: return new FactorySecond(actionId); default: return new FactoryException(actionId); } } } /* Здесь мы определяем сущности "фабрики"( что она может выпускать ) */ class FactoryFirst : Factory { public FactoryFirst(SomeEnum actionId) : base(actionId) { } } class FactorySecond : Factory { public FactorySecond(SomeEnum actionId) : base(actionId) { } } class FactoryException : Factory { public FactoryException(SomeEnum actionId) : base(actionId) { MessageBox.Show("Not Valid ActionId!"); } } ... Использование: ... Factory factoryFirst = Factory.GetById(SomeEnum.First); Factory factorySecond = Factory.GetById(SomeEnum.First); ... Очевидно, что весь интерфейс "фабричных объектов" теперь должен описываться в родительском абстрактном классе.Ответ 3
Во-первых, статический метод скорее всего должен быть объявлен как возвращающий базовый класс. Иначе, использующий код всё равно должен знать, объект какого типа он хочет получить (и указать это в generic-параметре), что делает наличие метода не слишком осмысленным - почему бы просто не вызвать нужный конструктор? С другой стророны, такой вариант тоже возможен, если этот метод делает некую значительную работу по инициализации (в которой и заключается его цель - в инициалитзации, не в создании), чтобы избежать необходимости дополнительного приведения типов. Однако, в таком случае параметр enum'а является избыточным, поскольку достаточно generic-типа. Кстати, в последнем случае я бы передавал уже созданный объект методу, инициализировал и возвращал его же. Тогда метод останется generic, но тип уже сможет вывести компилятор. Далее, про паттерн фабрики тут уже сказали, хотя не знаю, нужен ли он тебе. Но есть ещё одна мысль - то, что иногда называют smart enum. Есть базовый класс и заранее известные его потомки, которые используются внутри класса, а снаружи от них требуются только методы базового класса. В таком случае эти классы имеет смысл поместить внутрь базового и сделать private. Опять же, метод создания должен быть объявлен как возвращающий базовый класс.
Комментариев нет:
Отправить комментарий