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.
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 š ?
Why 1986 ? That’s my birthday.
I’m curious. how does the call to ‘new Date()’ look like?
Diederik:
new Date(Day(21), Month(2), Year(1986))
btw, I guess you will be interessed by libpapillon for KMess for KDE4
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.
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.
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.
jayKayEss: You can do the same thing in Python.
Narishma, jayKayEss:
In C++ try “Boost Parameter Library”
(http://boost.org/libs/parameter/doc/html/index.html)