Здравствуйте, valmond, Вы писали:
V>Подскажите V>Можно ли каким-то более менее красивым способом устроить историчность реализаций метода.
V>Практическая проблема в том, что для расчета в частности налогов время от времени меняется алгоритм. И необходимо "помнить" о всех версиях и для нужного интервала в прошлом использовать нужную реализацию.
Пиши отдельную реализацию для каждого нового алгоритма. Пусть у всех будет общий интерфейс. Создай менеджер, который будет возвращать (использовать) необходимую реализацию в зависимости от временного интервала.
Как мапить интервал на реализацию... ну, по-моему атрибуты тебе помогут
Здравствуйте, valmond, Вы писали:
V>Зд O>>Работает, конечно... и получаем тучу hard-coded if-ов или case-ов... вот затем и reflection.
V>Вот именно. Как сделать с кейсами и так понятно. Но уж очень не хочется.
Ок. Тогда простой пример, разбирайся (я его не компилил, но должен работать; в любом случае идея понятна):
// Твой интерфейсinterface IValueCalculator
{
int CalculateValue();
}
// Реализации. На каждую нацеплен твой атрибут
[Year(2004)]
class OneCalculator : IValueCalculator
{
public int CalculateValue()
{
return 1;
}
}
[Year(2005)]
class TwoCalculator : IValueCalculator
{
public int CalculateValue()
{
return 2;
}
}
// Определение своего атрибутаclass YearAttribute : Attribute
{
int _year;
public YearAttribute(int year)
{
_year = year;
}
public int Year {
get { return _year; }
}
}
// Определение фабрикиpublic class CalculatorFactory
{
static Hashtable _calculatorsHash;
static CalculatorFactory()
{
// В статическом конструкторе собираем все типы,
// реализующие IValueCalculator и имеющие YearAttribute
_calculatorsHash = new Hashtable();
// Обходим все типы сборки
Type[] assemblyTypes = Assembly.GetExecutingAssembly().GetTypes();
for (int i = 0; i < assemblyTypes.Length; ++i) {
Type type = assemblyTypes[i];
// Проверяем, реализует ли тип IValueCalculatorif (typeof(IValueCalculator).IsAssignableFrom(type)) {
// Проверяем, есть ли у типа YearAttribute
YearAttribute attribute = (YearAttribute)Attribute.GetCustomAttribute(type, typeof(YearAttribute));
if (attribute != null) {
// Сохраняем тип в хэш (а потом будем создавать)
_calculatorsHash[attribute.Year] = type;
}
}
}
}
static public IValueCalculator GetCalculator(int year)
{
Type calculatorType = (Type)_calculatorsHash[year];
if (calculatorType == null) return null;
// Создаём и возвращаем instance нужного калькулятораreturn Activator.CreateInstance(calculatorType);
}
}
Здравствуйте, valmond, Вы писали:
V>Подскажите V>Можно ли каким-то более менее красивым способом устроить историчность реализаций метода.
V>Практическая проблема в том, что для расчета в частности налогов время от времени меняется алгоритм. И необходимо "помнить" о всех версиях и для нужного интервала в прошлом использовать нужную реализацию.
Здравствуйте, Corwin_XX, Вы писали:
C_X>Я не совсем понимаю, зачем здесь рефлексия. Возможно, я не донца понял суть проблемы.
C_X>Алгоритм — это функция. Может быть функция значением переменной? Может! Я бы использовал делегаты.
Работает, конечно... и получаем тучу hard-coded if-ов или case-ов... вот затем и reflection.
Подскажите
Можно ли каким-то более менее красивым способом устроить историчность реализаций метода.
Практическая проблема в том, что для расчета в частности налогов время от времени меняется алгоритм. И необходимо "помнить" о всех версиях и для нужного интервала в прошлом использовать нужную реализацию.
22.04.05 23:15: Перенесено модератором из '.NET' — AndrewVK
O>Пиши отдельную реализацию для каждого нового алгоритма. Пусть у всех будет общий интерфейс. Создай менеджер, который будет возвращать (использовать) необходимую реализацию в зависимости от временного интервала.
Т.е. это примерно фабричный метод получается?
O>Как мапить интервал на реализацию... ну, по-моему атрибуты тебе помогут
ну я тоже сразу о них подумал. Может подскажешь чуть чуть еще? Никогда свои аттрибуты толком не использовал. Логику их работы не до конца понимаю.
S>> Попробоуй применить паттерн S>> [url=http://www.dofactory.com/Patterns/PatternStrategy.aspx]Strategy[/u S>> rl].
v> Ок, согласен. v> Но каким образом определять какую именно стратегию применять? v> кроме case-ов какие-то варианты могут быть?
Можно составить табличку Дата начала\Дата конца -> ConcreteStrategy и юзать ее. Табличку можно держать в БД, конфиге или прям в коде набить.
GIV>> Можно составить табличку Дата начала\Дата конца -> ConcreteStrategy и GIV>> юзать ее. Табличку можно держать в БД, конфиге или прям в коде GIV>> набить.
v> Угу, вариант. А создавать конкретную стратегию через рефлексию. Так?
Да. В конечном счете через рефлексию.
Posted via RSDN NNTP Server 1.9
WBR, Igor Evgrafov
Re[5]: Историчность методов
От:
Аноним
Дата:
22.04.05 11:38
Оценка:
Я не совсем понимаю, зачем здесь рефлексия. Возможно, я не донца понял суть проблемы.
Алгоритм — это функция. Может быть функция значением переменной? Может! Я бы использовал делегаты.
Работает, конечно... и получаем тучу hard-coded if-ов или case-ов... вот затем и reflection.
Не понимаю, а чем отличается моё:
"напиши метод, который будет возвращать нужный делегат в зависимости от временного интервала"
от Вашего:
"Создай менеджер, который будет возвращать (использовать) необходимую реализацию в зависимости от временного интервала."
Чем Вам мешают эти кейсы? Спрячьте их в одельный класс и открывайте его код только чтобы добавить ещё один кейс, когда очередной раз изменится законодательство.
По возможности я стараюсь не использовать reflection (а если и использую, то стараюсь минимизировать временные затраты — кеширую, например). Например, если у тебя всего 3 реализации, то хватит и if/switch. Но если их много или в будущем будут ещё добавляться, рефлекшн с атрибутами могут стать хорошим подспорьем. Например, при создании новой реализации достаточно будет написать класс, реализующий интерфейс, и навесить на него атрибут (не придётся лезть в дебри какого-нить ужасного switch-а ).
Здравствуйте, Oyster, Вы писали:
O>По возможности я стараюсь не использовать reflection (а если и использую, то стараюсь минимизировать временные затраты — кеширую, например). Например, если у тебя всего 3 реализации, то хватит и if/switch. Но если их много или в будущем будут ещё добавляться, рефлекшн с атрибутами могут стать хорошим подспорьем. Например, при создании новой реализации достаточно будет написать класс, реализующий интерфейс, и навесить на него атрибут (не придётся лезть в дебри какого-нить ужасного switch-а ).
Мне кажется тут тот случай когда аккуратность кода стоит выше чем возможные потери на рефлексию.
С рефлексией я смогу разнести все классы по разным файлам, например. И вообще могу забыть о фабрике и о всех старых реализациях в момент добавления новой.
Здравствуйте, Corwin_XX, Вы писали:
C_X>Oyster
C_X>Работает, конечно... и получаем тучу hard-coded if-ов или case-ов... вот затем и reflection. C_X> C_X>Не понимаю, а чем отличается моё: C_X>"напиши метод, который будет возвращать нужный делегат в зависимости от временного интервала" C_X>от Вашего: C_X>"Создай менеджер, который будет возвращать (использовать) необходимую реализацию в зависимости от временного интервала."
C_X>(с точки зрения количества if'ов)
Здравствуйте, <Аноним>, Вы писали:
А>Чем Вам мешают эти кейсы? Спрячьте их в одельный класс и открывайте его код только чтобы добавить ещё один кейс, когда очередной раз изменится законодательство.
Иногда законодательство меняется слишком часто.
Re[10]: PS
От:
Аноним
Дата:
22.04.05 13:18
Оценка:
valmond
С рефлексией я смогу разнести все классы по разным файлам, например.
А без рефлексии этого сделать нельзя?
И вообще могу забыть о фабрике и о всех старых реализациях в момент добавления новой.
Здравствуйте, Corwin_XX, Вы писали:
C_X>Чем Вам мешают эти кейсы? Спрячьте их в одельный класс и открывайте его код только чтобы добавить ещё один кейс, когда очередной раз изменится законодательство.
Вы наверное не видели во что вырастают кейсы через некоторое время.
Я увидел и волосы встали дыбом.
Здравствуйте, Corwin_XX, Вы писали:
C_X>Oyster
C_X>Я не против использования атрибутов. Я не понимаю, зачем нужно: C_X>Activator.CreateInstance(calculatorType)
Чтобы создать инстанс по типу в случае если нам нужен новый инстанс каждый раз (да, бывает и так). На самом деле можно в Hashtable и instance держать — ведь я написал всего лишь пример...
Re[8]: Историчность методов
От:
Аноним
Дата:
22.04.05 14:05
Оценка:
valmond
На самом деле вопрос разбивается на три вопроса.
1. Использовать Activator.CreateInstance или использовать delegate
Единственная разница в том, что в первом случае при изменении законодательства придётся добавлять новый класс, а во втором случае — новый метод. При желании, можно каждый раз выделять этот метод в отдельный класс. Тогда с точки зрения изменений в коде при изменениях законодательства не будет никакой разницы.
Зачем использовать Activator.CreateInstance, если можно обойтись без него?
Обратите внимание, на количество Case'ов это никак не повлияет.
2. Использовать атрибуты или не использовать.
Единственная разница в том, что в первом случае при изменении законодательства нужно будет добавлять перед классом/методом строку:
[Year(2004)]
Во втором случае нужно будет добавить в некий метод строку:
_calculatorsHash[2004] = new GetNalogDelegate(GetNalog2004);
Не вижу большой разницы. (Ведь в том методе нет ничего кроме таких вот строчек — запутаться негде.
3. Использовать Hashtable или Switch
Разница в добавляемой в тот самый метод (см. пункт 2) строке. Во втором случае она будет выглядеть так:
case 2004:
res = new GetNalogDelegate(GetNalog2004);
break;
Опять же не вижу принципиальной разницы. Ведь этот метод содержит один единственный Switch и ничего более.
(Фактически, он делает то же самое, что и Hashtable — возвращает значение по индексу.)
Разница в том, что при указанном мной подходе тебе придётся модифицировать только 1 класс, а это большое преимущество (предщставь себе 2000 классов. Говорите — нереально? А вот и нет...).
Кроме того, при незначительной модификации кода можно будет добавить возможность размещения конкретных реализаций в другой сборке. С твоими "поправками" это невозможно.
В общем, я привёл пример реализации. Заметил, что не всегда имеет смысл так извращаться и использовать атрибуты/рефлекшн. И согласен, что иногда старый добрый switch с делегатами будет проще и эффективнее. Засим считаю тему закрытой.
Здравствуйте, valmond, Вы писали:
V>Подскажите V>Можно ли каким-то более менее красивым способом устроить историчность реализаций метода.
V>Практическая проблема в том, что для расчета в частности налогов время от времени меняется алгоритм. И необходимо "помнить" о всех версиях и для нужного интервала в прошлом использовать нужную реализацию.
Решение этой проблемы в общем случае — "динамический полиморфизм". У нас даже докторскую из этого вроде делать собираются. Кандидатская уже по крайней мере есть
N>Решение этой проблемы в общем случае — "динамический полиморфизм". У нас даже докторскую из этого вроде делать собираются. Кандидатская уже по крайней мере есть
Интересное, а практическое применение на языках .Net у этих знаний есть?