vendredi 22 janvier 2016

Using strings vs types in library api

I am designing a library that I expect others to consume in which "things" in the system can have arbitrary attributes assigned to them dynamically at runtime. In my case an Attribute is a class that has a delegate function that returns a float value given some input (not a C# language attribute). Each of these "things" has some methods for getting and setting these attributes. Internally each thing stores attributes in a Dictionary but I do not want to expose direct access to this dictionary to the api consumer because the set/get methods will also do things like emit events.

I'm having an arguement with myself about how these attributes should be accessed. My first thought was to just keep it simple and use strings, so a get method might be thing.GetAttribute("Height"). But I don't like this for a few reasons: 1.) it is easy to misspell the string name. 2.) This isnt type safe, things can only have 1 attribute of a given 'type' at a time. So my next thought was to use generics to remove the dangers of loose strings. The v2 api might look like this: thing.GetAttribute<Height>().

The downside of that obviously is that classes need to be declared for each attribute type, but there is almost never any difference between the attribute implementations so requiring classes to be declared per attribute that needs to be added seems annoying. (Though code generation with a UI is very much on the table for this). If the classes were all pre-declared I would much prefer the generic solution here.

A more complete example:

var flower = new Item();
flower.SetAttribute<Petals>(4f);
float petalCount= flower.GetAttribute<Petals>(); //4
flower.GetAttribute<Gears>(); //0, flower doesnt have a Gears attribute

vs

var flower = new Item();
flower.SetAttribute("Petals", 4f);
float petalCount= flower.GetAttribute("Petals"); //4
flower.GetAttribute("Gears"); //0, flower doesnt have a Gears attribute

Ignoring the fact that this is a fairly contrived example and the system and reasons for this Get/Set api isnt fully explained, which is the perferred api design? Is there another pattern that might work that I haven't thought of here?

Aucun commentaire:

Enregistrer un commentaire