Module 3 Software Design
Module 3 Software Design
MODULE 3
SOFTWARE DESIGN
Ch 3: Software Design
During the software design phase, the design document is produced based on the customer
requirements specified in the Software Requirements Specification (SRS) document. The
design phase transforms the SRS into a form that is directly implementable in the coding phase.
The overall design process is shown schematically in Figure 5.1, where the process starts from
the SRS document and ends with the production of the design document. The design document
is expected to provide sufficient detail for direct implementation using a programming
language.
The design process involves converting the SRS into a design document through a series of
well-defined steps. The key elements designed and documented during this process include:
The design document must define the control relationships between modules, which typically
arise from function calls across modules.
The interfaces specify the exact data items exchanged between two modules when one module
invokes a function from another.
Each module may store and manage its own data. Appropriate data structures are designed to
handle this shared data.
Each function within a module performs specific processing tasks. The design phase specifies
the algorithms for these tasks, considering accuracy, time complexity, and space
complexity.
The design process is iterative: starting from the SRS (Figure 5.1), the design documents are
refined through multiple reviews to ensure they meet the specified requirements.
The design process is not a single-step procedure but an iterative one. It is classified into two
main stages:
• Objective: Decompose the problem into modules, identify control relationships, and
define interfaces.
• Outcome: The program structure or software architecture.
• Representation:
o Structure Charts (for procedural designs).
o UML diagrams (for object-oriented designs).
• At this stage, the design must ensure:
o High cohesion within modules.
o Low coupling among modules.
o Clear hierarchical structure.
2. Detailed Design
• Objective: Analyse each module to design its data structures and algorithms.
• Outcome: Module Specification (MSPEC) document.
o This includes detailed information that programmers can directly use for
implementation.
For example, once the high-level design is complete, the MSPEC allows programmers to
understand each module’s behaviour and proceed with coding.
Although several other design techniques exist (e.g., Jackson diagram [1975], Warnier-Orr
diagram [1977, 1981]), the primary focus in this context is on structure charts and UML
diagrams.
Even when the same methodology is applied, different designers may arrive at different
solutions. This happens because:
A good design is selected by comparing alternative solutions and identifying the one that meets
quality criteria like simplicity, efficiency, and maintainability.
Analysis vs Design
• Example: The analysis model of a library management system might only show data
flow (e.g., "issue book" or "return book"), while the design model specifies module
structure, algorithms, and data structures needed to implement these functions.
Thus, the design model is a transformation of the analysis model into a concrete solution that
is ready for coding.
Defining a “good” software design is challenging because the criteria depend heavily on the
problem domain and the application type. For instance, in embedded systems, where
memory and power are limited, memory usage is often the most important factor. In such
cases, design comprehensibility may be sacrificed to achieve code compactness.
Therefore, while criteria vary across application domains, there is broad agreement among
software engineers on a few universal characteristics that a good software design should
possess:
After correctness, understandability is the most important criterion for choosing the best
design from multiple correct alternatives.
A good design should overcome human cognitive limitations, especially the constraints of
short-term memory (as discussed in Chapter 1). A poor design overwhelms developers,
increases errors, and pushes up development costs.
Modularity
A modular design is one in which the software is broken down into a set of modules with
minimal interactions. Each module:
Benefits of Modularity:
• Divide and Conquer: Each module can be understood independently, which reduces
complexity.
• Improved Debugging: Errors can be isolated within individual modules.
• Parallel Development: Teams can work on separate modules concurrently.
• Figure 5.2(a): Low interaction among modules → Easier to understand and maintain.
Quantifying Modularity:
Although modularity cannot be measured directly, it can be characterized in terms of:
A good design has high cohesion and low coupling, leading to effective problem
decomposition.
Layered Design
For example:
• A user interface module at the top layer may call a business logic module, which in
turn calls a data access module.
Key Takeaways
• Cohesion represents how strongly related and focused the elements within a single
module are.
• Coupling represents the level of interdependence between different modules.
The goal of good software design is to maximize cohesion and minimize coupling, thereby
producing modular, maintainable, and reusable software.
1. Cohesion
• All the functions and data within it are directed toward accomplishing a single well-
defined task.
• There is minimal or no inclusion of unrelated responsibilities.
• If every sentence in the speech supports one central theme, the speech is cohesive.
• If the sentences are disjointed and unrelated, the speech lacks cohesion.
2. Coupling
Loose coupling is desirable because it ensures that changes in one module do not heavily affect
other modules, reducing the risk of ripple effects throughout the system.
3. Functional Independence
When a module has high cohesion and low coupling, it is said to be functionally independent.
Functional independence is one of the most critical objectives in software design because it
leads to several key advantages:
a) Error Isolation
b) Reusability
c) Understandability
Classification of Cohesiveness
Cohesiveness of a module indicates the extent to which the functions within the module
cooperate to accomplish a single well-defined objective. Modules in a software design can
vary significantly in their degree of cohesiveness. Figure 5.3 illustrates the classification of
cohesiveness, ranging from the weakest form (coincidental cohesion) to the strongest and most
desirable form (functional cohesion).
As we move from coincidental to functional cohesion, the quality of the design improves,
making the software more understandable, maintainable, and reusable.
1. Coincidental Cohesion
2. Logical Cohesion
• Definition: A module has logical cohesion if its functions perform similar operations
but are logically categorized rather than being functionally connected.
• Example: A module containing multiple print functions such as printGradeSheet(),
printSalarySlip(), and printAnnualReport().
• Drawback: While functions are similar in nature, they are not aimed at achieving a
single well-defined task.
• Impact: This improves over coincidental cohesion but still lacks strong modularity.
3. Temporal Cohesion
• Definition: Temporal cohesion occurs when a module’s functions are related because
they are executed during the same time frame or phase.
• Example:
o A system start-up module that initializes memory, configures devices, and loads
the operating system.
o Similarly, shutdown or initialization modules in other applications.
• Observation: These functions are grouped due to their timing rather than their
functional relationship.
• Impact: Temporal cohesion is better than logical cohesion but still does not promote
functional independence.
4. Procedural Cohesion
• Definition: A module has procedural cohesion if its functions are executed sequentially
in a predefined order but perform unrelated operations.
• Example: In an order-processing module of a trading system:
o Functions include login(), placeOrder(), checkOrder(), printBill(),
placeOrderOnVendor(), updateInventory(), and logout().
o While these are executed one after the other, each function performs a different
task.
• Impact: Though execution order gives a sense of structure, it does not improve
functional integration among the module’s functions.
5. Communicational Cohesion
6. Sequential Cohesion
• Definition: A module has functional cohesion if all its functions work together to
perform one single task.
• Example: A payroll module containing functions such as:
o computeOvertime()
o computeWorkHours()
o computeDeductions()
Together, these functions generate employee pay slips.
Determining Cohesion
Conclusion
A highly cohesive module has tightly related functions that interact heavily to achieve a single
goal. If one function is moved out, coupling would increase because the function would now
need to communicate across module boundaries. Thus, striving for functional cohesion is
essential for creating a robust, modular, and maintainable software design.
Classification of Coupling
The degree of coupling between two modules mainly depends on their interface complexity,
which is determined by:
Figure 5.5 illustrates the different types of coupling, arranged from the lowest (best) form of
coupling (data coupling) to the highest (worst) form (content coupling).
• Definition: Two modules are data coupled if they communicate only through
elementary data items passed as parameters, such as integers, floats, or characters.
• Key Points:
o The data exchanged is related to the problem domain.
o No control information (e.g., flags) should be passed.
• Example:
int calculateSquare (int number);
• Impact:
• This is the most desirable form of coupling because the modules remain highly
independent.
• Such modules are easier to understand, maintain, and test.
2. Stamp Coupling
• Definition: Stamp coupling occurs when two modules share a composite data
structure (e.g., records in Pascal or structures in C).
• Key Points:
The entire structure is passed even if only a part of it is needed.
• Example:
struct Student {
char name[50];
int marks;
int rollNo;
};
void printStudentDetails(struct Student s);
Here, the printStudentDetails() function might only need the student's name, but the
entire structure is passed.
• Impact:
o Reduces modularity since unnecessary data is exposed.
o Changes in the structure can affect multiple modules, leading to high
maintenance costs.
3. Control Coupling
• Definition: Control coupling exists when one module controls the execution flow of
another by passing control information (e.g., flags or codes).
• Key Points:
o The called module's behavior changes based on control parameters.
Example:
void processData(int flag) {
if(flag == 1)
// process for case 1
else
4. Common Coupling
• Definition: Common coupling occurs when multiple modules share global data.
• Key Points:
o Modules can read and modify the same global variable, which can lead to
unwanted side effects.
Example:
int globalCounter;
void incrementCounter() { globalCounter++; }
void resetCounter() { globalCounter = 0; }
Conclusion
High coupling among modules significantly increases software complexity and cost.
Therefore, a good design must focus on minimizing coupling while promoting high
cohesion. The ideal design strives for data coupling and avoids content or common
coupling, enabling modules to be developed, tested, and maintained independently.
Introduction
The concept of patterns originated in architecture, where standard solutions were created
for building designs. This concept has now been adapted to object-oriented analysis and
design (OOAD) to support design reuse. Patterns help in solving recurring design
problems systematically by applying proven solutions rather than reinventing them for
every project.
• Definition:
• Benefits:
When designers recognize a recurring problem, they can apply the corresponding pattern
instead of starting from scratch.
By mastering a few important patterns, developers can spot these subproblems in new
projects and reuse the documented solutions effectively.
• Patterns are not revolutionary techniques but are built on common sense and proven
design principles.
4. Applicability: When the solution works and when it should not be used.
Types of Patterns
Patterns exist at different levels of abstraction:
1. Architectural Patterns
• Definition: High-level strategies that deal with the overall structure of large software
systems.
• Purpose:
• Characteristics:
• Usage: Suitable for very large systems (e.g., Client-Server, Layered Architecture).
2. Design Patterns
• Definition: Solutions to recurring design problems at the class and object level.
• Purpose:
3. Idioms
• Definition: Low-level, language-specific patterns that describe how to implement a
solution using a specific programming language’s features.
• Analogy: Similar to idioms in natural languages (e.g., "raining cats and dogs"), which
provide concise, widely understood meanings.
Comparison of Patterns
Architectural patterns define the global structure, design patterns focus on class
interactions, and idioms deal with implementation details.
• Algorithms: Focus on solving specific problems efficiently in terms of time and space
complexity.
Advantages
Disadvantages
• Do not directly result in code reuse (they are conceptual, not code-specific).
• No universal methodology to select the right pattern for every design scenario.
Antipatterns
If a pattern represents a best practice, an antipattern represents a bad design practice
that should be avoided.
Examples of Antipatterns:
• Magic Pushbutton: Business logic coded directly inside the UI instead of separate
classes.
Antipatterns are valuable because they help designers recognize and avoid bad design
choices early.
Conclusion:
Patterns act as reusable blueprints for software design, helping to improve
maintainability, reduce complexity, and guide designers towards robust solutions. By
mastering patterns, designers can develop efficient, scalable, and maintainable software
while avoiding common pitfalls through awareness of antipatterns.
Although patterns represent generic solutions, they often require adaptation for specific
problem contexts. The most widely used patterns are those documented by Larman and
the Gang of Four (GoF): Erich Gamma, Richard Helm, Ralph Johnson, and John
Vlissides.
1. Facade Pattern
Problem: How should services be requested from a package by client classes?
Context:
• For example, an RDBMS interface package might contain multiple classes for database
operations.
Solution:
• Create a Facade class (e.g., DBFacade) that provides a unified interface for accessing
services within the package.
Explanation:
• Without a facade, clients need to know all the internal classes and their methods.
• By using a facade, only the facade class changes, while client classes remain unaffected.
Benefits:
(Figure 8.2 illustrates how facade reduces dependency between clients and the package.)
3. Observer Pattern
Problem: When a model object is accessed by multiple view objects, how should
interactions between them be structured?
Solution:
• When the model changes, it notifies all observers. Each observer can then query the
model for specific details.
Explanation:
Limitations:
• The model must maintain observer lists and handle updates, creating overhead.
(Figure 8.3 shows the interaction diagram for the observer pattern.)
Solution:
Explanation:
Benefits:
• Supports multiple views (e.g., line chart, bar chart, pie chart) of the same data.
(Figures 8.4 and 8.5 illustrate MVC class and collaboration diagrams.)
4. Publish-Subscribe Pattern
Problem: When a model object has many dependent views, and its state changes
asynchronously, how should updates be handled efficiently?
Solution:
o Subscribers register their interest in specific events with the event manager.
Explanation:
Benefits:
(Figures 8.6 and 8.7 illustrate event flow in the publish-subscribe pattern.)
Context:
Solution:
• The proxy forwards requests to the server, receives responses, and returns them to the
client.
Explanation:
• The proxy behaves like the server from the client's perspective.
o Transmitting requests.
Benefits:
Adapter Pattern
Problem: How can incompatible interfaces between two classes be resolved so that they
can work together?
Context:
In many software systems, we encounter situations where an existing class offers the
desired functionality but has an interface that does not match the one required by the client.
Modifying the existing class might not be feasible because it may be part of a library or
reused in other systems.
Solution:
The Adapter pattern introduces an intermediate class, called the adapter, that translates the
interface of the existing class (adaptee) into the interface expected by the client. The client
interacts only with the adapter, which internally delegates requests to the adaptee using the
appropriate conversions.
Explanation:
The Adapter acts like a bridge between the client and the incompatible class. This allows
integration without changing the existing class implementation. For example, if a drawing
application expects an interface Shape, but we have an existing LegacyRectangle class with
a different method signature, an adapter can be created to implement Shape while internally
using LegacyRectangle methods.
Benefits:
• Provides a clean separation between client code and legacy or third-party code.
Command Pattern
Problem: How can requests be encapsulated so that they can be queued, logged, undone,
or executed later?
Context:
In many systems, it is useful to treat requests or operations as first-class objects so they can
be passed around, stored, or executed at different times. For instance, in a text editor,
operations like "copy," "paste," or "undo" need to be executed flexibly while maintaining
a record for undo/redo functionality.
Solution:
The Command pattern encapsulates each request into a separate command object. Each
command object implements a common interface, typically containing an execute()
method. The client creates a command object and passes it to an invoker, which executes
the command without knowing its implementation details. The command internally calls
the appropriate method of the receiver object.
Explanation:
This pattern decouples the sender (invoker) from the receiver. It also allows operations such
as undo/redo by maintaining a history of executed command objects. For example, a "Save"
operation in a text editor can be implemented as a command object that knows how to call
the saveDocument() method on the receiver.
Benefits:
Strategy Pattern
Problem: How can different algorithms or behaviors be selected dynamically at runtime
without changing the context class?
Context:
Many applications require multiple algorithms to perform a specific task. For example, a
payment system may support different payment methods like credit card, UPI, or PayPal.
Without the Strategy pattern, the context class might contain multiple conditional
statements to handle each algorithm, making the code hard to maintain.
Solution:
The Strategy pattern defines a family of algorithms, encapsulates each one in a separate
class, and makes them interchangeable. The context class maintains a reference to a strategy
interface and delegates the algorithm execution to the strategy object currently set.
Explanation:
By encapsulating algorithms, the Strategy pattern eliminates the need for complex
conditional logic within the context. For example, a PaymentProcessor context can use a
PaymentStrategy interface. Specific strategies such as CreditCardPayment, UPIPayment,
or PayPalPayment implement this interface. At runtime, the desired strategy is injected into
the context, allowing the payment method to be changed dynamically.
Benefits:
1. Layered Architecture
Description
Layered architecture organizes the system into hierarchical layers where each layer
performs a specific role and only interacts with the layer directly above or below it.
Typically, systems have layers such as Presentation, Business Logic, and Data Access.
Each layer has clearly defined interfaces and responsibilities, making it easier to manage
complexity and evolve the system over time.
Real-time Example
In a university management system, the presentation layer manages user interactions (like
student portals), the business logic layer processes rules (like course registrations or fee
calculations), and the data layer handles storage and retrieval (like student records in a
database).
Benefits
• Supports scalability and changes in technology at one layer without impacting others
Limitations
2. Client-Server Architecture
Description
In this style, the system is divided into two main components: clients that request services
and servers that provide them. Clients are typically user-facing applications (e.g.,
browsers), while servers handle processing, data, or services (e.g., web servers or
databases). This architecture is foundational to internet and networked applications.
Real-time Example
In a banking application, the mobile app (client) sends transaction requests to a central
banking server (server), which processes the request, interacts with databases, and returns
the response to the app.
Benefits
Limitations
Real-time Example
An e-commerce website uses a web browser (presentation tier), an application server
handling shopping cart logic (business tier), and a database server storing
product/catalogue information (data tier).
Benefits
Limitations
by data streams (pipes). Each filter performs a transformation on the input and passes the
output to the next filter. This promotes the building of systems from small, reusable units.
Real-time Example
A compiler works in phases: lexical analysis → syntax analysis → semantic analysis →
code generation. Each phase acts as a filter that processes input and forwards output to the
next.
Benefits
Limitations
UI design goes beyond just aesthetics—it ensures that the system is usable, accessible, and
responsive to user actions. A well-designed UI should minimize the cognitive load of users
and make navigation seamless by using consistent and familiar patterns. It also ensures that
the software is visually aligned with its purpose, audience, and platform (web, mobile,
desktop, etc.).
Real-Time Example
Consider a food delivery app like Swiggy or Zomato. The user interface must allow
customers to quickly browse through restaurants, apply filters, select dishes, place orders,
and track delivery. A clean layout, readable fonts, intuitive icons (like a magnifying glass
for search or a cart icon for orders), and responsive feedback (like progress bars or success
messages) all contribute to a good UI experience. Even subtle animations and color choices
influence user behavior and satisfaction.
2. Increased Productivity
In business or enterprise applications, efficient UI design allows users to perform
tasks more quickly, reducing time and effort.
4. Improved Accessibility
Good UI considers diverse users, including those with disabilities, by supporting
screen readers, keyboard navigation, or high-contrast themes.
5. Competitive Advantage
In a crowded market, applications with superior UI often stand out and attract more
users simply by being more usable and visually appealing.
Limitations of UI Design
3. Technology Constraints
A well-designed UI might not always be technically feasible due to platform
limitations, performance issues, or hardware constraints (especially in embedded or
low-spec environments).
4. Overemphasis on Aesthetics
Sometimes teams focus too much on visual appeal and neglect usability or
accessibility, which defeats the purpose of UI design.
5. Constant Evolution
UI standards and user expectations evolve rapidly. Designers need to keep updating
the UI based on feedback, trends, and new device form factors.
Conclusion
User Interface Design is a critical component of software engineering that bridges human
psychology, technology, and creativity. While it's sometimes undervalued compared to
backend logic or system architecture, UI is often the deciding factor in user satisfaction
and product success. A good UI anticipates user needs, responds gracefully to errors, and
provides clear guidance throughout the user’s journey—making software not just
functional, but delightful to use.