Double-dispatch и альтернативы

Я пытаюсь найти лучший способ обработки некоторых растущих конструкций if для обработки классов различных типов. Эти классы, в конечном счете, являются обертками вокруг разрозненных типов значений (int, DateTime и т.д.) с некоторой дополнительной информацией о состоянии. Таким образом, основное различие между этими классами заключается в типе данных, которые они содержат. Хотя они реализуют общие интерфейсы, их также необходимо хранить в однородных коллекциях, поэтому они также реализуют не общий интерфейс. Экземпляры класса обрабатываются в соответствии с типом данных, которые они представляют, и их распространение продолжается или не продолжается на основе этого.

Хотя это не обязательно проблема .NET или C#, мой код написан на C#.

Примеры классов:

interface ITimedValue {
 TimeSpan TimeStamp { get; }
}

interface ITimedValue<T> : ITimedValue {
 T Value { get; }
}

class NumericValue : ITimedValue<float> {
 public TimeSpan TimeStamp { get; private set; }
 public float Value { get; private set; }
}

class DateTimeValue : ITimedValue<DateTime> {
 public TimeSpan TimeStamp { get; private set; }
 public DateTime Value { get; private set; }
}

class NumericEvaluator {
 public void Evaluate(IEnumerable<ITimedValue> values) ...
}

У меня есть два варианта:

Двойная диспетчеризация

Недавно я узнал о паттерне Visitor и его использовании двойной диспетчеризации для обработки именно такого случая. Это привлекает тем, что позволяет не распространять нежелательные данные (если мы хотим обрабатывать только int, мы можем обрабатывать его иначе, чем DateTime). Кроме того, поведение того, как обрабатываются различные типы, будет ограничено одним классом, который обрабатывает диспетчеризацию. Однако, если/когда потребуется поддержка нового типа значения, придется приложить немало усилий для его обслуживания.

Союзный класс

Класс, содержащий свойство для каждого поддерживаемого типа значения, мог бы быть тем, что хранит каждый из этих классов. Любая операция над значением будет влиять на соответствующий компонент. Это менее сложно и требует меньше обслуживания, чем стратегия двойного диспетчирования, но это будет означать, что каждая часть данных будет распространяться по всему пути без необходимости, поскольку вы больше не сможете проводить дискриминацию по принципу "я не работаю с этим типом данных". Однако, если/когда потребуется поддержка новых типов, их нужно будет поместить только в этот класс (плюс любые дополнительные классы, которые нужно будет создать для поддержки нового типа данных).

class UnionData {
 public int NumericValue;
 public DateTime DateTimeValue;
}

Есть ли лучшие варианты? Есть ли что-то в этих двух вариантах, что я не учел, а следовало бы?

11
задан redman 1 March 2012 в 13:16
поделиться