Замените переключатель Enum с шаблоном проектирования (IOC)
У меня есть обработчик событий, который получает объект eventargs внутри, который является перечисляемым значением, которое дополнительно уточняет информацию внутри. Это выглядит примерно так
public enum StatusCallbackType { Status1, Status2, Status3, Status4 }
public class StatusEventArgs
{
public StatusCallbackType Type {get;set;}
public StatusData Data {get; set;}
}
Statusdata - это базовый абстрактный класс, который изменяется в зависимости от заданного CalllbackType.
Теперь код, который обрабатывает событие, выглядит как
switch e.Type
{
case Status1:
DoAction1(e.Data as Opt1Data);
break;
case Status2:
DoAction1(e.Data as Opt2Data);
break;
case Status3:
DoAction1(e.Data as Opt3Data);
break;
}
Проблема в том, что если мы меняем StatusNotifications или хотим изменить способ их обработки, нам нужно настроить переключатель, который может стать действительно большим, и я подумывал об использовании чего-то, что позволило бы мне добавить обработчик для любого данного статуса.
С другой стороны, мне это сейчас не нужно. Решения для коммутаторов работают, но они начинают набирать обороты, поэтому я борюсь между своим ощущением, что это не ремонт и YAGNI.
Есть ли шаблон, который позволяет конвертировать такие переключатели в шаблон IOC? Как вы думаете, мне стоит заняться рефакторингом этого переключателя?
3 ответа
Я думаю, что вы можете рефакторинг по схеме цепочки ответственности. Каждый оператор switch становится объектом-обработчиком в коллекции, которая либо обрабатывает уведомление, либо уведомление передается следующему.
Что касается внедрения, вы сможете изменять количество обработчиков без изменения клиентского кода и вставлять макеты / заглушки для тестирования.
Чтобы ответить, следует ли вам рефакторинг, нет ничего плохого в самом операторе switch, если он появляется только один раз и остается простым. Вы захотите провести рефакторинг, если начнете замечать дублирование или другие запахи кода, которые можно исправить с помощью шаблона.
Я хотел бы использовать что-то вроде шаблона Abstract Factory. Это очень полезно по отношению к IoC, потому что оно может инкапсулировать сложную логику, которая создает другие зависимости. Затем вы можете внедрить свою абстрактную фабрику с помощью инструмента IoC и избавиться от оператора switch.
Я бы использовал что-то вроде этого, возможно:
public interface IStatusHandler
{
bool Handle(StatusData statusData);
}
public class Status1Handler : IStatusHandler
{
public bool Handle(StatusData statusData)
{
if (statusData as Opt1Data == null) return false; // you don't need Type anymore
...
return true;
}
}
public class StatusManager
{
private IList<IStatusHandler> handlers;
public StatusManager(IList<IStatusHandler> handlers) // inject with IoC here
{
this.handlers = handlers;
}
public void ProcessStatus(StatusEventArgs args)
{
foreach(var handler in handlers)
if (handler.Handle(args.Data)) return;
throw new Exception("No handler for this kind of status!");
}
}