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

Actions

Information

9 responses

15 06 2006
Marc Cramdal

I think the first idea, “Write once, read multiple of times” is a good one, although one should check that important members are initialized in the constructor and only there. It should be avoided to put a setMember function for a member which should not be modified during the class instance lifetime.

Just some stupid Freudian question : why 1986 😉 ?

15 06 2006
Michaël Larouche

Why 1986 ? That’s my birthday.

15 06 2006
Diederik

I’m curious. how does the call to ‘new Date()’ look like?

15 06 2006
Michaël Larouche

Diederik:
new Date(Day(21), Month(2), Year(1986))

btw, I guess you will be interessed by libpapillon for KMess for KDE4

15 06 2006
pinky

I curious how to use it.

I have tried this test program:

#include

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);
};

Date::Date(Day day, Month month, Year year)
{
;
}

int main()
{
new Date(Day(21), Month(2), Year(1986));
return 0;
}

But i get this compiler error:

g++ explicit.cpp
/tmp/cc2bPaAh.o: In function `main’:explicit.cpp:(.text+0x37): undefined reference to `Day::Day(int)’
:explicit.cpp:(.text+0x4a): undefined reference to `Month::Month(int)’
:explicit.cpp:(.text+0x5d): undefined reference to `Year::Year(int)’
collect2: ld returned 1 exit status

How do i use this structs.

Also what do i do if i want to use this values in the program for some calculations or as variables? I think something like:

Day d = day;
d = d+5;

doesn’t work.

15 06 2006
jayKayEss

One thing I love about Perl is “named parameters:”

my $smtp = new Mail::Sender(
smtp => ‘mail.somewhere.com’,
from => ‘me@myhost.com’
);

You get the benefits of the approach you just outlined w/out all the extra typing. There doesn’t seem to be an equivalent in any other languages I’ve worked with, though.

15 06 2006
bjoern

Ada95 has a really nice feature for such things.

In Ada95 you can write:

type year is new integer;
subtype day is integer range 1..31;
subtype month is integer range 1..12;

Now you can use variables from type year, day and month like normal integer variables but you can’t mix them up and month can only have a value between 1 and 12.

Very nice and simple way to create own data types to avoid misuse and increase readability. Ada95 is the only language i know with such a powerfull mechanism.

15 06 2006
Narishma

jayKayEss: You can do the same thing in Python.

17 06 2006
Arghil

Narishma, jayKayEss:
In C++ try “Boost Parameter Library”
(http://boost.org/libs/parameter/doc/html/index.html)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




%d bloggers like this: