SDA Notes MN
SDA Notes MN
CLASS DIAGRAM:
A class diagram is a visual representation of the classes in your Java application, along with their attributes and methods, and
the relationships between them. Here's how to create one:
Define Methods: Note the functions or operations associated with each class.
Recognize Relationships: Identify how classes interact, such as inheritance, association, or aggregation.
Class Box:
Relationships:
o Inheritance: A solid line with a hollow arrowhead pointing from the child class to the parent class.
o Aggregation: A hollow diamond on one end of the line, indicating a "has-a" relationship.
Draw Class Boxes: Represent each class as a rectangle with three compartments.
Add Attributes and Methods: List the variables and functions within each class box.
Connect Classes: Use lines and arrows to show relationships between classes.
Label Relationships: Clearly label the type of relationship (inheritance, association, etc.).
Plus (+) Public: The member can be accessed from anywhere in the program. It's visible to other classes and
objects.
Minus (-) Private: The member can only be accessed within the class itself. It's hidden from other classes and
objects.
Example:
Lecture 03
OOP BASICS:
Abstraction:
Think of it like a blueprint: It's about focusing on the essential features of an object and hiding the complex details.
Example: Consider a car. You only need to know how to drive it, not the inner workings of the engine.
Encapsulation:
Imagine a capsule: It's about bundling data (attributes) and methods (actions) together within a single unit (class).
Example: A car class would encapsulate properties like color, model, and methods like start(), stop().
Polymorphism:
Think of it as taking different forms: It means objects can take on different forms or behaviors depending on the
context.
Example: A "drive" method can be applied to a car or a bicycle, but the action will be different for each.
Inheritance:
Think of it as family relationships: It's about creating new classes (child classes) that inherit properties and methods
from existing classes (parent classes).
Example: A "SportsCar" class could inherit from a "Car" class, inheriting common properties like color and model, but
adding specific features like a turbocharger.
Composition:
Think of it as assembling components: Instead of inheriting behavior and properties from a parent class, a class is
composed of several smaller, independent objects (or components) that each handle specific tasks.
Example: Instead of a "SportsCar" inheriting from a "Car" class, we could have a "SportsCar" class that has various
components (objects) like an Engine, Transmission, and Wheels, each represented by its own class. The "SportsCar"
class would use these components to gain functionality rather than inheriting from a parent class.
In essence, OOP allows you to model real-world objects and their relationships in a structured and organized way, making
code more reusable, understandable, and maintainable.
Overridden Methods:
In Java, method overriding is a feature that allows a subclass to provide a specific implementation for a method that is already
defined in its superclass. This is done to customize the behavior of the inherited method for the subclass.
Method Signature: The overridden method in the subclass must have the same signature as the original method in
the superclass, including the same name, return type, and parameter list.
Access Modifiers: The overridden method's access modifier can be the same or more permissive than the original
method's. For example, if the superclass method is public, the subclass method can be public or protected, but not
private.
@Override Annotation: While optional, it's recommended to use the @Override annotation to indicate that a
method is intended to override a superclass method. This can help catch potential errors during compilation.
In this example, the Dog class overrides the makeSound() method inherited from the Animal class. When an instance of Dog
calls the makeSound() method, the overridden implementation in the Dog class is executed, printing "Woof!" instead of the
generic animal sound.
1. Reference Variable: A reference variable can point to objects of different subclasses at runtime.
2. Method Call: When a method is called on a reference variable, the JVM checks the actual object's type at runtime.
3. Method Resolution: The appropriate method implementation is selected based on the actual object's type, even if
the reference variable's declared type is a superclass.
In this example, even though the reference variable animal is declared as type Animal, it actually refers to a Dog object. When
the makeSound() method is called, the JVM determines that the actual object is a Dog, and therefore calls the overridden
makeSound() method from the Dog class.
Dynamic method binding enables polymorphism, allowing different objects to respond to the same method call in
different ways.
It promotes code reusability and flexibility by allowing subclasses to provide specialized implementations of inherited
methods.
Behaviors:
IS-A (Inheritance): This represents an "is a" relationship, where a subclass inherits properties and behaviors from a
superclass.
o Arrow: A solid line with a hollow arrowhead pointing from the subclass to the superclass.
o Example in the diagram: MallardDuck, RedheadDuck, RubberDuck, and DecoyDuck inherit from Duck.
HAS-A (Composition/Aggregation): This indicates a "has a" relationship, where one object contains another object as
a part.
o Arrow: A solid line with a diamond on one end, pointing towards the containing object.
o Arrow: A dashed line with a hollow arrowhead pointing from the implementing class to the interface.
o Example in the diagram: FlyWithWings and FlyNoWay implement FlyBehavior, while Quack, Squeak, and
MuteQuack implement QuackBehavior.
In summary:
DESIGN PRINCIPLES:
In software design and architecture, there are key principles that guide the creation of clean, maintainable, and scalable
systems. These principles form the foundation for good software design:
1. Modularity: Break down a system into smaller, independent modules that focus on a single responsibility. Each
module should be self-contained and able to work on its own or with other modules.
2. Separation of Concerns (SoC): Divide the system into distinct sections, each handling a specific aspect or concern. For
example, separate the user interface from the business logic and the data layer.
3. Single Responsibility Principle (SRP): Each component or class should have only one reason to change. This helps to
keep the code focused and easier to maintain.
4. Open/Closed Principle (OCP): Software entities like classes should be open for extension but closed for modification.
This means you can add new features or changes without altering existing code, which helps to minimize errors.
5. Encapsulation: Keep internal details hidden from the outside, exposing only what's necessary. This protects parts of
the program from being affected by changes in other parts.
6. DRY (Don't Repeat Yourself): Avoid duplicating code by ensuring each piece of logic is written only once. This reduces
errors and makes the code easier to maintain.
7. High Cohesion and Low Coupling: Aim for high cohesion (each part does its job well) and low coupling (components
are not overly dependent on one another). This makes the code easier to modify and test.
8. SOLID Principles: A set of five principles (SRP, OCP, Liskov Substitution, Interface Segregation, and Dependency
Inversion) that provide guidelines for object-oriented design.
These principles help structure code that is robust, scalable, and easy to understand, forming the backbone of any well-
designed software system. In a "Stage 4" scenario, such as in a more advanced phase of design, these principles become
especially critical, as the system's complexity and the need for maintainability increase.
2. Encapsulate (isolate) those changes: Once you've identified the parts that might vary, keep them separate from the
rest of the code. This way, if you need to update or modify them later, you can do so without affecting the entire
system.
The benefit? This approach makes your application more flexible and reduces the risk of bugs. When you change or add new
features, you only need to work on the isolated, changeable parts without touching the stable parts. This leads to fewer
unexpected issues and a more manageable, adaptable codebase.
In short, keep the parts that vary isolated so you can update them easily without affecting the rest of the application.
1. Use interfaces to define behaviors: Instead of writing code that directly defines how a duck flies or quacks, create a
general "interface" (or set of rules) for flying and quacking behaviors. This way, different types of flying or quacking
can be added without changing the core duck code.
2. Assign behaviors dynamically: By using interfaces, you can assign specific behaviors to each duck. For example, one
duck might fly with wings while another might not fly at all. This allows you to change how each duck behaves without
altering the core duck class.
3. Flexibility and adaptability: Since behaviors are defined by interfaces, you can create new types of flying or quacking
without modifying the main duck class. This makes your code flexible, reusable, and easier to extend in the future.
Benefit: By programming to an interface, your code becomes more adaptable. You can easily add or change behaviors (like new
ways to fly or quack) without altering the core duck class, making the system more maintainable and scalable.
Maintainability: It keeps code simple and modular because each behavior is a separate piece.
Bottom line: Build classes by combining parts (composition) instead of making them inherit behavior directly. This makes your
code more adaptable and easier to manage.
Lecture 04 - 07
DESIGN PATTERNS:
1. Observer Pattern:
The Observer pattern is a behavioral design pattern that defines a one-to-many dependency between objects, meaning that
when one object (the subject) changes its state, all its dependents (observers) are notified and updated automatically.
It's like a newspaper subscription: You (the observer) subscribe to a newspaper (the subject). When a new article is published,
you (the observer) are notified.
Examples:
1. Social Media Notifications:
Notification: When you post a new update, your followers are notified.
Notification: When a stock price changes, traders and investors are notified.
3. Email Notifications:
4. Weather Updates:
5. Game Development:
Observers: Other game objects, the game's user interface, and sound effects.
Notification: When the game object's state changes (e.g., health, position), other objects are notified and react
accordingly.
In all of these examples, the Observer Pattern allows for a loose coupling between the subject and the observers.
The subject doesn't need to know the specific details of each observer, and observers can be added or removed without
affecting the subject or other observers. This makes the system more flexible and easier to maintain.
2. Decorator Pattern:
Think of adding toppings to a pizza: You start with a basic pizza (the component) and add toppings (decorators) to
customize it. You can add multiple toppings, changing the pizza's appearance and functionality.
Key Components:
o Component: The interface or abstract class that defines the core functionality.
o Decorator: An abstract class or interface that extends the component interface and adds additional
functionality.
Benefits:
Functionality: You can add these decorators to a basic cup of coffee to create various customized drinks.
2. Car Manufacturing:
Functionality: You can add these features to a basic car model to create different car configurations.
3. Clothing:
Functionality: You can customize a plain t-shirt with various decorations to create unique designs.
4. Food:
Functionality: You can add different toppings to a plain pizza to create various pizza flavors.
5. Software Development:
Base Component: A core application functionality.
Functionality: You can add these features to the core application to enhance its performance and security.
In all these examples, the Decorator Pattern allows you to add new functionalities to an object dynamically, without modifying
the core object itself. This promotes flexibility and reusability in software design.
3. Adapter Pattern:
Imagine a foreign plug adapter: You have a device that uses a different plug type (the target interface) than the
power outlet (the source interface). You use an adapter to connect the two, allowing the device to work with the
outlet.
Key Components:
o Adapter: The class that adapts the source interface to the target interface.
Benefits:
o Reusability: You can reuse existing classes that don't fit the target interface.
o Compatibility: You can make different interfaces compatible with each other.
Examples:
1.Power Adapters:
Adapter: A power adapter that converts the power from a different type of plug to the device's required type.
2. File Formats:
Target: A specific software application that can only read a particular file format (e.g., .doc).
Adapter: A file converter that converts a different file format (e.g., .pdf) to the compatible format.
Adapter: A driver that translates the new software's commands into a format that the old hardware can understand.
Adapter: A protocol converter that translates data from one protocol to another (e.g., from serial to TCP/IP).
Adapter: A wrapper class that adapts the old code to the new system's interfaces.
In all these examples, the Adapter Pattern allows you to make existing interfaces compatible with new ones, promoting
reusability and interoperability.
4. Factory Pattern:
Imagine a car factory:
Instead of directly creating specific car models (like a Sedan, SUV, or Truck), the factory creates them indirectly through a
common interface (Vehicle). This allows the factory to produce different types of vehicles without knowing the exact
implementation details of each model.
Key Components:
Product: The interface or abstract class that defines the common interface for all products.
Creator: The abstract class or interface that declares the factory method.
ConcreteCreator: The concrete implementation of the creator, which implements the factory method to create
specific products.
Benefits:
Decoupling: The client code is decoupled from the specific product implementations, making the code more flexible
and easier to maintain.
Encapsulation: The factory hides the complexity of creating objects, making the client code simpler.
Extensibility: New product types can be added without modifying the client code.
Real-world Example:
Product: The Pizza interface defines a common interface for all pizzas.
ConcreteProduct: Specific pizza types like MargheritaPizza, PepperoniPizza, etc., implement the Pizza interface.
Creator: The PizzaFactory class defines the factory method to create different types of pizzas.
ConcreteCreator: The PizzaShop class uses the PizzaFactory to create pizzas based on customer orders.
By using the Factory Pattern, the pizza shop can easily add new pizza types without modifying the core logic of the ordering
process.
Examples:
1. Database Connection Factory:
Product: The DatabaseConnection interface defines a common interface for database connections.
Creator: The DatabaseConnectionFactory class defines the createConnection() method to create database
connections.
ConcreteCreator: The application uses the DatabaseConnectionFactory to create database connections based on
configuration settings.
ConcreteProduct: Specific GUI component implementations like Button, TextField, Label, etc., implement the
GUIComponent interface.
Creator: The GUIFactory class defines the createButton(), createTextField(), createLabel(), etc., methods to create GUI
components.
ConcreteCreator: The application uses the GUIFactory to create GUI components based on the platform (e.g.,
Windows, macOS, Linux) and user preferences.
3. Logging Factory:
ConcreteProduct: Specific logger implementations like FileLogger, ConsoleLogger, DatabaseLogger, etc., implement
the Logger interface.
Creator: The LoggerFactory class defines the createLogger() method to create loggers.
ConcreteCreator: The application uses the LoggerFactory to create loggers based on configuration settings (e.g., log
level, log format, log destination).
4. Document Factory:
Creator: The DocumentFactory class defines the createDocument() method to create documents.
ConcreteCreator: The application uses the DocumentFactory to create documents based on the file format and
content.
These examples demonstrate how the Factory Pattern can be used to decouple the creation of objects from their usage, making
the code more flexible, maintainable, and extensible.
Key Components:
1. Abstract Factory:
o Declares an interface for creating families of related products.
o Doesn't specify concrete product classes.
2. Concrete Factory:
o Implements the abstract factory interface.
o Creates specific product objects.
3. Abstract Product:
o Declares an interface for a type of product.
o Doesn't specify concrete implementation.
4. Concrete Product:
o Implements the abstract product interface.
o Provides a specific implementation of the product.
Imagine a car factory that produces cars in different styles (e.g., luxury, sports, compact) and regions (e.g., Europe, US, Asia).
Instead of having separate factories for each style and region, the factory uses an abstract factory to create families of related
products. This allows for flexible configuration and customization of the products.
Key Components:
Abstract Factory: The interface that declares the operations for creating different product families.
Concrete Factory: The implementation of the abstract factory, which creates specific product families.
Abstract Product: The interface that declares the operations for a specific product type.
Concrete Product: The implementation of the abstract product, which defines the specific behavior of the product.
Benefits:
Flexibility: The abstract factory pattern promotes flexibility by allowing the creation of different product families.
Encapsulation: It encapsulates the creation of product families, making the client code more independent of the
specific implementations.
Consistency: It ensures consistency within a product family by creating related products together.
Examples:
1. Document Creation:
Abstract Factory: The DocumentFactory interface defines methods to create different document types (e.g.,
createPDFDocument(), createWordDocument(), createExcelDocument()).
Concrete Factory: Specific DocumentFactory implementations (e.g., PDFDocumentFactory, WordDocumentFactory,
ExcelDocumentFactory) create the corresponding document types.
Abstract Product: The Document interface defines common methods for all document types (e.g., addText(),
addTable(), addImage()).
Abstract Factory: The DatabaseFactory interface defines methods to create database connections and query builders
for different database systems (e.g., createMySQLConnection(), createOracleConnection(),
createSQLServerConnection()).
Abstract Product: The DatabaseConnection and QueryBuilder interfaces define common methods for database
connections and query building.
Concrete Product: Specific database connection and query builder implementations for each database system.
Abstract Factory: The LoggerFactory interface defines methods to create different types of loggers (e.g.,
createConsoleLogger(), createFileLogger(), createDatabaseLogger()).
Abstract Product: The Logger interface defines common methods for logging (e.g., logInfo(), logWarning(), logError()).
Concrete Product: Specific logger implementations (e.g., ConsoleLogger, FileLogger, DatabaseLogger) implement the
Logger interface and provide specific logging mechanisms.
4. GUI Toolkit:
Abstract Factory: The GUIFactory interface defines methods to create buttons, text fields, and other GUI components.
Concrete Factory: Specific GUIFactory implementations (e.g., WindowsFactory, MacOSFactory) create platform-
specific GUI components.
Abstract Product: The Button, TextField, and other GUI component interfaces define the common behavior of these
components.
Concrete Product: The specific implementations of these components for Windows, macOS, or other platforms.
By using the Abstract Factory Pattern, the GUI toolkit can create consistent and platform-specific GUI components
without tightly coupling the client code to the specific platform implementation.
These examples illustrate how the Abstract Factory Pattern can be used to create families of related objects, making the code
more flexible, maintainable, and adaptable to different configurations and environments.
Factory Pattern: Use when you need to create a single type of object and want to hide the creation process.
Abstract Factory Pattern: Use when you need to create families of related objects and want to be able to easily switch between
different product families.
6. Composite Pattern
Composite Pattern
The Composite Pattern is a structural design pattern that allows you to treat individual objects and compositions of objects
uniformly. It creates a tree-like structure where individual objects (leaves) and composite objects (branches) can be treated
similarly.
Key Components:
Component: The base interface that defines common operations for both individual objects and composite objects.
Composite: Concrete implementation of the Component interface representing composite objects. It can contain
other components, both leaf and composite.
Real-world Example:
Component: The File interface defines common operations like open(), close(), and getSize().
Leaf: Concrete implementations of the File interface represent individual files (e.g., text files, image files).
Composite: Concrete implementation of the File interface represents directories. A directory can contain both
individual files and other directories.
Flexibility: New components can be added to the structure without affecting existing code.
By using the Composite Pattern, you can create flexible and reusable object hierarchies that can represent complex structures
in a simple and elegant way.
Examples:
1. File System
A directory can contain both files and other directories, forming a hierarchical structure.
2. Organization Chart
A department can contain both individual employees and other departments, creating a hierarchical structure.
A container can hold multiple GUI components, including other containers, forming a hierarchical structure.
4. HTML DOM
Component: Node interface
An HTML element can contain both text content and other elements, forming a hierarchical structure.
5. Mathematical Expressions
An operation can contain both numbers and other operations, forming a hierarchical structure for complex calculations.
6. Neural Networks
A layer can contain multiple neurons, including other layers, forming a hierarchical neural network structure.
7. Organizational Structure
A department can contain both individuals and other departments, forming a hierarchical organizational structure.
A product bundle can contain both simple products and other product bundles, forming a hierarchical product catalog.
9. Menu Structure
A menu can contain both simple menu items and other menus, forming a hierarchical menu structure.
7. Facade Pattern
Imagine you're ordering a burger at a fast-food restaurant. You don't need to know the intricate details of how the burger is
prepared (e.g., grinding the meat, slicing the tomatoes, baking the bun). You simply place your order with the cashier, and they
handle the complex processes behind the scenes.
The Facade Pattern works similarly. It provides a simplified interface to a complex system. It hides the underlying complexity
and presents a simpler interface to the client.
Key Components:
Subsystem Classes: The underlying classes that perform the complex operations.
Benefits:
Examples:
1. Computer Hardware:
Facade: The BIOS or UEFI firmware provides a simple interface to complex hardware components like the CPU,
memory, and storage devices.
Subsystem Classes: The BIOS/UEFI interacts with the BIOS ROM, CPU registers, memory controllers, and disk
controllers.
2. Database Access:
Facade: A database connection pool provides a simplified interface for obtaining database connections.
Subsystem Classes: The connection pool manages the creation, pooling, and destruction of database connections.
3. Web Applications:
Facade: A framework like Spring or Django provides a simplified interface for building web applications.
Subsystem Classes: The framework interacts with web servers, database systems, and other components.
4. Compiler:
Facade: The compiler frontend provides a simple interface for users to compile source code.
Subsystem Classes: The frontend interacts with the lexer, parser, semantic analyzer, code generator, and optimizer.
5. Operating System:
Facade: The operating system kernel provides a simplified interface for applications to interact with hardware
resources.
Subsystem Classes: The kernel interacts with device drivers, memory management, file systems, and process
scheduling.
6. Network Programming:
Facade: A networking library like Socket.IO or WebSockets provides a simplified interface for client-server
communication.
Subsystem Classes: The library handles low-level socket programming, TCP/IP protocols, and data serialization.
7. Audio/Video Playback:
Facade: A media player provides a simple interface for playing audio and video files.
Subsystem Classes: The media player interacts with codecs, decoders, and rendering libraries.
8. Distributed Systems:
Facade: A service registry or service mesh provides a simplified interface for services to discover and communicate
with each other.
Subsystem Classes: The service registry or mesh handles service discovery, load balancing, and fault tolerance.
9. Game Development:
Facade: A game engine provides a simplified interface for game developers to create games.
Subsystem Classes: The game engine handles graphics rendering, physics simulation, sound effects, and input
handling.
Facade: A machine learning library like TensorFlow or PyTorch provides a simplified interface for building and training
machine learning models.
Subsystem Classes: The library handles tensor operations, gradient descent, and optimization algorithms.
In all these examples, the Facade Pattern simplifies the interaction with complex systems, making it easier to use and maintain.
The MVC Pattern is a software design pattern that separates an application into three interconnected parts:
View: Represents the presentation layer, displaying the information to the user.
Controller: Handles user input and updates the model and view accordingly.
Benefits:
Reusability: The view and controller can be reused with different models.
In essence, the Facade Pattern simplifies complex systems, while the MVC Pattern is a framework for structuring applications
into distinct layers.
Examples:
1. Web Applications:
Model: Represents the data, such as user information, product catalogs, and database interactions.
View: Renders the HTML, CSS, and JavaScript to display the user interface.
Controller: Handles user requests, processes data, and updates the model, which in turn triggers updates to the view.
2. Desktop Applications:
Model: Represents the application's data, such as documents, settings, and user preferences.
View: Renders the user interface, including windows, menus, and dialog boxes.
Controller: Handles user input, updates the model, and triggers updates to the view.
3. Mobile Applications:
Model: Represents the application's data, such as user data, app settings, and network requests.
View: Renders the user interface for different screen sizes and orientations.
Controller: Handles user input, updates the model, and triggers updates to the view.
4. Game Development:
Model: Represents the game state, including player positions, enemy positions, and game objects.
Controller: Handles user input, updates the game state, and triggers updates to the view.
5. E-commerce Applications:
View: Renders the product catalog, shopping cart, and checkout pages.
Controller: Handles user actions like adding products to the cart, checkout, and payment processing.
Model: Represents the content, including articles, pages, and media files.
View: Renders the website's frontend, including the homepage, blog posts, and admin dashboard.
Controller: Handles user input, updates the content, and triggers updates to the view.
View: Renders the user feed, profile pages, and messaging interfaces.
Controller: Handles user actions like posting, commenting, liking, and following.
8. Financial Applications:
Model: Represents financial data, such as stock prices, market trends, and portfolio information.
View: Renders charts, tables, and other visualizations of the financial data.
Controller: Handles user input, updates the financial data, and triggers updates to the view.
Controller: Handles user interactions, filters data, and updates the visualizations.
View: Renders the user interface for monitoring and controlling devices.
Controller: Handles user input, processes sensor data, and sends commands to devices.
In all these examples, the MVC pattern promotes a clear separation of concerns, making the application more modular,
maintainable, and testable.
9. Compound Patterns
A compound pattern is a combination of two or more design patterns that work together to solve a specific design problem. By
combining these patterns, developers can create more robust, flexible, and maintainable software solutions.
Enhanced flexibility: By combining patterns, you can create more adaptable and customizable solutions.
Improved performance: Certain pattern combinations can optimize performance and resource utilization.
Increased code readability: Well-structured compound patterns can make code easier to understand and maintain.
While there are many possible combinations, here are a few common compound patterns:
1. MVC + Facade:
o MVC separates the application into model, view, and controller components.
2. Factory + Singleton:
3. Observer + Strategy:
4. Decorator + Strategy:
Examples:
1. E-commerce Website:
MVC + Facade:
o MVC separates the presentation layer (views), business logic (controllers), and data (models).
2. Game Development:
Composite + Strategy:
o Strategy pattern allows for different AI behaviors for different game objects.
3. Operating System:
Factory + Singleton:
o Singleton pattern ensures a single instance of critical system components (e.g., kernel).
4. Web Framework:
MVC + Facade:
o MVC separates the presentation layer (views), business logic (controllers), and data (models).
Command + Observer:
o Command pattern stores and executes actions like "undo" and "redo."
o Observer pattern updates the image display when changes are made.
6. Network Protocols:
State + Strategy:
o State pattern defines the different states of a connection (e.g., established, closed).
7. Financial Software:
Observer + Strategy:
8. Document Processing:
Composite + Visitor:
o Visitor pattern allows for different operations on the document (e.g., formatting, printing, exporting).
9. Artificial Intelligence:
Observer + State:
o State pattern defines different system states (e.g., idle, running, error).
These are just a few examples of how compound patterns can be used to create robust, flexible, and maintainable software
systems. By combining multiple patterns, you can achieve more complex and powerful solutions.
Differentiating the 9 Design Patterns
Creational Patterns
Structural Patterns
Behavioral Patterns
MVC Pattern: Separates an application into model, view, and controller components.