Advantages
of Minimizing Dependencies (or Coupling) Between the Parts
|
|
The main objectives of software design and development paradigm are:
|
|
1.
|
Most experts agree the reusable components are essential. The premise is: if we (i.e. application developers) write less code, it costs less and would have fewer bugs. |
|
|
2.
|
But many experts argue a system that can be easily adoptable to evolving needs is more important than reusability. |
|
We believe both are very important. Although the first has obvious and many well documented advantages, the
second is also extremely important. Our personal experience shows that, most of us (software developers with pure software
background with little experience in problem domain) develop software applications for
specialized areas, such as bio-tech, semiconductors, finance or banking would face daunting task of what user wants.
They give us a spec and we spend some time discussing over it. Then we pretend (most of us believe) we understand
what they want, only to learn after implementing that we miss understand many things.
|
|
It is practically impossible for developers to get the
big picture by just reviewing the requirements and sitting in many requirements meetings (brainstorming sessions).
We would form better comprehension and insights on the requirements and overall system, as we implement one
subsystem at a time. The time we spend in coding/tinkering allow us to gain insights into that subsystem (its dependencies
or relationships with others) and slowly get clearer and clearer view on the big picture. The prolonged process
of pondering over design, coding and then debugging implants deeper understanding and knowledge over different
aspects of the system and the application domain. (As soon as you get sufficient knowledge, you boss assigns
you to new domain and a new recruit end up responsible for the half backed code).
|
|
Not only this, even by chance we satisfy all the user requirements,
then it’s turn for the users to learn that it is not what they really want. Usually users don’t
even know what exactly they want. As a user, I was in that position few times. Also, user needs usually change,
for example, new technologies, innovations or change in competitive landscape. Hence, an ideal development
paradigm must accommodate this imperfect system (e.g. constantly moving target and the gap of communication
or domain knowledge), so that, we can incrementally refine the parts to reach and maintain the perfection.
If I were a user, I would prefer to build a working system first and iteratively improve it, one part at
a time.
|
|
Observations
on the practice of Software Engineering
(From: CBSE-2003; Slide#9;
M.R.V.Chaudron; University Eindhoven)
About
80% of software engineering deals with changing existing
software
It
is not the strongest of the species that survive, nor the
most intelligent, but the ones most responsive to change
-- Charles
Darwin
Time
to market is an important competitive advantage: incorporate
successful innovations quickly
|
|
An ideal component based systems must comprise of many
“loosely coupled” subsystems, which would allow us to refine each subsystem mostly independently.
This substantial reduces the cost and complexity to evolve the system. As we know, in most systems, the
components collaborate with each other. This forces us to create coupling (or dependency) between them, so that, they can collaborate with each other. For example,
in an automobile, the CD-player needs power, which is a real dependency. We resolve that dependency by connecting the CD-player to the 12V DC car battery. Here the 12V
DC is a predefined standardized interface, which is an artificial dependency.
|
|
For example, in a shopping cart application there are two
types of components: A shopping cart (e.g. Invoice/Table -- a mini-spreadsheet) and shopping items (GUI components
that present the items for sale). When user drag-n-drops an item on the shopping-cart, the shopping cart need
information about the item to add at to the Invoice. This information exchange is real dependency between
the two components. To properly exchange and interpret the information, they may use predefine XML-schema to
communicate various pieces of data (e.g. price, discounts, inventory and model etc.). This
format of the data is minimum required artificial
dependency. However, if you look at the code, there will be only one service-function
call. The dependency constitutes the function-call and the format of the
parameters.
|
|
The CBSDF needs only resolve this kind of minimal rational dependencies
to remove or replace any component. On the other hand, the traditional programming paradigm, forces the developers to create numerous
irrational dependencies. This results in
tangled web of harmful dependencies between the Objects as shown in the CF-LC-Figures, even for three components.
A large software would have hundreds of such irrational harmful dependencies, which
increases the cost of even small changes by about 10 times in the large
applications.
|
|
We cannot
eliminate the rational dependencies (i.e. real and
artificial dependencies). But an Ideal “loosely coupled” component based systems could eliminate the “irrational dependencies”.
Therefore, an ideal “loosely coupled” component based system must minimize and well-document the high-level rational dependencies
of each component, such that, developer only need to know and resolve those simplified dependencies to replace each of the “loosely coupled” components.
(e.g. only plug-in CD-player that can operate on 12V DC).
|
|
In a complex system, the OOP or Object oriented approach
forces us to create large number of complex irrational dependencies between the objects. The OOP
has no mechanism to encapsulate the object-level dependencies, so introduces
costly irrational dependence. It would make hard to make any changes to any object. It is hard to predict
what kind of effect a small change might have on the overall system.
|
|
We must create the system or application, such that, the
subsystems have minimal dependency (i.e. highly loosely coupled) between sub-systems (e.g. Fig#8). For example, in the automobile or in computers, the interfaces between
the parts are well defined, which allow the manufacturer to independently refine the design of
each part (e.g. engine, battery, CD-Player or CPU). Furthermore,
preferable to have automated mechanisms to detect any bracken interfaces between them. (i.e. automated
software tools and processes to maintain and resolve the dependencies).
|
|
Usually the reusable components (e.g. Java classes for
pie or bar-chart) require to be designed to accommodate wide range of needs. So these
classes require lot of code to customize. For example, input data
and style information, such as, 2D/3D, fonts and colors etc. On the other hand, the resultant component, after all the data is initialized would require very little code
to collaborate with other such components.
|
|
Besides, the resultant component
(generated by the CF) is custom designed to satisfy specific
need of the application. Hence certainly possible to design, so that, it needs
simple minimum high-level couplings (may be 1 or 2 service functions and about 5
lines of coupling code).
Hence it would have very little dependency on rest of the application. This would allow us to refine or replace this component very easily to satisfy evolving needs.
Most importantly, the high-level service-interfaces or dependencies reflect the
design intent of the component.
|
|
CBSDF accommodates this through “Component Factories”
(or “CF”). The well-designed supply chain of the CF (see Fig#4
& #5) would allow us to constantly
refine or replace each part (e.g. each node in the supply
chain or tree), with less effort. If any interface is
broken inadvertently, automated software tools should detect
the broken interface-contract for the developer to take
necessary action.
|
|
Two very useful basic characteristics of “loosely
coupled” components for computers are: Each component physically encapsulates all its subcomponents and offers simplest possible interfaces to access its services
(in order to minimize dependencies). For example, plug-in parts such as graphics card and hard drive.
Without this ability to
build the component-hierarchies, the computer-makers also forced to create
complex irrational dependencies, similar to the irrational dependencies, we
found in the software applications today.
|
|
In the software application, it is desirable to have such
encapsulation and simpler service oriented interfaces, for example, to replace them with little effort or to constantly refine the component (such changes would
have no effect as long as the service interfaces are not changed). However if the interfaces or services are upgraded or changed, it would be much simpler to
understand the impact on the other parts.
|
|
The flaw in the OOP paradigm is that it is not able to control
the unstructured explosion of irrational harmful dependencies between objects.
How can you remove any one of the three components in FIG#15?
The scattered objects and random distributions of tangled
spaghetti code in the source files, make it impossible to preserve the plug-n-play type independence for the components.
The software code in itself in each component is not so complex; however, system complexity explodes if you
cannot determine what kind of effect a small change in it may have on the other parts of the system. It
is impossible to control the complexity of large systems, if we cannot determine
the dependencies accurately
& quickly for each component.
|