old-www/LDP/LG/issue81/tougher.html

507 lines
12 KiB
HTML

<!--startcut ==============================================-->
<!-- *** BEGIN HTML header *** -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML><HEAD>
<title>Creating Reusable Software Libraries LG #81</title>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#0000AF"
ALINK="#FF0000">
<!-- *** END HTML header *** -->
<CENTER>
<A HREF="http://www.linuxgazette.com/">
<IMG ALT="LINUX GAZETTE" SRC="../gx/lglogo.png"
WIDTH="600" HEIGHT="124" border="0"></A>
<BR>
<!-- *** BEGIN navbar *** -->
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="sevenich.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue81/tougher.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../lg_faq.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="vikas.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
<!-- *** END navbar *** -->
<P>
</CENTER>
<!--endcut ============================================================-->
<H4 ALIGN="center">
"Linux Gazette...<I>making Linux just a little more fun!</I>"
</H4>
<P> <HR> <P>
<!--===================================================================-->
<center>
<H1><font color="maroon">Creating Reusable Software Libraries</font></H1>
<H4>By <a href="mailto:robt@robtougher.com">Rob Tougher</a></H4>
</center>
<P> <HR> <P>
<!-- END header -->
<dl>
<dt><a href=#1>1. Introduction</a>
<dt><a href=#2>2. Making It Easy To Use</a>
<dd><a href=#2.1>2.1 Keeping It Simple</a>
<dd><a href=#2.2>2.2 Being Consistent</a>
<dd><a href=#2.3>2.3 Making It Intuitive</a>
<dt><a href=#3>3. Testing Thoroughly</a>
<dt><a href=#4>4. Providing Detailed Error Information</a>
<dt><a href=#5>5. Conclusion</a>
</dl>
<a name=1></a>
<h3>1. Introduction</h3>
<p>
Software libraries provide functionality to
application developers. They consist of reusable code that
developers can utilize in their projects.
Software libraries targeted for Linux are usually available in both
binary and source code form.
</p>
<p>
A well-written software library:
</p>
<ul>
<li>is easy to use
<li>works flawlessly
<li>provides detailed error information
</ul>
<p>
This article describes the above principles of library creation,
and gives examples in C++.
</p>
<table width=80% border=1>
<tr>
<td bgcolor=#EEEEEE>
<font>
<h4>
Is This Article For You?
</h4>
<p>
Create software libraries only when you have to.
Ask yourself
these questions before proceeding:
</p>
<ul>
<li><i>Will anyone (including you) need functionality X in future applications?</i>
<li><i>If so, does a library implementing functionality X already exist?</i>
</ul>
<p>
If no one will need the functionality you are developing,
or a software library implementing it already exists,
don't create a new library.
</p>
</font>
</td>
</tr>
</table>
<a name=2></a>
<h3>2. Making It Easy To Use</h3>
<p>
The first step in creating a software library is designing
its <b>interface</b>.
Interfaces written in procedural languages, like C, contain
functions. Interfaces written in object-oriented languages,
like C++ and Python, can contain both functions and classes.
</p>
<p>
Remember this motto when designing your interface:
</p>
<ul>
<li><i>The easier to use, the better</i>
</ul>
<p>
As a library designer, I am constantly faced with
finding the right balance between functionality and
ease of use. The above motto helps me resist
adding too much functionality into my designs.
</p>
<p>
Stick with the following guidelines, and you'll be fine.
</p>
<a name=2.1></a>
<h4>2.1 Keeping It Simple</h4>
<p>
The more complex a library, the harder it
is to use.
</p>
<ul>
<li><i>Keep It Simple, Stupid</i>
</ul>
<p>
I recently encountered a C++ library that consisted of
one class. This class contained 150 methods.
<i>150 methods!</i>
The designer was most likely a C veteran using C++ - the
class acted like a C module.
Because this class was so complex, it was
<i>very difficult</i> to learn.
</p>
<p>
Avoid complexity in your designs, and your interfaces will
be cleaner and easier to understand.
</p>
<a name=2.2></a>
<h4>2.2 Being Consistent</h4>
<p>
Users learn consistent interfaces more easily. After learning
the rules once, they feel confident in applying those
rules across all classes and methods, even if they
haven't used those classes and methods before.
</p>
<p>
One example I am guilty of involves public accessors
for private variables. I sometimes do the following:
</p>
<pre>
class point
{
public:
int get_x() { return m_x; }
int set_x ( int x ) { m_x = x; }
int y() { return m_y; }
private:
int m_x, m_y;
};
</pre>
<p>
Do you see the problem here? For the m_x member, the public accessor
is "get_x()", but for the m_y member, the public accessor is
"y()". This inconsistency generates more work for the users - they
have to look up the definition of each accessor before using it.
</p>
<p>
Here's another example of an inconsistent interface:
</p>
<pre>
class DataBase
{
public:
recordset get_recordset ( const std::string sql );
void RunSQLQuery ( std::string query, std::string connection );
std::string connectionString() { return m_connection_string; }
long m_sError;
private:
std::string m_connection_string;
};
</pre>
<p>
Can you spot its problems? I can think of at least
these items:
</p>
<ul>
<li>Methods and variables are not named consistently
<li>Two different terms, <i>sql</i> and <i>query</i>, are
used to denote a SQL string
<li><i>m_sError</i> does not have a public accessor
<li><i>get_recordset()</i> does not have a <i>connection</i> in
its argument list
</ul>
<p>
Here is a revised version that solves these problems:
</p>
<pre>
class database
{
public:
recordset get_recordset ( const std::string sql );
void run_sql_query ( std::string sql );
std::string connection_string() { return m_connection_string; }
long error() { return m_error; }
private:
std::string m_connection_string;
long m_error;
};
</pre>
<p>
Keep your interfaces as consistent as possible - your users will
find them much easier to learn.
</p>
<a name=2.3></a>
<h4>2.3 Making It Intuitive</h4>
<p>
Design an interface how you would expect it to
work from a user's point of view - don't design it
with the internal implementation in mind.
</p>
<p>
I find that the easiest way to design an intuitive interface
is to write code that will use the library <i>before</i> actually
writing the library. This forces me to think about the
library from the user's standpoint.
</p>
<p>
Let's look at an example. I was recently considering writing
an encryption library based
on OpenSSL. Before thinking about the library
design, I wrote some code snippets:
</p>
<pre>
crypto::message msg ( "My data" );
crypto::key k ( "my key" );
// blowfish algorithm
msg.encrypt ( k, crypto::blowfish );
msg.decrypt ( k, crypto::blowfish ):
// rijndael algorithm
msg.encrypt ( k, crypto::rijndael );
msg.decrypt ( k, crypto::rijndael ):
</pre>
<p>
This code helped me think about how I should design the interface -
it put me in the user's shoes. If I decide to implement this
library, my design will flow from these initial ideas.
</p>
<a name=3></a>
<h3>3. Testing Thoroughly</h3>
<p>
A software library should work flawlessly. Well not
<i>flawlessly</i>, but as close to flawless as possible.
Users of a library need to know that the library is performing
its tasks correctly.
</p>
<ul>
<li><i>Why use a software library if it doesn't work correctly?</i>
</ul>
<p>
I test my software libraries using automated scripts.
For each library, I create a corresponding
application that exercises all features of the library.
</p>
<p>
For example, say I decided to develop the encryption
library I introduced in the previous section. My test
application would look like the following:
</p>
<pre>
#include "crypto.hpp"
int main ( int argc, int argv[] )
{
//
// 1. Encrypt, decrypt, and check
// message data.
//
crypto::message msg ( "Hello there" );
crypto::key k ( "my key" );
msg.encrypt ( k, crypto::blowfish );
msg.decrypt ( k, crypto::blowfish );
if ( msg.data() != "Hello there" )
{
// Error!
}
//
// 2. Encrypt with one algorithm,
// decrypt with another, and check
// message data.
//
// etc....
}
</pre>
<p>
I would occasionally run this application to make
sure that my software library did not have any major
errors.
</p>
<a name=4></a>
<h3>4. Providing Detailed Error Information</h3>
<p>
Users need to know when a software library cannot perform
its tasks correctly.
</p>
<ul>
<li><i>Alert the user when there is a problem</i>
</ul>
<p>
Software libraries written in C++ use
<b>exceptions</b> to pass information to its
users. Consider the following example:
</p>
<pre>
#include &lt;string&gt;
#include &lt;iostream&gt;
class car
{
public:
void accelerate() { throw error ( "Could not accelerate" ); }
};
class error
{
public:
Error ( std::string text ) : m_text ( text ) {}
std::string text() { return m_text; }
private:
std::string m_text;
};
int main ( int argc, int argv[] )
{
car my_car;
try
{
my_car.accelerate();
}
catch ( error& e )
{
std::cout << e.text() << "\n";
}
}
</pre>
<p>
The <i>car</i> class uses the <b>throw</b> keyword to
alert the caller to an erroneous situation. The caller
catches this exception with the <b>try</b> and
<b>catch</b> keywords, and deals with the problem.
</p>
<p>
</p>
<a name=5></a>
<h3>5. Conclusion</h3>
<p>
In this article I explained the important principles
of well-written software libraries.
Hopefully I've explained everything clearly enough so
that you can incorporate these principles into your
own libraries.
</p>
<!-- *** BEGIN bio *** -->
<SPACER TYPE="vertical" SIZE="30">
<P>
<H4><IMG ALIGN=BOTTOM ALT="" SRC="../gx/note.gif">Rob Tougher</H4>
<EM>Rob is a C++ software engineer in the New York City area.</EM>
<!-- *** END bio *** -->
<!-- *** BEGIN copyright *** -->
<P> <hr> <!-- P -->
<H5 ALIGN=center>
Copyright &copy; 2002, Rob Tougher.<BR>
Copying license <A HREF="../copying.html">http://www.linuxgazette.com/copying.html</A><BR>
Published in Issue 81 of <i>Linux Gazette</i>, August 2002</H5>
<!-- *** END copyright *** -->
<!--startcut ==========================================================-->
<HR><P>
<CENTER>
<!-- *** BEGIN navbar *** -->
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="sevenich.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue81/tougher.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../lg_faq.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="vikas.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
<!-- *** END navbar *** -->
</CENTER>
</BODY></HTML>
<!--endcut ============================================================-->