Software Engineering Notes UNIT 3 Notes Part 2
Software Engineering Notes UNIT 3 Notes Part 2
The design activity begins when the requirements document for the software to be developed is
available and the architecture has been designed.
Generally, design focuses on module view. That is, during design we determine what modules
should the system have and which have to be developed.
The design of a system is essentially a blueprint or a plan for a solution for the system. Here we
consider a system to be a set of modules with clearly defined behavior which interact with each
other in a defined manner to produce some behavior or services for its environment.
The design process for software systems often has two levels. At the first level the focus is on
deciding which modules are needed for the system, the specifications of these modules, and how
the modules should be interconnected. This is what is called the system design or top-level
design.
In the second level, the internal design of the modules, or how the specifications of the module
can be satisfied, is decided. This design level is often called detailed design or logic design.
Detailed design essentially expands the system design to contain a more detailed description of
the processing logic and data structures so that the design is sufficiently complete for coding.
The design of a system is correct if a system built precisely according to the design satisfies the
requirements of that system.
The goal of the design process is not simply to produce a design for the system. Instead, the goal
is to find the best possible design within the limitations imposed by the requirements and the
physical and social environment in which the system will operate.
A design should clearly be verifiable, complete (implements all the specifications), and traceable
(all design elements can be traced to some requirements).
However, the two most important properties that concern designers are efficiency and simplicity.
Efficiency of any system is concerned with the proper use of scarce resources by the system. The
need for efficiency arises due to cost considerations. If some resources are scarce and expensive,
it is desirable that those resources be used efficiently. In computer systems, the resources that are
most often considered for efficiency are processor time and memory. An efficient system is one
that consumes less processor time and requires less memory.
Simplicity is perhaps the most important quality criteria for software systems. A simple and
understandable design will go a long way in making the job of the maintainer easier.
Creating a simple (and efficient) design of a large system can be an extremely complex task that
requires good engineering judgment. As designing is fundamentally a creative activity, it cannot
be reduced to a series of steps that can be simply followed, though guidelines can be provided.
For software design, therefore, the goal is to divide the problem into manageably small pieces that can be
solved separately.
However, the different pieces cannot be entirely independent of each other, as they together form the
system. The different pieces have to cooperate and communicate to solve the larger problem. This
communication adds complexity, which arises due to partitioning and may not have existed in the original
problem.
As the number of components increases, the cost of partitioning, together with the cost of this added
complexity, may become more than the savings achieved by partitioning. It is at this point that no further
partitioning needs to be done. The designer has to make the judgment about when to stop partitioning.
Total independence of modules of one system is not possible, but the design process should support as
much independence as possible between modules. Dependence between modules in a software system is
one of the reasons for high maintenance costs.
Problem partitioning, which is essential for solving a complex problem, leads to hierarchies in the design.
That is, the design produced by using problem partitioning can be represented as a hierarchy of
components.
In general, hierarchical structure makes it much easier to comprehend a complex system. Due to this, all
design methodologies aim to produce a design that employs hierarchical structures.
2. Abstraction
Abstraction permits a designer to consider a component at an abstract level without worrying about the
details of the implementation of the component.
An abstraction of a component describes the external behavior of that component without bothering with
the internal details that produce the behavior.
Abstraction is an indispensable part of the design process and is essential for problem partitioning.
There are two common abstraction mechanisms for software systems: functional abstraction and data
abstraction. In functional abstraction, a module is specified by the function it performs.
Functional abstraction is the basis of partitioning in function-oriented approaches. That is, when the
problem is being partitioned, the overall transformation function for the system is partitioned into smaller
functions that comprise the system function. The decomposition of the system is in terms of functional
modules.
The second unit for abstraction is data abstraction. Any entity in the real world provides some services to
the environment to which it belongs.
Certain operations are required from a data object, depending on the object and the environment in which
it is used. Data abstraction supports this view. Data is not treated simply as objects, but is treated as
objects with some predefined operations on them. The operations defined on a data object are the only
operations that can be performed on those objects. From outside an object, the internals of the object are
hidden; only the operations on the object are visible.
3. Modularity
A system is considered modular if it consists of discreet components so that each component can be
implemented separately, and a change to one component has minimal impact on other components.
Modularity is a clearly a desirable property in a system.
Modularity helps in system debugging—isolating the system problem to a component is easier if the
system is modular; in system repair—changing a part of the system is easy as it affects few other parts;
and in system building—a modular system can be easily built by "putting its modules together."
For modularity, each module needs to support a well defined abstraction and have a clear interface
through which it can interact with other modules. Modularity is where abstraction and partitioning come
together.
For easily understandable and maintainable systems, modularity is clearly the basic objective; partitioning
and abstraction can be viewed as concepts that help achieve modularity.
A system consists of components, which have components of their own; indeed a system is a hierarchy of
components. The highest-level component corresponds to the total system. To design such a hierarchy
here are two possible approaches: top-down and bottom-up.
A top-down design approach starts by identifying the major components of the system, decomposing
them into their lower-level components and iterating until the desired level of detail is achieved. Top-
down design methods often result in some form of stepwise refinement.
Starting from an abstract design, in each step the design is refined to a more concrete level, until we reach
a level where no more refinement is needed and the design can be implemented directly.
A bottom-up design approach starts with designing the most basic or primitive components and proceeds
to higher-level components that use these lower-level components. Bottom-up methods work with layers
of abstraction.
Starting from the very bottom, operations that provide a layer of abstraction are implemented. The
operations of this layer are then used to implement more powerful operations and a still higher layer of
abstraction, until the stage is reached where the operations supported by the layer are those desired by the
system.
A top-down approach is suitable only if the specifications of the system are clearly known and the system
development is from scratch.
However, if a system is to be built from an existing system, a bottom-up approach is more suitable, as it
starts from some existing components.
Module-Level Concepts
In a system using functional abstraction, coupling and cohesion are two modularization criteria, which are
often used together.
1. Coupling
To solve and modify a module separately, we would like the module to be loosely coupled with other
modules. The choice of modules decides the coupling between modules.
Types of coupling:
As long as a simple argument list is present (i.e., simple data are passed; a one-to-one correspondence of
items exists), low coupling (called data coupling) is exhibited.
A variation of data coupling, called stamp coupling is found when a portion of a data structure (rather
than simple arguments) is passed via a module interface.
At moderate levels, coupling is characterized by passage of control between modules. Control coupling is
very common in most software designs where a “control flag” (a variable that controls decisions in a
subordinate or superordinate module) is passed between modules.
Relatively high levels of coupling occur when modules are tied to an environment external to software.
For example, I/O couples a module to specific devices, formats, and communication protocols. External
coupling is essential, but should be limited to a small number of modules with a structure.
High coupling also occurs when a number of modules reference a global data area. Common coupling, as
this mode is called. Modules access a data item in a global data area (e.g., a disk file or a globally
accessible memory area).
The highest degree of coupling, content coupling, occurs when one module makes use of data or control
information maintained within the boundary of another module. Secondarily, content coupling occurs
when branches are made into the middle of a module. This mode of coupling can and should be avoided.
2. Cohesion
Cohesion of a module represents how tightly bound the internal elements of the module are to one
another. Cohesion of a module gives the designer an idea about whether the different elements of a
module belong together in the same module. Cohesion and coupling are clearly related.
Usually, the greater the cohesion of each module in the system, the lower the coupling between modules
is.
Coincidental (Lowest)
Logical
Temporal
Procedural
Communicational
Sequential
Functional (Highest)
Coincidental is the lowest level, and functional is the highest.
Coincidental cohesion occurs when there is no meaningful relationship among the elements of a module.
Coincidental cohesion can occur if an existing program is "modularized" by chopping it into pieces and
making different pieces modules.
A module has logical cohesion if there is some logical relationship between the elements of a module,
and the elements perform functions that fall in the same logical class. A typical example of this kind of
cohesion is a module that performs all the inputs or all the outputs.
Temporal cohesion is the same as logical cohesion, except that the elements are also related in time and
are executed together. Modules that perform activities like "initialization," "clean-up," and "termination"
are usually temporally bound.
A procedurally cohesive module contains elements that belong to a common procedural unit. For
example, a loop or a sequence of decision statements in a module may be combined to form a separate
module.
A module with communicational cohesion has elements that are related by a reference to the same input
or output data. That is, in a communicationally bound module, the elements are together because they
operate on the same input or output data. An example of this could be a module to "print and punch
record."
When the elements are together in a module because the output of one forms the input to another, we get
sequential cohesion. a sequentially bound module may contain several functions or parts of different
functions.
Functional cohesion is the strongest cohesion. In a functionally bound module, all the elements of the
module are related to performing a single function. By function, we do not mean simply mathematical
functions; modules accomplishing a single goal are also included. Functions like "compute square root"
and "sort the array" are clear examples of functionally cohesive modules.
Structured design methodology (SDM) views every software system as having some inputs that are
converted into the desired outputs by the software system. The software is viewed as a transformation
function that transforms the given inputs into the desired outputs, and the central problem of designing
software systems is considered to be properly designing this transformation function. Due to this view of
software, the structured design methodology is primarily function-oriented and relies heavily on
functional abstraction and functional decomposition.
The concept of the structure of a program has at the heart of the structured design method. During design,
structured design methodology aims to control and influence the structure of the final program. The aim is
to design a system so that programs implementing the design would have a hierarchical structure, with
functionally cohesive modules and as few interconnections between modules as possible.
The overall strategy is to identify the input and output streams and the primary transformations that have
to be performed to produce the output. High-level modules are then created to perform these major
activities, which are later refined. There are four major steps in this strategy:
3. First-level factoring