Download as pdf or txt
Download as pdf or txt
You are on page 1of 27

Large Scale C++ Software Design

John Lakos

It would be ludicrous to attempt to erect a 50 story office building


using the same materials and techniques a carpenter would use to
build a single family home.
Experience gained from small projects does not always extend to large
development efforts.

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 1 / 27
Large Scale C++ Software Design

Motivation

Object Oriented programs in their most general form are


fundamentally more difficult to test than their procedural
counterparts.
Their ability to alter internal behavior via virtual functions may
invalidate essential class invariants.
As programs get larger, many different forces come into play. Most
large projects fail due to poor physical design.

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 2 / 27
Large Scale C++ Software Design

Why do large projects Fail ?

Cyclic Link-Time Dependencies


Excessive Link-Time Dependencies
Excessive Compile-Time Dependencies

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 3 / 27
Cyclic Link-Time Dependencies

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 4 / 27
Cyclic Link-Time Dependencies

Any Single module does not make sense independently


Unable to start anywhere.
Each module requires the definition of the other
No one component can be used or tested without the other two

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 5 / 27
Excessive Link-Time Dependencies

Ever wanted to use a small functionality which took hours to link?


What happens when unnecessary functionality turns a lean concise
class into huge dianosaurs.
What happens when functionality of all clients cramped into one
module.
Each time a new feature is added,causes rest of clients increased
instance size, code size, runtime and physical dependencies.

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 6 / 27
Excessive Link-Time Dependencies

//str.h
class String{
//str.cpp
char* d_string_p;
#include "str.h"
int d_length;
#include "sun.h"
int d_Size;
#include "Asterix.h"
int d_Count;
//...
//...
//lots of includes
public:
//...
String();
#include "everyone.h"
String(const String&);
#include "theirmother.h"
String(const char*);
String::String()
String(const char c);
:d_String(0)
~String();
,d_length(0)
//
,d_size(0)
//...27 pages omitted
,d_Count(0)
//
//...
int isPalindrome();
//...
int isNameofFamousActor();
};

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 7 / 27
Excessive Link-Time Dependencies

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 8 / 27
Excessive Compile-Time Dependencies

Ever developed a multi-file program?


Changing a header file can potentially cause several translation units
to recompile.
In large projects, not only is the time to recompile the entire system
increasing, but so is the time to compile even individual translation
units.
Soon, refusal to modify low level units make the design
unmaintainable and practically useless

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 9 / 27
Excessive Compile-Time Dependencies

//myerror.h
#ifndef MY ERROR H
#define MY ERROR H
struct MyError
{
enum Codes
{
SUCCESS = 0,
WARNING,
IO ERROR,
// . . .
READ ERROR,
WRITE ERROR,
// . . .
BAD STRING,
BAD FILENAME,
// . . .
MARTIANS HAVE LANDED,
// . . .
}
}
#endif

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 10 / 27
Reuse is not without cost!!

If several programmers are attempting to use the same standard


component without demanding functional changes, the reuse is
probably reaonable and justified.
If several clients working on different programs, attempt to reuse a
component for a different purpose, an enhancement for a client may
possibly disrupt the other.
Unlike a program, a system has no main. It is a collection of
interdependent components that support a domain.
One must find a way to resuse part of a system needed to implement
a particular program without having to link in the rest of the system.

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 11 / 27
To Summarize

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 12 / 27
What are Dependencies?

Notations

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 13 / 27
What are Dependencies?

IsA
class B{/*...*/};
class D : public B{/*...*/};

D inherits from B

Uses In the Interface


//Imagine in a class D
int operator==(const A&,const A&)

D uses A in D’s interface

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 14 / 27
What are Dependencies?

Uses In the Implementation

//Intset class
int operator==(const Intset&,const Intset&)
{
IntsetIter iter = ...
}

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 15 / 27
The ”DependsOn” relation

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 16 / 27
Implied Dependencies

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 17 / 27
Compile-Time Dependencies

//str.h
#include "chararray.h"
class String{
charArray d_array; //Has A
//...
};

Visible that class String has CharArray as data member.


It is known from C that if a struct has an instance of a user defined
type as a member, it is necessary to know the size and layout of the
data member to parse through the definition of the struct.

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 18 / 27
Link-Time Dependencies

//word.h //str.h
#ifndef INCLUDED_WORD #ifndef INCLUDED_STR
#define INCLUDED_WORD #define INCLUDED_STR

#ifndef INCLUDED_STR class CharArray;


#include "str.h"
#endif class String{
CharArray* pd_Array; //HoldsA
class Word{ //...
String d_string; //HasA public:
//... String();
public: //...
Word(); };
//... #endif
}; ---------------------------------
//endif //str.c
-------------------------- #include "str.h"
//word.c #include "charArray"
#include "word.h" //...
//.... //...

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 19 / 27
Link-Time Dependencies

A component need not be dependent on another at compile time to


be dependent at link time.
Compiling charArray.c requires charArray.h Both str.h and charArray.h
are reuired to compile str.c and Finally word.h and str.h is required to
compile word.c
Notice, charArray.h is not required to compile word.c. However, word
still exhibits a physical dependency on charArray.
Except for inline functions, all class member functions and static data
members in C++ have external linkage.
A compile time dependency almost always implies a link time
dependency but not vice versa.

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 20 / 27
Ensure Reliable Quality Software
Physical Heirarchy
Heirarchy among components as defined by the DependsOn realtion is
analogous to logical heirarchy implied by layering.(Physical heirarchy
is not “logical inheritance“ among classes altough inheritance implies
physical dependencies.
Avoiding cyclic physical dependencies is core to effective
understanding,maintainability testing and re-use of code.
Well designed interfaces are small, easy to understand, easy to use,
yet these interfaces make user-level testing expensive.
Complex software systems should be designed to have low level
objects with well defined interfaces, allowing each object to be tested
in isolation. these objects must be integrated via layering into a
sequence of increasing complex sub systems again allowing each sub
system to be tested in isolation
The final product must also be tested to ensure customer quality
Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 21 / 27
Acyclic Physical Dependencies

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 22 / 27
Acyclic Physical Dependencies

//c2.h //c3.h //c1.h


//c1.h
#ifndef INCLUDED_C2 #ifndef INCLUDED_C3 #ifndef INCLUDED_C1
#ifndef INCLUDED_C1
#define INCLUDED_C2 #define INCLUDED_C3 #define INCLUDED_C1
#define INCLUDED_C1
class C1 class C2 class C2;
class C1{
class C1{
class C2{ class C3{ //...
//...
//... //... public:
public:
public: public: //C1 f(); //old
C1 f();
C1 g(); C1 h(const C2&); //C2 f(); //new
};
}; }; };
#endif
#endif #endif #endif

Decompose design into units with manageable complexity.In case of


APD, there is atleast one reasonable order to go about testing the
system.
C1 is tested first as it depends on nobody, then the additional C2
functionality As both C1 and C2 work properly, finally C3 can be
tested

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 23 / 27
High Quality Designs are Levelizable

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 24 / 27
High Quality Designs are Levelizable

Idea of partioning components into equivalence classes based on


physical dependencies.
Components whcih are external to the system and which have already
been tested and are reliable have level 0
Component with no local physical dependencies have level 1
A component that depends physically on another component at level
N-1, but not higher has level N
With a level diagram components can designed to be tested in
isolation.
Not possible with graphs that contain cyclic dependencies.

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 25 / 27
Is this Design Levelizable ?

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 26 / 27
Yes

Girish Loganathan (LSS Erlangen) Large Scale C++ Software Design 18/06/2012 27 / 27

You might also like