If you can't decide, give the user more options -- not!

I find my self in the middle of lots of sticky API design issues.  Usually we have a couple of pretty clear options with well known pros and cons.  Sometimes the battles get pretty heated between people arguing for one option or another and some well meaning PM suggests “let’s just do them both”… It is hard to seem reasonable and argue with that - after all both sides got what they wanted right?  Maybe, but the poor user is in a net worse place.  If we’d just decide on an option and get 100% behind it we could make the world SOO much better.   There would be net fewer types and methods in the system and the ones we’d have would be better!

 

I had another one of those issues come up today.  A team coming in for API reviews had a base class, let’s call it “Foo” (so as to not spoil any of the fun from the PDC) and an interface called “IFoo” that represented exactly the same contract.  So I ask why they have both.  Well, it seems that some clients wanted and interface and some wanted a base class.  In other words, they couldn’t decide.  So I ask, when you expose methods that take with Foo’s what are you suppose to use? Foo or IFoo? When would I use one over the other?  Do your methods take Foo and IFoo?  We had the same problem with Component and IComponent and eventually had to add specific test cases to make sure IComponent kept working because most everything derived from Component, it became the defacto standard.  Having IComponent there to support a niche scenario adds net complexity to the system.  I think the same is true for today’s Foo\IFoo case.  In the end we decided to the vast majority of scenarios were satisfied by just using the Foo base class.  

 

I think the world got every so slightly better today because we took away an option…. Score another point for addition through subtraction.

 

 

 

 

Published 12 September 03 02:14 by BradA

Comments

# dom said on September 12, 2003 6:26 AM:
Glad to hear that simpicity has ruled the day! But how do you convince those who don't understand the importance of simplicity in design? http://dotnetjunkies.com/weblog/d0m1/posts/1561.aspx
# RichardH said on September 12, 2003 7:15 AM:
I fully agree with that. The success of generics and templates in Standard C++, and the announced generics that will be incorporated in C# means that we all want to write lesser and simpler code, rather than copy paste the same thing in many places. But I think when it comes to User Interface, we should provide more options than one.
# Urs Muff said on September 12, 2003 10:29 AM:
I don't see the problem for the user to decide which to use: Foo or IFoo. It should be very easy: if you use the concept (composition, parameters, or returns) always use the interface IFoo. If you want to extend/implement the base class and only want to use inheritance use Foo as base class. Usage is normally at least 10 to 1 compared to implementation, furthermore you allow others to aggregate (COM like) and be compatible to the concept of Foo even if they cannot derive from Foo (single inheritance restriction). In a framework that is very important in my opinion, if you want others to be able to make full usage of new concepts introduce in the framework, but they have already existing concepts and implementation hierarchy that is hard to change.
# mikelor said on September 12, 2003 2:08 PM:
I would agree with the usage of both. Whenever possible I *always* use the interface when creating references to instances of classes. Why, because you program to the interface, not the implementation. A second and I believe more important point is brought up by Urs Muff, C# like Java is limited to single inheritance (thank goodness). By not creating an interface to implement, I must always derive my class from Foo, well what if it makes more sense for my application to derive from Bar and implement IFoo. Single inheritance for classes, but multiple inheritance for interfaces. See http://www.javaworld.com/javaworld/jw-09-2001/jw-0921-interface_p.html
# mikelor said on September 12, 2003 2:08 PM:
I would agree with the usage of both. Whenever possible I *always* use the interface when creating references to instances of classes. Why, because you program to the interface, not the implementation. A second and I believe more important point is brought up by Urs Muff, C# like Java is limited to single inheritance (thank goodness). By not creating an interface to implement, I must always derive my class from Foo, well what if it makes more sense for my application to derive from Bar and implement IFoo. Single inheritance for classes, but multiple inheritance for interfaces. See http://www.javaworld.com/javaworld/jw-09-2001/jw-0921-interface_p.html
# Gerke Geurts said on September 12, 2003 2:23 PM:
I wholeheartedly agree with Urs. Especially when building (on top of existing) infrastructure code, it is useful not too be forced to use someone's base class. The interface (including preconditions, postconditions, invariants) is the contract. Using a base class and inheritance is mere technical convenience. In VB6 days it was frustrating not to have this convenience. Now, I have already encountered several times the shear frustration of not having interfaces as well as base classes. I want both and need the freedom to decide whether I'll implement the contract myself or use an easy base implementation! So, I find it rather short-sighted to take the decision to only use a base class, as any framework designer does not have full oversight of how others will use and extend the framework.
# Paul Walker said on September 12, 2003 8:02 PM:
Whenever I find myself w/ a similar choice in which my interface methods would map one to one w/ my base class methods I ask myself this question: Is the base class a candidate for run-time decoration? For example: I have a Division class that implements IDivision and contains all the state data that is used in almost all contexts of the application. This state data, being small/static/used enough, is cached in the application as instances of each division. Depending on the context, I often like to add run time capability to these division instances ( for example, add a collection employees ) by passing this instance to the constructor of a a Decorator class that also implements IDivision. The implemented methods of the Decorator delegate to it's private instance of the Division class and the client can now treat the decorator as it's IDIvision when needed or as the Decorator when needed for the specific runtime capability. Straight up inheritance is no good for me here...I already have an instantiated Division class. Keeping one to one mapping of the methods of the base class and interface allows for easier run time decoration of the base class when desired.
# Keith Hill said on September 16, 2003 12:45 AM:
Foo vs. IFoo aside, I think reducing and keeping the "surface area" of the .NET FCL as small as possible is a very worthwhile goal. As more functionality and more and more of the different MS groups start contributing to the FCL I worry about winding up with a complicated mess.
# Frank Hileman said on September 16, 2003 10:24 AM:
IComponent was essential. Component is a fat base class, and derives from MarshalByRefObject, which has performance issues (inlining) and other limitations. Without IComponent we would have been in trouble. So when you design low level abstractions such as these, please listen to the PM that says "the base is not for everybody". You picked a very bad example here. I would agree that sometimes you only need the base class. But not for the lowest level abstractions.
# Joe Cheng said on September 26, 2003 1:16 PM:
+1 on including both interface/base class. I've always wondered, why is it not possible to "implement" (in Java terminology) the contract exposed by a public class--that is, assert that my class fulfills the contract of Foo, without inheriting any of the implementation of Foo? That way, you never need an explicit IFoo. Most people could just extend Foo, but if you have a class that is part of another class hierarchy you could just implement Foo. Does any statically type-checked OOP language support this kind of thing?
New Comments to this post are disabled

Search

Go

This Blog

Syndication

Page view tracker