У нас есть табличка в базе данных с поставщиками, каждому поставщику соответствует класс обработчика, который является наследником от базового класса. Надо сделать генератор для создания обработчиков.
В голову пришло три способа организации:
switch .. case
Добавить в табличку столбец и туда поместить постфиксы, дальше создавать объект на осное имени наследника, который должен включать этот постфикс
Поместить индекс в атрибут класса-наследника и создавать объект, используя это значение
Первый способ:
public class parcerA: ParcerBace {}
public class parcerB: ParcerBace {}
public abstract class ParcerBace
{
switch (id)
{
case 1: return new parcerA();
case 2: return new parcerB();
}
return null;
}
Дешево и сердито, но если этих обработчиков много, то мы получаем длинную простыню
Второй способ:
public class parcerA: ParcerBace {}
public class parcerB: ParcerBace {}
public abstract class ParcerBace
{
public static ParcerBace creator(string postfix)
{
Type tFind = typeof(ParcerBace);
Type tR = Assembly.GetAssembly(tFind).GetTypes().FirstOrDefault(t => t.IsSubclassOf(tFind) && t.Name== "parcer" + postfix);
if (tR != null) return (ParcerBace)Activator.CreateInstance(tR);
return null;
}
}
Тут довольно короткий код, но требуется следить за соответствием этих постфиксов в базе и наименованием объектов, что порождает ошибки
Третий способ:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class IDAttribute : Attribute
{
public IDAttribute(int id)
{
this.id = id;
}
public int id { get; set; }
}
[ID(1)]
public class parcerA :ParcerBace {}
[ID(2)]
public class parcerB : ParcerBace {}
public abstract class ParcerBace
{
public static ParcerBace creator(int id)
{
Type tFind = typeof(ParcerBace);
Type tR = Assembly.GetAssembly(tFind).GetTypes().FirstOrDefault(t => t.IsSubclassOf(tFind) && (t.GetCustomAttribute(typeof(IDAttribute)) as IDAttribute).id == id);
if (tR != null) return (ParcerBace)Activator.CreateInstance(tR);
return null;
}
}
А теперь сравним производительность указанных способов:, создадим 1 000 000 объектов каждым из способов и сравним время. Очевидно, что от количества наследников время тоже будет зависеть
5 наследников
способ 00:00.0249570
способ 00:01.5358061
способ 00:10.4656699
10 наследников
способ 00:00.0433666
способ 00:01.7637981
способ 00:11.0782414
Улучшение способа 2 и 3 на основе обсуждения
Привожу код для поиска по именам, при поиске по идентификаторам, спрятанным в атрибуты делаем аналогичный перенос
public abstract class ParcerBace
{
static Dictionary<string, Type> parcers;
static ParcerBace()
{
Type tFind = typeof(ParcerBace);
parcers = Assembly.GetAssembly(tFind).GetTypes().Where(t => t.IsSubclassOf(tFind) && t.Name.StartsWith("parcer")).ToDictionary(t => t.Name.Replace("parcer", ""));
}
public static ParcerBace creator(string postfix)
{
Type tR;
parcers.TryGetValue(postfix, out tR);
if (tR != null) return (ParcerBace)Activator.CreateInstance(tR);
return null;
}
}
Если предполагается много вызовов, то это даст значительный рост производительности
2. способ 10 наследников 1000000 вызовов 00:00.1450798
3. способ 10 наследников 1000000 вызовов 00:00.1883757
Комментарии (6)
doctorw
09.09.2024 18:10+3Зачем наследовать от одного базового класса? Тут же банальная абстрактная фабрика.
xFFFF
09.09.2024 18:10Какое-то очень геморройное решение. Я бы сделал обработку через Стратегию, ну либо Абстрактная фабрика. Либо можно реализовать воркфлоу со скриптами, чтобы не перекомпилировать каждый раз.
ncix
09.09.2024 18:10+1Похоже, вы на пути изобретения паттерна Фабрика, но путь еще до конца не пройден.
ALogachev
Гуру поправят, но имхо если звать
Assembly.GetAssembly(tFind).GetTypes().FirstOrDefault(t => t.IsSubclassOf(tFind) && t.Name== "parcer" + postfix);
или
Type tR = Assembly.GetAssembly(tFind).GetTypes().FirstOrDefault(t => t.IsSubclassOf(tFind) && (t.GetCustomAttribute(typeof(IDAttribute)) as IDAttribute).id == id);
на каждый чих - в с случае миллиардов записей получится очень долго... как минимум хочется закешировать нужные типы по ид в Dictionary или Хештаблице
SOProger Автор
Само собой, если требуется высокая производительность, то первый способ будет быстрее и без создания дополнительных сложностей с кэшированием
Если же задача загрузить обработчик в зависимости от выбранного пользователем поставщика. То получаем разовую подгрузку, что не скажется негативно на производительности
SOProger Автор
Добавил в статью проверку производительности и хеширование в словаре, спасибо