I am trying to find a better way to handle some growing if
constructs to handle classes of different types. These classes are, ultimately, wrappers around disparate value types (int, DateTime, etc) with some additional state information. So the primary difference between these classes is the type of data they contain. While they implement generic interfaces, they also need to be kept in homogeneous collections, so they also implement a non-generic interface. The class instances are handled according to the type of data they represent and their propogation continues or doesn't continue based on that.
While this is not necessarily a .NET or C# issue, my code is in C#.
Example classes:
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) ...
}
I have come up with two options:
Double Dispatch
I recently learned of the Visitor pattern and its use of double dispatch to handle just such a case. This appeals because it would allow undesired data to not propogate (if we only want to handle an int, we can handle that differently than a DateTime). Also, the behaviors of how the different types are handled would be confined to the single class that is handling the dispatch. But there is a fair bit of maintenance if/when a new value type has to be supported.
Union Class
A class that contains a property for each value type supported could be what each of these classes store. Any operation on a value would affect the appropriate component. This is less complex and less maintenance than the double-dispatch strategy, but it would mean that every piece of data would propogate all the way through unnecessarily as you can no longer discriminate along the lines of "I don't operate upon that data type". However, if/when new types need to be supported, they only need to go into this class (plus whatever additional classes that need to be created to support the new data type).
class UnionData {
public int NumericValue;
public DateTime DateTimeValue;
}
Are there better options? Is there something in either of these two options that I did not consider that I should?
In “double dispatch”, the operation executed depends on: the name of the request, and the type of TWO receivers (the type of the Visitor and the type of the element it visits). This essentially means different visitors can visit the same type and different types can be visited by the same visitor.
Double dispatch is a technical term to describe the process of choosing the method to invoke based both on receiver and argument types. A lot of developers often confuse double dispatch with Strategy Pattern. Java doesn't support double dispatch, but there are techniques we can employ to overcome this limitation.
In software engineering, double dispatch is a special form of multiple dispatch, and a mechanism that dispatches a function call to different concrete functions depending on the runtime types of two objects involved in the call.
single dispatch (plural single dispatches) (computing) A dispatch method where the implementation of a function or method is chosen solely on the type of the instance calling the method.
Multiple dispatch or multimethods is a feature of some programming languages in which a function or method can be dynamically dispatched based on the run-time (dynamic) type or, in the more general case, some other attribute of more than one of its arguments.
method 1, using dynamic for double dispatch (credit goes to http://blogs.msdn.com/b/curth/archive/2008/11/15/c-dynamic-and-multiple-dispatch.aspx). Basically you can have your Visitor pattern simplified like this:
class Evaluator {
public void Evaluate(IEnumerable<ITimedValue> values) {
foreach(var v in values)
{
Eval((dynamic)(v));
}
}
private void Eval(DateTimeValue d) {
Console.WriteLine(d.Value.ToString() + " is a datetime");
}
private void Eval(NumericValue f) {
Console.WriteLine(f.Value.ToString() + " is a float");
}
}
sample of usage:
var l = new List<ITimedValue>(){
new NumericValue(){Value= 5.1F},
new DateTimeValue() {Value= DateTime.Now}};
new Evaluator()
.Evaluate(l);
// output:
// 5,1 is a float
// 29/02/2012 19:15:16 is a datetime
method 2 would use Union types in c# as proposed by @Juliet here (alternative implementation here)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With