Building on Leri, I would think more along the lines of what a tracker might need to do.
I would be inclined to do something like this:
public interface ITracker {
void BeginTracking();
void Track(ITrackerEvent trackerEvent);
void EndTracking();
}
Then all trackers have a sense of when they're starting and when they're finishing. This is important because a tracker may be holding onto resources that shouldn't be held longer than necessary. If a tracker doesn't need to use either BeginTracking
or EndTracking
, the implementation is trivial. A trivial implementation is not a leaky abstraction. A leaky abstraction is one that doesn't work for all implementations.
Now suppose you are flat-out against having two extra methods in each tracker (why?). You could instead have ITrackerEvents that are out of band and cover the semantic meaning of Begin and End. I don't like this. It requires every tracker to have special code to handle out of band events.
You can also have a separate interface
public interface IDemarcatedTracker : ITracker {
void BeginTracking();
void EndTracking();
}
which requires you to have special case code in your calling code to check to see if the ITracker is also an IDemarcatedTracker:
public void BeginTracking(ITracker tracker)
{
IDemarcatedTracker demarcatedTracker = tracker as IDemarcatedTracker;
if (demarcatedTracker != null)
demarcatedTracker.BeginTracking();
}
And not to over blow things too much, but I would also wonder what is supposed to happen when a tracker fails? Just blindly throw an exception? And here is where the abstraction is actually leaky. There is no process for a tracker to let you know that it can't track.
In your case you might want to return a boolean (limited information), an error code (somewhat more information), or an error class/struct. Or you might want to have a standard exception that gets thrown. Or you might want the Begin() method to include a delegate to call when an error happens in tracking.