I'm developing software that manipulates all facets of ID3 tags in digital audio files (typically mp3s). I'm curious as to what good design in this domain entails. Here's a quick synopsis:
An ID3 tag is a small piece of information, in a specific format, embedded in an mp3 file. There are 2 major versions of ID3 tags v1 and v2. They are entirely different animals. Inside each major version are minor versions ( v2.3, v2.4, etc... ) which have many similarities. There are no constraints here - a client of this API should have complete flexibility to manipulate tags however it sees fit. The design, of course should adhere to the OCP as well as other basic OOD principles.
I already have a design which I purposely didn't post. I am hoping to harness the design knowledge of others who may see the domain entirely different than I do. All ideas are welcomed.
Work out what a tag can do. It's ok if some tags can do things that others cannot.
Suppose a tag can do that() and theOther(). Write a class that implements that() and theOther() for one kind of tag. Write another class that does the same for another kind of tag. Do they have some data in common? If so, then you only have behaviour differing, so you can factor out the behaviour into another object, which has, say, that(Tag tag) and theOther(Tag tag) as methods, and you can make the original two classes into one. Provide two factory methods for instantiating it, with different names, one for each tag type.
Add other types in the same way. If none of the data is common between two tag types, then make an interface with that() and theOther() in, instead of factoring out the behaviour.
The best design will be the one with the least repetition.
V! and V2 are so different, that personally, I'd probably define a couple of pretty simple interfaces along the lines of:
I would then create a factory to return an ID3 instance from the file under consideration. A file with a V1 header would return an instance with the fixed set of V1 tags. A file with a V2 header would return an instance which scanned and decoded the much more flexible V2 header and dynamically build a collection of tags.
If you want to be able to add new V2 tags as well as edit existing ones in place, you could add something to the ID3 interface (maybe something like boolean canAddTags(); void addTag(Tag tag); )
Does that make sense? [ May 06, 2007: Message edited by: Frank Carver ]