API simplicity and design

14 06 2006

I just ported Kopete to new KDialog API and to new KPageDialog, which was done by Tobias Koenig last week. The new API is really nicer to use, you have done a great job Tobias and this helped me to adapt a lot of old code.

This new API use a philosophy called “Write once, read multiple of times.”, I use the same philosophy for some time while doing libpapillon. I will give you a case, when you read a constructor like this new KDialog(i18n("Caption"),parent, name, User1 | User2, User1, true, ...). This make sense when you first write it because you have the API documentation open in another window. Now, re-read this code 1 month later, the code will make less much sense and you will need to remember and try to guess what each parameter do. By simplicifing your API, you make your code more readable and more maintainable, not just for you but for yours followers too because we are in open source world and other people read our code.
Updated code:

KDialog *newDialog =new KDialog(parent);
newDialog->setCaption( i18n("Caption") );
newDialog->setButtons( KDialog::User1 | KDialog::User2 );
newDialog->setDefaultButton( KDialog::User1 );

Easier to read isn’t ? 🙂

Another philosophy should be used while designing API is make your interface ease to use and harder to misuse. In fact, that’s one item from book “Effective C++ 3rd Edition.”. Every serious C++ Programmer should have a copy of this book IMO. The goal is to reduce the complexity of your API to avoid confusion when using it, thus avoiding bugs and unexpected behavior. Also to report error at compile-time and not at runtime. I will reuse the example from the book (that I paste from my memory ;)).

Here a simple encapsulation of a Date.

class Date
{
public:
Date(int year, int month, int day);
};

What happen if someone call Date(21, 02, 1986) or Date(02, 21, 1986) ? This can lead to an unexpected behavior.

Here a good way to encapsulate Date:

struct Month
{
explicit Month(int);
};
struct Day
{
explicit Day(int);
};
struct Year
{
explicit Year(int);
};
class Date
{
public:
explicit Date(Day day, Month month, Year year);
};

By making use of keyword explicit, you avoid implicit conversion to int and compiler will generate an error if you try to use the wrong date component.

Advertisements