UML & Software Design Models
UML & Software Design Models
UNIT-I
Software development process: The Waterfall Model vs. The Spiral Model. -The
Software Crisis, description of the real world using the Objects Model. -Classes,
inheritance and multiple configurations-Quality software characteristics. -Description
of the Object-Oriented Analysis process vs. the Structure Analysis Model. Introduction
to the UML Language: Standards, Elements of the language. General description of
various models, The process of Object Oriented software development, Description of
Design Patterns, Technological Description of Distributed Systems.
1
Spiral Model
DevOps Model
DevOps Model
Quality Assurance
Risk Management
Collaboration
Efficiency
Consistency
1. Waterfall Model
Overview:
Key Characteristics:
Sequential and Rigid: Each phase flows into the next, like a waterfall. Once a phase
is completed, it’s difficult to go back to it.
Clear Documentation: Documentation is created at each phase, making it easier to
track progress.
2
Predictability: The project’s outcome, timeline, and cost are defined early in the
process.
Easy to manage for small projects: Its simple structure makes it ideal for small
projects with well-defined requirements.
Advantages:
Disadvantages:
Inflexibility: Once a phase is completed, it’s difficult and costly to go back and make
changes. It’s unsuitable for projects where requirements evolve.
Late Testing: Testing only happens after the implementation phase, which means
bugs are found late in the process.
Not Ideal for Complex Projects: Not suitable for large projects with evolving
requirements or high uncertainty.
2. Spiral Model
Overview:
3
Each iteration involves revisiting these phases, refining the product as it progresses.
Key Characteristics:
Iterative and Incremental: The project is developed in small cycles (spirals), with
each cycle building on the previous one.
Risk Management: Emphasis on identifying and addressing potential risks early on.
Flexibility: Allows for changes and adjustments during each iteration, making it more
adaptable to changes in requirements.
Prototyping: Early prototypes may be created and refined through iterations.
Advantages:
Risk Mitigation: Helps identify and reduce risks at each phase, leading to better
management of uncertainties.
Flexibility: Changes can be made in each iteration, making it suitable for projects
with evolving requirements.
Continuous Feedback: Each iteration involves evaluating and refining the product,
ensuring the system aligns with stakeholder needs.
Suited for Complex Projects: Ideal for large, complex, and high-risk projects.
Disadvantages:
Complexity: The model can be more difficult to manage due to its iterative nature
and emphasis on risk assessment.
Time-Consuming: The repeated risk analysis and evaluation phases can lengthen the
development process.
Requires Expertise: Effective implementation requires experienced managers and a
clear understanding of risk management.
4
Comparison between Waterfall and Spiral Models
The Waterfall model is simple and The spiral model is a lot more
Complexity easy. complex.
Development The waterfall model works in a While the spiral model works in
Method sequential method. the evolutionary method.
Project Size The waterfall model is applicable While the Spiral model is used
Suitability for small projects. for large projects.
5
Aspect Waterfall Model Spiral Model
Software Crisis
Software Crisis is a term used in computer science for the difficulty of writing useful and
efficient computer programs in the required time. The software crisis was due to using the
same workforce, same methods, and same tools even though rapidly increasing software
demand, the complexity of software, and software challenges. With the increase in software
complexity, many software problems arose because existing methods were insufficient.
Suppose we use the same workforce, same methods, and same tools after the fast increase
in software demand, software complexity, and software challenges. In that case, there arise
some issues like software budget problems, software efficiency problems, software
quality problems, software management, and delivery problems, etc. This condition is
called a Software Crisis.
8
Fig.3.Factors of Software Crisis
The cost of owning and maintaining software was as expensive as developing the
software.
At that time Projects were running overtime.
At that time Software was very inefficient.
The quality of the software was low quality.
Software often did not meet user requirements.
The average software project overshoots its schedule by half.
At that time Software was never delivered.
Non-optimal resource utilization.
Challenging to alter, debug, and enhance.
The software complexity is harder to change.
9
Solution of Software Crisis:
There is no single solution to the crisis. One possible solution to a software crisis
is Software Engineering because software engineering is a systematic, disciplined, and
quantifiable approach. For preventing software crises, there are some guidelines:
In software engineering, an object model is a logical interface that represents how objects,
their attributes, and relationships are organized and manipulated. It's a part of the object-
oriented programming (OOP) lifecycle and is used to create an architectural model of a
software or system before development.
Encapsulation
Bundling data and the methods that operate on it into a single unit called a class. This
restricts access to some of an object's components, which helps protect data from external
interference.
Abstraction
Simplifying a concept to its bare essentials to better understand it
Dynamic modelling
A method that describes how objects and components in a system interact and change over
time. It focuses on showing the behaviours, events, and transitions that occur within a
system.
10
Description of the real world using the Objects Model
The Objects Model is a conceptual framework that represents the world in terms of objects
—entities that encapsulate both state (data) and behaviour (methods or actions). In the
context of describing the real world, this model treats various components or entities of
reality as objects that interact with one another. Here’s how we can apply the Objects Model
to describe the real world:
Definition: An object is anything that can be distinctly identified and has specific
attributes or characteristics.
Examples: A car, a person, a tree, a building, or even more abstract concepts like a
bank account or an event.
2. Attributes (State)
Examples:
A car might have attributes like color, model, engine type, and speed.
A person might have attributes like age, name, height, and profession.
A tree might have attributes like species, height, age, and leaf type.
3. Methods (Behavior)
Definition: Methods refer to the actions or behaviors that an object can perform.
These actions often modify the object’s state or interact with other objects.
Examples:
Definition: Objects often interact with one another, forming relationships. These
interactions can change their states or result in the creation of new objects.
Examples:
5. Encapsulation
Examples:
A phone object has a display, buttons, and internal circuits, but when you interact
with it, you just touch the screen or press buttons without worrying about the
underlying mechanics.
A bank account object hides the internal balance calculations and transactions but
exposes methods to deposit or withdraw money.
6. Abstraction
Examples:
When we use a computer, we typically interact with icons and menus, abstracting
away the complexities of the operating system or hardware architecture.
A car rental service might only show the availability of cars without focusing on
every technical detail of each model.
7. Inheritance
Definition: Inheritance refers to the ability to create new objects based on existing
ones, inheriting their attributes and methods while allowing for additional features or
customization.
12
Examples:
A sedan is a type of car, so it inherits properties like wheels, an engine, and the ability
to drive but also has unique properties like a different seating configuration or trunk
size.
An employee inherits properties from a person but has additional attributes like salary
or job title.
8. Polymorphism
Examples:
A dog and a cat might both respond to a "speak" method, but a dog barks, and a cat meows.
Different vehicles (cars, bikes, trucks) can implement the "start" method, but each one will
start differently.
Definition: Objects can trigger events or respond to events that occur in their environment.
These events can cause objects to change their state or initiate actions.
Examples:
A person might receive an email (event) which prompts the person to check the email
(action).
A car might sense that its fuel is low and trigger an alert (event) for the driver to refuel.
Definition: Every object has a lifecycle: creation, state changes, and destruction.
Examples:
A plant starts as a seed (creation), grows through different stages (state changes), and
eventually dies (destruction).
In the context of object-oriented programming (OOP) and the Objects Model, classes,
inheritance, and multiple configurations (also known as multiple inheritance or composition-
based configurations) are key concepts that help organize and structure the real world as
objects.
13
Here's a breakdown of these concepts and how they work together:
1. Classes
A class is a blueprint or template for creating objects (instances). It defines the attributes
(properties or state) and methods (behaviors or actions) that objects of that class will have.
Definition: A class is like a mold, and each object created from the class is an
instance of that class.
Key Points:
o Attributes are variables that store data about the object.
o Methods are functions that define behaviors for the objects.
Example of a class:
Python Copy code
class Car:
def __init__(self, brand, model, year):
self.brand = brand # Attribute
self.model = model # Attribute
self.year = year # Attribute
def drive(self):
print(f"The {self.year} {self.brand} {self.model} is driving.") # Method
Class Car defines the attributes (brand, model, year) and the method
drive().
An object my_car is an instance of the Car class.
2. Inheritance
Inheritance is a mechanism by which one class can inherit attributes and methods from
another class. It allows for code reuse and can help structure hierarchies of objects.
14
Example of inheritance:
class Vehicle:
def __init__(self, brand, model):
self.brand = brand
self.model = model
def move(self):
print(f"The {self.brand} {self.model} is moving.")
def honk(self):
print(f"The {self.year} {self.brand} {self.model} honked.")
Class Car inherits from the class Vehicle, so it has the move() method from Vehicle and
also has its own honk() method.
The Car class can override methods and add new functionality, such as the honk()
method.
3. Multiple Inheritance
Multiple inheritances occur when a class inherits from more than one parent class. It allows
a subclass to inherit features from multiple classes, combining their behaviors and attributes.
Definition: A class can have multiple base classes and inherit from all of them. This
can lead to more complex relationships.
Key Points:
o Python (and many OOP languages) supports multiple inheritance.
o Multiple inheritances allow you to combine the behavior of several classes
into a single class, but it also can introduce complications like method
resolution order (MRO).
class Engine:
def start(self):
print("Engine started.")
15
class Radio:
def play_music(self):
print("Playing music.")
class Car(Engine, Radio): # Car inherits from both Engine and Radio
def drive(self):
print("Car is driving.")
This allows Car objects to have methods from both parent classes, enabling it to start the
engine and play music while driving.
Instead of multiple inheritance, which can sometimes lead to issues with complexity or
ambiguity, composition is a preferred approach in many cases. Composition involves
creating classes with attributes that are instances of other classes. This can lead to cleaner and
more modular designs.
class Engine:
def start(self):
print("Engine started.")
class Radio:
def play_music(self):
print("Playing music.")
class Car:
def __init__(self):
16
self.engine = Engine() # Composition: Car has an Engine
self.radio = Radio() # Composition: Car has a Radio
def drive(self):
self.engine.start() # Delegating the start method to Engine
self.radio.play_music() # Delegating the play_music method to Radio
print("Car is driving.")
Class Car has instances of Engine and Radio as part of its composition.
The Car class delegates specific behaviors (like starting the engine and playing music) to its
composed objects.
When dealing with complex systems, classes and inheritance allow flexibility, but real-world
applications often require different configurations. Some common configurations in OOP
include:
class Car:
def __init__(self, strategy):
self.strategy = strategy
def drive(self):
self.strategy.drive() # Strategy pattern to choose driving behavior
class NormalDrive:
def drive(self):
print("Driving normally.")
class SportsDrive:
def drive(self):
print("Driving fast and aggressively.")
17
# Create a Car with NormalDrive strategy
normal_car = Car(NormalDrive())
normal_car.drive()
Multiple configurations in software development refer to the ability to design a system with
variations or options for its components or behaviors. These configurations can be achieved
through techniques like inheritance, composition, polymorphism, and design patterns,
which make the system adaptable and reusable in different contexts.
With inheritance and polymorphism, you can create various configurations of a class
that change its behavior depending on the context. This allows a single piece of
software to adapt to different situations without needing to rewrite code.
Example: Consider a system for handling payment methods in an e-commerce
application. You might have a base PaymentMethod class, and subclasses such as
CreditCardPayment, PayPalPayment, and GiftCardPayment. Each subclass has a
different way of processing payments, but they all share a common interface and can
be treated interchangeably.
Python code
class PaymentMethod:
def process_payment(self, amount):
pass
class CreditCardPayment(PaymentMethod):
def process_payment(self, amount):
print(f"Processing credit card payment of {amount}.")
class PayPalPayment(PaymentMethod):
def process_payment(self, amount):
print(f"Processing PayPal payment of {amount}.")
class GiftCardPayment(PaymentMethod):
18
def process_payment(self, amount):
print(f"Processing gift card payment of {amount}.")
In this case, the payment system can be extended easily by adding new payment methods
without changing the existing classes. The use of polymorphism allows you to treat all
payment methods uniformly, enhancing flexibility and code reuse.
Example: In a graphical user interface (GUI) system, a window can be composed of various
components such as buttons, text fields, and labels. These components can be customized or
replaced without altering the core logic of the window.
class Button:
def click(self):
print("Button clicked.")
class TextField:
def enter_text(self, text):
print(f"Text entered: {text}")
class Window:
def __init__(self):
self.button = Button()
self.text_field = TextField()
def show(self):
self.button.click()
self.text_field.enter_text("Hello, World!")
window = Window()
window.show()
19
In this scenario, the Window class can easily be configured with different components
(buttons, text fields, etc.), enabling flexibility in how windows are displayed and interacted
with.
By defining an abstract Database class, the system can easily add support for new types of
databases, adhering to the same interface and promoting reuse across different configurations.
Classes in OOP define the structure and behavior of objects, providing a blueprint for
creating instances with shared attributes and methods.
20
Inheritance allows for the creation of subclasses that extend or modify the behavior
of parent classes, promoting code reuse and extensibility.
Multiple configurations enhance software flexibility by allowing the same system to
adapt to different requirements or contexts. This is achieved through inheritance,
composition, and interfaces, which support modularity, variability, and reusability.
By designing software with these principles, developers can create flexible, scalable, and
reusable systems that can evolve to meet changing needs without significant rework.
Quality software refers to software that meets or exceeds the expectations of its users,
performs its intended tasks effectively, and operates efficiently within its environment.
1. Functionality
Definition: The software must perform the required tasks correctly and satisfy the
functional requirements specified by the users or stakeholders.
Key Attributes:
Correctness: The software works as intended and meets the specified requirements.
Appropriateness: The features and capabilities of the software are relevant and
useful to the user.
Example: A payroll system that accurately calculates salaries, taxes, and deductions
according to local laws.
2. Reliability
21
Example: An e-commerce website that remains operational and doesn't crash during
high traffic periods, even if there are some input errors from users.
3. Usability
Definition: The software should be easy to learn, understand, and use, ensuring users
can interact with it effectively.
Key Attributes:
Learnability: How easy it is for new users to accomplish basic tasks the first time
they use the software.
Efficiency: The software should allow users to perform tasks with minimum effort
and time.
Satisfaction: Users should be happy with the user interface and overall experience.
Example: A mobile banking app with intuitive navigation and clear instructions that
allow users to perform transactions easily.
4. Efficiency
Definition: The software should make optimal use of system resources (such as
memory, CPU, and network bandwidth), offering good performance even under load.
Key Attributes:
Resource Usage: The software doesn’t consume excessive system resources (e.g.,
memory or processing power).
Scalability: The software can handle increasing amounts of data or users without
significant performance degradation.
Example: A video streaming app that buffers videos quickly and runs smoothly on
both low and high-end devices.
5. Maintainability
Definition: The software should be easy to modify, enhance, or fix after its initial
release.
Key Attributes:
22
Readability: The code should be easy for developers to understand, with clear
comments and proper documentation.
Testability: The software should be easy to test for defects and performance issues.
Example: A web application that can be easily updated with new features without
affecting existing functionality.
6. Portability
Key Attributes:
Installability: The software can be easily installed and set up across various
platforms.
7. Security
Definition: The software should protect against unauthorized access, data breaches,
and other vulnerabilities.
Key Attributes:
Confidentiality: Ensuring that user data is protected and kept private.
Integrity: Ensuring that data is not altered or corrupted by unauthorized individuals.
Authentication & Authorization: The software should ensure that only authorized
users can access specific features or data.
Resilience: The software should be resilient to malicious attacks (e.g., SQL injection,
cross-site scripting).
Example: A banking application that encrypts user data and has secure authentication
methods such as multi-factor authentication.
8. Interoperability
Definition: The software should work with other systems and applications, enabling
integration and data exchange.
23
Key Attributes:
Data Formats Compatibility: The software can exchange data with other systems in
common formats (e.g., XML, JSON).
Communication Protocols: The software can communicate with other systems using
standard protocols (e.g., HTTP, SOAP).
9. Flexibility
Key Attributes:
Example: A content management system (CMS) that can be customized with plugins
or themes for different business needs.
10. Compatibility
Key Attributes:
Forward Compatibility: The software can work with future versions of operating
systems or hardware.
Example: A video player application that supports a wide range of video file formats
and works on both new and older operating systems.
The Object-Oriented Analysis (OOA) and Structured Analysis (SA) models are two
different approaches to system analysis and design. They both aim to develop a deep
understanding of the problem domain and design software solutions but differ fundamentally
24
in how they approach and represent the system. Below is a comparison of the two
methodologies, along with an explanation of each.
Object-Oriented Analysis (OOA) is a process that focuses on modeling the problem domain
using objects, classes, and their interactions. The main idea is to view the system as a
collection of interacting entities, or objects, which encapsulate both data (attributes) and
behavior (methods).
Concepts in OOA:
Objects: Entities that represent real-world concepts in the problem domain. Each
object has attributes (data) and methods (behavior).
Classes: Blueprints or templates for creating objects. Objects are instances of classes.
Encapsulation: Bundling data and methods together within an object, hiding
implementation details.
Inheritance: Creating new classes based on existing ones, promoting reuse and
hierarchy.
Polymorphism: Objects of different classes can be treated as instances of a common
superclass.
Abstraction: Simplifying complex systems by focusing on essential features and
ignoring the irrelevant details.
Messages: Communication between objects, typically through method calls.
1. Identify Objects and Classes: Analyze the real-world system and identify key
objects or entities that interact in the problem domain. These objects should have
clearly defined attributes and behaviors.
2. Identify Relationships: Define how objects relate to one another. This could involve
inheritance (subclasses) or associations (objects interacting or communicating with
one another).
3. Define Methods: Identify the operations or behaviors that each object should support.
4. Refine the Model: As more detail is gathered, objects and their interactions are
refined. Use UML (Unified Modeling Language) diagrams like Class Diagrams,
Object Diagrams, Sequence Diagrams, and State Diagrams to visualize the system
structure and behavior.
5. Encapsulate and Abstract: Objects and their relationships are abstracted to reduce
complexity, and encapsulation is used to hide unnecessary implementation details.
Example of OOA:
Structured Analysis (SA), also known as the data flow model, is a process focused on
breaking down the system into functions and data flows. It emphasizes identifying and
organizing the functional requirements of the system, focusing on data processes and flow.
Processes: Functions or activities that transform input data into output. Each process
represents a specific action or transformation.
Data Stores: Places where data is stored for later use.
Data Flows: Arrows that represent the movement of data between processes, data
stores, and external entities.
External Entities: Actors outside the system (e.g., users or other systems) that
interact with the system.
Top-Down Design: The system is broken down into smaller, manageable pieces by
progressively detailing functions and processes.
The SA Process:
1. Identify Processes: Break down the system into smaller, manageable functions or
processes that transform inputs into outputs.
2. Identify Data Flows: Determine how data moves between processes, data stores, and
external entities.
3. Data Modeling: Identify and define the data structures and relationships, using tools
like Entity-Relationship Diagrams (ERD).
4. Develop a Data Flow Diagram (DFD): A DFD visually represents processes, data
flows, data stores, and external entities. It shows how data moves through the system.
5. Refine the Model: As more information is gathered, refine the DFD to represent
increasingly detailed levels of functionality, moving from a high-level diagram (Level
0) to lower levels (Level 1, Level 2, etc.).
6. Functional Decomposition: Break down processes into smaller sub-processes,
allowing for a clear hierarchical structure of the system's operations.
Example of SA:
A Data Flow Diagram (DFD) would show how data like "Member Information" or "Book
Inventory" moves through processes like "CheckOutBook" or "SearchCatalog."
26
Comparison of Object-Oriented Analysis and Structured Analysis
Uses UML diagrams like class Uses Data Flow Diagrams (DFD)
Representation diagrams, sequence diagrams, and and Entity-Relationship Diagrams
state diagrams. (ERD).
Focuses on functional
Directly maps to object-oriented
decomposition of processes before
Focus on Design programming (OOP) paradigms,
focusing on design or
focusing on design using objects.
implementation.
Interaction between Models how objects interact Focuses on how processes interact
Components through messages and method calls. via data inputs and outputs.
27
What is UML?
UML Standards
UML has several elements that allow for detailed modeling of systems, categorized into
structural, behavioral, and interaction diagrams.
Structural Elements
28
Behavioral Elements
1. Use Case Diagram: Describes the system’s functionality from a user’s perspective. It
shows actors and the use cases (specific functionality) they interact with.
2. State Machine Diagram: Represents the states of an object during its lifecycle and
the transitions between those states.
3. Activity Diagram: Depicts the flow of control in a system, often used for
representing workflows or business processes.
4. Sequence Diagram: Shows how objects interact in a particular sequence of events,
focusing on message passing between objects.
5. Communication Diagram: Similar to sequence diagrams but focuses on object
relationships rather than time order.
Interaction Elements
1. Structural Models
These models describe the static aspects of a system, such as the relationships
between objects, classes, and components.
Examples: Class Diagram, Object Diagram, Component Diagram.
2. Behavioral Models
These models describe the dynamic aspects of a system, including how objects
interact and how they change over time.
Examples: Use Case Diagram, Activity Diagram, State Diagram.
29
3. Interaction Models
4. Architectural Models
These models define the system’s architecture and how various components or
systems are integrated.
Example: Deployment Diagram.
1. Requirement Analysis:
o Identify and gather the requirements for the system from stakeholders.
o Use Use Case Diagrams in UML to capture functional requirements.
2. System Design:
o Design the overall system architecture using Class Diagrams and
Component Diagrams.
o Identify the classes, relationships, and system components.
3. Implementation (Coding):
o Write the code based on the design. Each class is implemented with its
methods and attributes.
4. Testing:
30
o Test the software for functionality, integration, and usability.
o Use State Diagrams and Sequence Diagrams for specific scenarios and user
interactions.
5. Deployment:
o Deploy the system in its production environment.
o Ensure system integration and readiness for live use.
Design Patterns
31
3. Behavioral Patterns: Deal with object interactions and responsibilities.
o Examples: Observer, Strategy, Command, Iterator, Mediator, State,
Visitor.
Ensures that a class has only one instance and provides a global point of access to that
instance.
1. Client: A node that requests services or resources from other nodes (servers).
2. Server: A node that provides resources or services to clients.
3. Middleware: Software that facilitates communication and data exchange between
clients and servers in a distributed system.
4. Network: The communication channel through which the distributed components
interact.
32
Examples of Distributed Systems:
Cloud Computing: Services like AWS, Microsoft Azure, and Google Cloud, where
resources are distributed across multiple data centers.
Microservices Architecture: A distributed architecture style where the system is
broken down into loosely coupled services that can be independently developed,
deployed, and scaled.
Peer-to-Peer Networks (P2P): A distributed system where each node (peer) acts as
both a client and a server. Examples include BitTorrent or blockchain.
Design patterns are general reusable solutions to commonly occurring problems in software
design. They are not finished code or algorithms but rather templates that can be applied to
specific problems in software development. Design patterns help streamline development,
improve code readability, maintainability, and ensure a systematic approach to solving
recurring design problems.
33
Three Design Patterns with Examples in C Programming
The Singleton Pattern ensures that only one instance of a class (or object) is created and
provides a global access point to that instance. It is useful when exactly one object is needed
to coordinate actions.
Example in C:
// Singleton instance
typedef struct {
int value;
} Singleton;
Singleton* getInstance() {
static Singleton* instance = NULL; // Static ensures single instance
if (instance == NULL) {
instance = (Singleton*)malloc(sizeof(Singleton));
instance->value = 0; // Initialize
}
return instance;
}
int main() {
Singleton* s1 = getInstance();
Singleton* s2 = getInstance();
s1->value = 42;
printf("s1 value: %d\n", s1->value);
printf("s2 value: %d\n", s2->value); // Both point to the same instance
return 0;
}
Importance:
34
2. Factory Pattern (Creational Pattern)
The Factory Pattern provides an interface for creating objects without specifying the exact
class of the object that will be created. It helps delegate object creation to a factory method.
Example in C:
#include <stdio.h>
#include <stdlib.h>
if (strcmp(type, "circle") == 0) {
shape->draw = drawCircle;
} else if (strcmp(type, "square") == 0) {
shape->draw = drawSquare;
} else {
free(shape);
return NULL;
}
return shape;
}
int main() {
Shape* shape1 = createShape("circle");
Shape* shape2 = createShape("square");
if (shape1) shape1->draw();
35
if (shape2) shape2->draw();
free(shape1);
free(shape2);
return 0;
}
Importance:
The Observer Pattern defines a one-to-many dependency between objects so that when one
object changes state, all its dependents (observers) are notified and updated automatically.
Example in C:
We can implement the Observer pattern with function pointers and arrays of callbacks.
#include <stdio.h>
#include <stdlib.h>
#define MAX_OBSERVERS 10
typedef void (*Observer)(int); // Observer function pointer
// Subject (observable)
typedef struct {
Observer observers[MAX_OBSERVERS];
int observer_count;
int state;
} Subject;
subject.state = 20;
notify(&subject);
return 0;
}
Importance:
Design patterns are powerful tools for solving recurring design problems in software
engineering. They provide solutions that are reusable, maintainable, and improve overall
design quality. In C programming:
By applying design patterns, developers can write cleaner, scalable, and maintainable code
while reducing code duplication and improving system structure.
Design patterns are classified into three main categories based on their purpose and
functionality:
1. Creational Patterns
o Focus on object creation mechanisms, trying to create objects in a manner
suitable for the situation.
o They abstract the instantiation process, helping to make a system independent
of how objects are created, composed, and represented.
37
Examples:
o Singleton Pattern
o Factory Method
o Abstract Factory
o Builder Pattern
o Prototype Pattern
2. Structural Patterns
o Focus on class and object composition.
o They define how objects and classes can be combined to form larger
structures, making them flexible and efficient.
Examples:
o Adapter Pattern
o Bridge Pattern
o Composite Pattern
o Decorator Pattern
o Facade Pattern
o Proxy Pattern
3. Behavioral Patterns
o Focus on communication between objects and how they interact and behave.
o They help define how objects communicate in a way that is flexible, efficient,
and reusable.
Examples:
o Observer Pattern
o Strategy Pattern
o Command Pattern
o Chain of Responsibility
o Template Method
o Mediator Pattern
The creational design patterns are particularly useful for handling object creation. To
explain this, we will use an example of a Maze Game. The Abstract Factory Pattern and
Builder Pattern are two prominent creational patterns that can be applied to designing such a
game.
38
Maze Game Example Using Abstract Factory Pattern
Problem:
A Maze game consists of rooms, doors, and walls. The game can have different types of
mazes:
A regular maze
A maze with enchanted rooms
A maze with bombed walls
Instead of hardcoding how each maze is created, we use a creational pattern to decouple the
maze creation logic from the game.
The Abstract Factory pattern provides an interface for creating families of related objects
without specifying their concrete classes. It allows flexibility to create different types of
mazes based on requirements.
#include <stdio.h>
#include <stdlib.h>
// Abstract Components for the Maze
typedef struct Room {
int roomNumber;
void (*enter)(struct Room*); // Entering a room
} Room;
typedef struct Door {
struct Room* room1;
struct Room* room2;
void (*enter)(struct Door*);
} Door;
// Concrete Room Implementations
void enterEnchantedRoom(struct Room* room) {
printf("You have entered an enchanted room #%d. Magical spells surround you!\n", room-
>roomNumber);
}
void enterRegularRoom(struct Room* room) {
printf("You have entered a regular room #%d. It looks ordinary.\n", room->roomNumber);
}
// Abstract Factory for creating maze components
typedef struct MazeFactory {
39
Room* (*createRoom)(int roomNumber);
Door* (*createDoor)(Room* r1, Room* r2);
} MazeFactory;
// Concrete Factory for a Regular Maze
Room* createRegularRoom(int roomNumber) {
Room* room = (Room*)malloc(sizeof(Room));
room->roomNumber = roomNumber;
room->enter = enterRegularRoom;
return room;
}
Door* createRegularDoor(Room* r1, Room* r2) {
Door* door = (Door*)malloc(sizeof(Door));
door->room1 = r1;
door->room2 = r2;
door->enter = NULL; // No specific logic for entering a door here
return door;
}
MazeFactory* createRegularMazeFactory() {
MazeFactory* factory = (MazeFactory*)malloc(sizeof(MazeFactory));
factory->createRoom = createRegularRoom;
factory->createDoor = createRegularDoor;
return factory;
}
// Concrete Factory for an Enchanted Maze
Room* createEnchantedRoom(int roomNumber) {
Room* room = (Room*)malloc(sizeof(Room));
room->roomNumber = roomNumber;
room->enter = enterEnchantedRoom;
return room;
}
MazeFactory* createEnchantedMazeFactory() {
MazeFactory* factory = (MazeFactory*)malloc(sizeof(MazeFactory));
factory->createRoom = createEnchantedRoom;
factory->createDoor = createRegularDoor; // Doors remain regular in enchanted mazes
return factory;
}
// Maze Game
void playMazeGame(MazeFactory* factory) {
Room* room1 = factory->createRoom(1);
Room* room2 = factory->createRoom(2);
Door* door = factory->createDoor(room1, room2);
room1->enter(room1);
room2->enter(room2);
printf("You pass through a door connecting Room #%d and Room #%d.\n", room1-
>roomNumber, room2->roomNumber);
free(room1);
free(room2);
free(door);
}
int main() {
40
printf("Playing a Regular Maze Game:\n");
MazeFactory* regularFactory = createRegularMazeFactory();
playMazeGame(regularFactory);
free(regularFactory);
printf("\nPlaying an Enchanted Maze Game:\n");
MazeFactory* enchantedFactory = createEnchantedMazeFactory();
playMazeGame(enchantedFactory);
free(enchantedFactory);
return 0;
}
Explanation of the Code:
1. Abstract Components:
o Room and Door are the abstract components of the maze.
o enter is a function pointer for behavior when entering a room.
2. Concrete Factories:
o Regular Maze Factory: Produces ordinary rooms and doors.
o Enchanted Maze Factory: Produces enchanted rooms and uses regular doors.
4. Flexibility:
o By using the Abstract Factory pattern, we can easily extend the system to
create different types of mazes without modifying the main game logic.
2. Flexibility:
o New types of mazes (e.g., mazes with bombs) can be added easily by creating
new factories.
41
3. Scalability:
o As new features are added, we can create additional factories without
modifying existing code (open-closed principle).
The Abstract Factory Pattern is a powerful creational pattern that provides flexibility in
creating different types of objects (rooms, doors, etc.) for a Maze Game. It decouples the
maze creation logic from the game, making the system easier to extend and maintain.
Creational patterns like this are critical for systems where object creation logic can vary
based on the context.
The ATM System is a great example for illustrating Use Cases as it involves multiple
functionalities and interactions with users. The ATM system supports user operations like
withdrawing money, checking balances, and depositing cash.
Relationship:
42
o This Use Case is a prerequisite for all other Use Cases, so other functionalities
like "Withdraw Cash" or "Check Balance" depend on successful
authentication.
2. Withdraw Cash
Description:
o The Bank Customer selects the cash withdrawal option, enters the desired
amount, and the ATM dispenses cash.
o The ATM communicates with the Bank Server to verify the user’s balance and
update it.
Steps:
Relationships:
3. Check Balance
Description:
o The Bank Customer selects “Check Balance” to view their account balance.
o The ATM System retrieves the balance from the Bank Server and displays it
to the user.
Steps:
43
5. ATM displays the account balance.
Relationships:
4. Deposit Cash
Description:
o The Bank Customer inserts cash or checks into the ATM.
o The ATM verifies the amount and updates the balance in the Bank Server.
Steps:
Relationships:
1. The actors (Bank Customer, ATM System, Bank Server) are represented.
2. The Use Cases are shown as ovals.
44
3. Relationships like Include and Extend are marked with arrows.
Use Cases and Use Case Models play a vital role in understanding and designing systems like
the ATM.
1. Use Cases identify critical functionalities (e.g., Withdraw Cash, Check Balance).
2. Actors (Bank Customer, Bank Server) illustrate system interactions.
3. Relationships like Include and Extend help modularize and structure the Use Cases.
By modeling the system using Use Cases, we ensure all user interactions are accounted for,
leading to a robust and user-centered design.
1. Identify Actors:
45
o Determine the users or external systems that interact with the system.
o Ask: Who will use the system? and What are their goals?
Example: For an ATM system, actors include Bank Customer, Bank
Server, and Maintenance Staff.
2. Identify System Functionalities:
o List all the functionalities that the system must provide.
o Ask: What tasks or actions does each actor need to perform?
3. Define System Boundaries:
o Establish the scope of the system to ensure you only capture relevant Use
Cases.
o Example: An ATM allows withdrawals, deposits, and balance checks but does
not manage loan requests.
4. Focus on User Goals:
o Use Cases should represent tasks that achieve specific goals for the actors.
o Use goal-based analysis to uncover high-level and detailed Use Cases.
5. Explore Scenarios:
o Identify main and alternative scenarios for each interaction.
o Example: If the “Withdraw Cash” functionality fails due to insufficient
balance, describe the alternative flow.
8. Verify Completeness:
o Ensure each Use Case covers all possible actions and goals related to the actor.
o Insufficient Funds:
1. The system displays an error message: “Insufficient Balance.”
2. The system prompts the user to enter a smaller amount or cancel the
transaction.
Postcondition: The cash is dispensed, and the account balance is updated.
48