I am refactoring a smelly class and I'm sure I'm making a pig's ear of it. It feels like a common problem, but I can't see a common solution. As the domain is fairly niche, I've changed names etc.
I have an interface, let's say IThing
, which has a few methods and started with a few properties. As time went on, many different IThing
s cropped up with different properties. (IThing
is a sort of interface to multiple different reverse-engineered Things that we have no control over, so the properties are thrust on us.)
We ended up with a pattern of the sort bool HasSpecialNumber
, int SpecialNumber {get; set;}
. This got smelly as we added more and more properties, with every implementation having to implement 20+ methods just to say they don't support a property.
I thought of using a mixin approach, but maybe I'm thinking of this wrongly, because it would involve as many interfaces as properties or combinations of properties, and a lot of casting. It also seems heavy-handed when I'm only providing properties here and the methods are not changing.
An IThing looks sort of like this (C#ish pseudo-ish code)
IThing
// Some methods every Thing supports
DoSomething
DoSomethingElse
// A bunch of properties some Things support
bool HasSpecialNumber { get; }
int SpecialNumber { get; }
bool HasName { get; }
string Name { get; }
... and so on
Apart from the smell, every time a property was added, a whole bunch of classes broke. These all needed to be serialized too, using protobuf-net. Many of these classes were only distinct in that they had special objects.
The next thing we tried was reducing the properties to two methods, with a private method for adding properties.
IThing
// Some methods every Thing supports
DoSomething
DoSomethingElse
// A bunch of properties some Things support
bool HasProperty( PropertyIdEnum propertyId )
T GetProperty<T>( PropertyIdEnum propertyId )
// Private method for adding properties
void AddProperty<T>( PropertyIdEnum propertyId, T value )
This sort of worked. Dozens of properties became two accessor methods, and updating the PropertyIdEnum
didn't break anything. The AddProperty
was used to add properties to a dictionary that mapped the IDs to objects, with a Type
stored alongside to ensure no weird casting errors. But I exchanged compile-time type checking for run-time type checking. Also, protobuf-net doesn't support serializing Object
s or Type
s, though that is an implementation detail.
We ditched the AddProperty
abstraction and went back to dozens of classes. That resolved the protobuff-net issue at the cost of having a lot more classes to worry about. We still lack the compile-time type safety.
I see this issue all over the place in areas I work. For example, ffmpeg and the CODECs they deal with, each with special behaviour. The solutions they use are constrained by backwards compatibility though, an they are working in heavily optimized C while I'm in C#. Is there some pattern or advise for dealing with a run-away set of properties that need to be handled trough a single common interface? If I had control over the properties I would just not be in this situation in the first place, but I don't, so here I am.