This is the process of looking at a problem, system, or situation, and identifying the objects and interactions between those objects.
âď¸
What is Object-Oriented Analysis and Design?
Design is a conceptual solution that meets the requirements â how can we solve the problem
Object-Oriented Design (OOD):
We take the analysis results and mold them into a design that can be implemented in a specific programming language such as Java.
flowchart LR
A[Analysis] --> B[Design] --> C[Implementation \n Java]
Why does it matter?
In the real world, complex systems are often best understood through the interactions of simpler parts. OOA&D leverages this principle to create modular, reusable and flexible software.
Benefits of OOA&D
Modularity: Degree to which a system or computer program is composed of discrete components such that a change to one component has minimal impact on other components. ( ISO/IEC 2011)
UM & 2FA
Benefits of OOA&D
Reusability: Objects and classes created for one project can be used in another, reducing development time and increasing productivity.
Degree to which an asset can be used in more than one system, or in building other assets. (ISO 25010)
Payment processing for different services (e-commerce, in-app purchases)
Benefits of OOA&D
Flexibility: Systems designed with OOA&D can be easily adapted to meet changing requirements.
OO Analaysis in Software Development
OOA is the investigative phase where you dive deep into the problem domain.
Ask the right questions and identify the objects and interactions.
Goal: Create a model of the real world that can be translated into a software system.
Itâs easier to change a design than to change a built system.
OO Design in Software Development
Turn the conceptual model (from OOA) into a blueprint for building the system.
Think about the system architecture, the choice of system components etc.
What is good software?
Understanding Software Quality
ISO 25010 1 defines software quality with a comprehensive set of attributes:
Functionality
Reliability
Usability
Efficiency
Maintainability
Portability
Compatibility
Security
Quality Attribute: Functionality
Definition: The degree to which a product or system provides functions that meet stated and implied needs when used under specified conditions
Completeness, Correctness & Appropriateness
Quality Attribute: Reliability
Definition: Reliability is about the softwareâs capacity to maintain its performance level under stated conditions for a specified period.
đĄď¸ď¸
Maturity, Fault Tolerance and Recoverability
Quality Attribute: Usability
Definition: Usability refers to how well a product or system can be used to achieve specified goals effectively, efficiently, and satisfactorily.
Understandability, Learnability and Operability
Quality Attribute: Efficiency
Definition: Efficiency relates to the relationship between the performance level of the software and the amount of resources used, under stated conditions.
âĄď¸ď¸
Time Behavior, Resource Utilization and Capacity
Quality Attribute: Maintainability
Definition: Maintainability is the ease with which the software can be modified to correct faults, improve performance, adapt to a changed environment, or enhance the product.
đď¸ď¸
Modularity, Reusability and Analyzability
How to design good (OO) software?
Developing good (OO) Software
Understand the requirements
Analyze the requirements in an OO context (OO Analaysis)
Design the software using OO design principles (more on this in the later half of the lecture)
Gathering Requirements
Introduction to Software Requirements
Requirements are descriptions of the systemâs features, and constraints.
Essential for guiding development and testing.
Help in setting customer expectations.
Basis for project planning and design decisions.
Types of Software Requirements
Software Requirements:
Functional Requirements
Non-functional Requirements
Domain Requirements
Functional Requirements (FR)
Functional Requirements describe:
The services the system must provide.
How the system should react to particular inputs.
How it should behave in certain situations.
Functional Requirements (FR)
Functional Requirements Characteristics:
Clearly state what the system should do.
Include calculations, technical details, data manipulation and processing, and other specific functionality.
Functional Requirements (FR)
Example:
âThe system shall allow users to enter their credentials to log in.â
âUpon receiving a userâs input, the system shall calculate and display the results within 2 seconds.â
Non-Functional Requirements
Non-functional requirements set criteria to judge the operation of a system, rather than specific behaviors, including constraints on the system and standards.
Non-Functional Requirements
Performance: Speed, responsiveness, consumption.
Reliability: Frequency of failure, recoverability.
âThe system should load the homepage in under 3 seconds when accessed from a standard broadband connection.â
âUser data should be encrypted using industry-standard encryption algorithms.â
Domain Requirements
Domain requirements reflect domain-specific knowledge, standards, or regulations that the software must comply with.
Key Aspects:
Reflect application domain behavior.
Influence system functionality and performance.
Include legal and regulatory requirements.
Domain Requirements
Example:
âThe software must store medical data in compliance with healthcare regulations.â
âThe system must perform currency conversion according to international financial standards.â
The Art of Gathering Requirements
Requirements gathering is the process of collecting the needs and specifications from stakeholders to design a system that meets their expectations.
Understand stakeholder needs, and translate those into technical specifications.
Stakeholders
Stakeholders are:
Individuals or groups with an interest in the success of the project.
Example:
Clients, end-users, project managers, developers or your examiner.
Techniques for Gathering Requirements
Techniques:
Interviews,
Surveys/Questionnaires,
Observations,
Workshops, and
Brainstorming sessions.
Introduction to Use Cases
A use case is a detailed description of how users perform tasks, outlining a systemâs behavior from a userâs perspective.
Starts with a userâs goal and ends when that goal is fulfilled, providing a sequence of steps and interactions.
Elements of a Use Case
Actor: The user performing the behavior.
Stakeholder: Individuals with interests in the system.
Preconditions: States that must be true before the use case runs.
Triggers: Events that initiate the use case.
Main Success Scenarios (Basic Flow): The expected successful path.
Alternative Paths (Alternative Flows): Variations when deviations occur.
Writing a Use Case
Identify users: Determine who will be using the system.
Define actions: Each action becomes a use case.
Describe normal and alternate courses: Outline the basic and alternative flows.
Note commonalities: Identify similar patterns across use cases.
Repeat for all users: Ensure comprehensive coverage.
Simple Laundry Use Case đ§ş
Actor: Housekeeper
Basic Flow: Sorting, washing, drying, folding, ironing, and discarding items.
Preconditions: Itâs Wednesday, and there is laundry.
Trigger: Dirty laundry is present.
Post Conditions: All laundry is clean and either folded or hung up.
Alternative Flows in Laundry Use Case đ§ş
Alternative Flow 1: If items are wrinkled, they are ironed and hung.
Alternative Flow 2: If items are still dirty, they are rewashed.
Alternative Flow 3: If items have shrunk, they are discarded.
Example Use Case: Enter Order đŚ
Description: This use case describes how a customer can enter a new order into the system.
Actors:
Customer
System
Market
Preconditions: Customer is logged on to the website.
Postconditions:
The trade has been processed.
The Customer has received the confirmation from the system.
Basic Flow of Enter Order đŚ
Step ID
Actor
Action
Notes and References
BF-1
Customer
Customer navigates to the order entry page.
BF-2
System
System displays the order entry page.
BF-3
Customer
Customer enters the following order details: Buy/Sell, Quantity, Stock Symbol
BF-4
Customer
Customer submits the order.
BF-5
System
System validates the information entered by the customer.
AF-1: Customer enters invalid information
BF-6
System
System submits the order to the marketplace.
BF-7
Market
Market executes the order.
BF-8
Market
Market sends the execution report to the system.
BF-9
System
System displays the execution report to the customer.
Alternate Flows - Invalid Information
AF-1: Customer enters invalid information
Step ID
Actor
Action
Notes and References
AF-1-1
System
If the customer has entered invalid information the system will display an error message: âYou have entered invalid information.â
AF-1-2
Customer
Customer corrects the information and resubmits.
AF-1-3
[Go to BF-5]
Exception Flows - System Issues
EF-1: System is down or unavailable
Step ID
Actor
Action
Notes and References
EF-1-1
System
If the system is down/unavailable the system will display a message to the customer: âThe system is currently unavailable.â
EF-1-2
[Use case ends]
EF-2: System cannot connect to the market
Step ID
Actor
Action
Notes and References
EF-1-1
System
If the system cannot connect to the market the system will display a message to the customer: âThe system cannot connect to the market. Please try again later.â
EF-1-2
[Use case ends]
Supplemental Requirements
ID
Name
Description
SR-1
Tabbing
The system will enable the user to tab from field to field on the order entry page.
Use Cases: Key Takeaways
User-Centric Design: Focus on user actions and system responses.
Detailing Flows: Clear definition of basic, alternate, and exception flows.
Supplemental Requirements: Address additional user experience enhancements.
Adaptability: The use caseâs structure allows for easy updates and modifications.
Case Study: Starbucks Mobile Order & Pay âď¸
Starbucks introduced Mobile Order & Pay to allows customers to place orders and pay in advance to reduce waiting times.
Objectives:
Improve customer satisfaction by reducing in-store waiting times.
Increase operational efficiency and order accuracy.
Enhance the customerâs personalization and convenience.
Use Case: Placing an Order through Mobile Order & Pay âď¸
Actor: Customer
Preconditions:
The customer has the Starbucks mobile app installed.
The customer has an account with payment information.
Basic Flow:
The customer opens the app and selects the âOrderâ option.
The customer browses the menu and selects items to order.
The customer reviews the order and makes any modifications.
The customer confirms the order and selects a pickup location.
The customer pays for the order through the app.
The system sends the order to the selected store.
The customer receives a notification when the order is ready for pickup.
Postconditions:
The customer picks up the order and is satisfied with the service.
Derived Requirements from Use Case
Functional Requirements:
FR1: The app must allow users to browse the menu and select items.
FR2: The app must provide an option for order modification before confirmation.
FR3: The app must securely process payment information.
FR4: The system must send the order to the selected store and confirm readiness through a notification.
Non-Functional Requirements:
NFR1: The app should load the menu within 2 seconds (Performance).
NFR2: The payment system must comply with PCI DSS standards (Security).
NFR3: The app should be accessible and user-friendly (Usability).
NFR4: The system should handle 1000 orders simultaneously without performance degradation (Scalability).
Requirements Prioritization
Not all requirements are of equal importance.
Evaluate the urgency, value, and feasibility of each requirement. (Prioritization)
Technique(s): MoSCoW (Must have, Should have, Could have, Wonât have this time)
Requirements Prioritization: MoSCoW Technique
MoSCoW technique is a prioritization tool used to reach a common understanding on the importance of various requirements.
Components:
Must have (M): Essential features, required for successful and functional project.
Should have (S): Important but not vital features.
Could have (C): Desirable features that are beneficial but not crucial.
Wonât have this time (W): Features that, may be useful, are not a priority for this iteration.
Starbucks âď¸- Derived Requirements from Use Case
Functional Requirements:
FR1: The app must allow users to browse the menu and select items.
FR2: The app must provide an option for order modification before confirmation.
FR3: The app must securely process payment information.
FR4: The system must send the order to the selected store and confirm readiness through a notification.
Non-Functional Requirements:
NFR1: The app should load the menu within 2 seconds (Performance).
NFR2: The payment system must comply with PCI DSS standards (Security).
NFR3: The app should be accessible and user-friendly (Usability).
NFR4: The system should handle 1000 orders simultaneously without performance degradation (Scalability).
MoSCoW Prioritization - Functional Requirements
Must have (M):
FR1: Browse menu and select items - critical for basic functionality.
FR3: Securely process payment - essential for trust and legal compliance.
Should have (S):
FR2: Order modification - important for user satisfaction but not critical.
Could have (C):
Additional personalized recommendations based on user preferences.
Wonât have this time (W): - Advanced custom ordering options that require new technology.
NFR2: Compliance with PCI DSS for payment security - non-negotiable.
NFR3: Basic app usability - essential for user adoption.
Should have (S):
NFR1: Fast menu loading - important for a good user experience.
Could have (C):
NFR4: Scalability to handle more than 1000 orders - desirable for future growth.
Wonât have this time (W):
Integration with third-party loyalty systems - considered for future phases.
Analysis
Object-Oriented Analysis
What is OOA?
Methodical approach to understanding a system by viewing it through the lens of the âobjectsâ (real-world entities) it involves.
Object-Oriented Analysis
Key Aspects:
- Understanding the Domain: Examining the problem or system from an object-centric perspective.
- Capturing Requirements: Identifying what the system must do from the viewpoint of the objects.
- Defining the System: Creating a model that effectively addresses and fulfills the identified needs.
Identifying Objects and Classes
Core Concepts:
- Objects: Instances from the problem domain represented in the system.
- Classes: Blueprints defining attributes and behaviors of objects.
Identifying Objects and Classes
Process:
Examine the Domain: Analyze the real-world scenario to model.
Spot Key Entities: Identify significant entities that play vital roles.
Define Nature and Functionality: Determine attributes and operations for each entity.
Identifying Objects and Classes
Example: - Library System: Key objects like books, members, and loans with specific attributes and behaviors.
Identify Relationships and Interactions
Objects do not exist in isolation!
Types of Relationships:
Associations: Simple connections between objects.
Aggregations: Whole-part relationships indicating a collective.
Compositions: Strong whole-part relationships with dependency.
Identify Relationships and Interactions
Example: - E-commerce Platform: âCustomerâ places an âOrderâ containing âProductsâ.
Associations
Definition:
Represents a âuse-aâ or âhas-aâ relationship where one object uses or interacts with another.
Can be one-to-one, one-to-many, many-to-one, or many-to-many.
Associations
class Customer {private Order order;voidplaceOrder(){/*...*/}}class Order {// Order related methods}
Here, a Customer has an association with Order as it places an order. This is a one-to-one relationship.
Aggregations
Definition:
A specialized form of association representing a âwhole-partâ relationship, with objects having their own life cycle but forming a whole.
Denotes a âhas-aâ relationship.
The part can exist independently of the whole.
Aggregations
class Team {privateList<Player> players;}class Player {// Player specific methods}
A Team consists of multiple Players. Players can exist without a team, illustrating the whole-part relationship.
Compositions
Definition:
Composition is a strong form of aggregation implying ownership. When the whole is destroyed, so are the parts.
Represents a strong âcontains-aâ relationship.
The life cycle of the part is tied to the whole.
Compositions
class Engine {// Engine specific methods}class Car {private Engine engine =newEngine();voidstart(){/* Starts the engine */}}
A Car has a Engine as part of its composition. If the car ceases to exist, so does the engine, indicating a strong dependency.
Object Interaction Analysis
Object Interaction Analysis is a technique used to understand how objects in the system will communicate and collaborate.
Interaction diagrams are used to visualize these interactions 1.
Object Interaction Analysis
For example, in an airline reservation system, understanding how a âPassengerâ object interacts with âFlightsâ, âTicketsâ, and âPaymentsâ objects is crucial for designing a functional system.
Assigning Responsibilities with CRC Cards
Class-Responsibility-Collaborator (CRC) Cards:
CRC cards is a simple tool used to define the behaviors and interactions of classes.
Components of a CRC Card:
Class Name: The entity or concept being modeled.
Responsibilities: What the class should know or do (its behavior).
Collaborators: Other classes this class interacts with or uses.
CRC Card Example - ShoppingCart
Class Name: ShoppingCart
Responsibilities:
Add Item: Include a new product in the cart.
Calculate Total: Compute the total cost of items in the cart.
Remove Item: Take out a product from the cart.
Checkout: Initiate the purchasing process.
Collaborators:
Product: Items that can be added to the shopping cart.
User: The customer who owns the shopping cart.
CRC Card Example - ShoppingCart
class ShoppingCart {privateList<Product> products;private User owner;voidaddItem(Product product){/*...*/}doublecalculateTotal(){/*...*/}voidremoveItem(Product product){/*...*/}voidcheckout(){/*...*/}}class Product {/* Product details */}class User {/* User details */}
The ShoppingCart class has clear responsibilities like managing items and calculating totals, and it collaborates with Product and User classes to fulfill these responsibilities.
Class Diagrams: The Static Blueprint
Class diagrams show the classes, along with their attributes and operations, and the relationships between them.
See you tomorrow! đđź
Design
Good Design in Software
Beyond aesthetics, good software design is about crafting solutions that are effective, efficient, and maintainable.
Simplicity means focusing on essential elements, making software more understandable and less error-prone.
In Practice:
A Java method that performs a single, well-defined function is a good example. Easy to understand, test, and maintain.
Simplicity
// Method to add two numberspublicclass Addition {// Entry method to demonstrate addition complexitypublicstaticvoidmain(String[] args){int number1 =5;int number2 =10;int result =Add(number1, number2);System.out.println("The result is: "+ result);}// addition methodpublicstaticintAdd(int a,int b){// Initialize sumint sum =0;// Convert integers to stringsString strA =Integer.toString(a);String strB =Integer.toString(b);// Convert strings back to integersint intA =convertStringToInt(strA);int intB =convertStringToInt(strB);// Perform addition in a loopfor(int i =0; i < intA; i++){ sum =increment(sum);}for(int i =0; i < intB; i++){ sum =increment(sum);}// Return the calculated sumreturn sum;}// Method to convert string to integerprivatestaticintconvertStringToInt(String number){try{returnInteger.parseInt(number);}catch(NumberFormatException e){System.err.println("Error converting String to int: "+ e.getMessage());return0;}}// Method to increment a numberprivatestaticintincrement(int number){return number +1;}}
Simplicity
// Simple method to add two numbersintadd(int a,int b){return a + b;}
Reduces complexity, enhances maintainability, and improves user experience.
Consistency
Consistency involves uniformity in visual elements, terminology, and behavior.
In Practice:
A Java GUI application using a consistent color scheme and button styles throughout.
Consistency
// Consistent use of button styleButton saveButton =newButton("Save");saveButton.setStyle("-fx-background-color: #FF0000");// Other buttons would use the same style
Modularity
Modularity means designing systems as separate, interchangeable components.
In Practice:
Java packages organizing classes by functionality, allowing independent development and testing.
Modularity
// Package for order processingpackage com.company.orderprocessing;publicclass OrderProcessor {/*...*/}// Separate package for customer managementpackage com.company.customermanagement;publicclass CustomerManager {/*...*/}
Impact: - Enhances flexibility, manageability, and scalability.
Usability
Usability focuses on making software intuitive and accessible based on usersâ needs and limitations.
In Practice:
A Java web application with a straightforward, well-organized layout, clear instructions, and responsive feedback.
Makes software easy and pleasant to use, enhancing user satisfaction.
Maintainability
Concept:
Maintainable design allows for easy understanding, correction, adaptation, and enhancement.
In Practice:
Writing clean, well-documented Java code and adhering to architectural patterns.
Maintainability
/*** Calculates the monthly payment for a given loan.*@param loanAmount Total amount of the loan.*@param termInYears Number of years for the loan.*@param interestRate Annual interest rate.*@return The monthly payment amount.*/publicdoublecalculateMonthlyPayment(double loanAmount,int termInYears,double interestRate){/*...*/}
Impact:
Ensures software can evolve and adapt efficiently over time.
Robustness
Concept:
Robustness means the systemâs ability to handle errors and unexpected situations gracefully.
In Practice:
Java applications implementing error handling and validation to manage unexpected inputs or disruptions.
Robustness
try{// Attempt a risky operation}catch(SpecificException e){// Handle exception and provide recovery options}
Impact: - Keeps systems stable and functional under various conditions, enhancing reliability.
Case Study: Airbnb Design Overhaul
Challenges:
Users struggled with a complex booking process and inconsistent interface.
Hosts were concerned about guest reliability.
Case Study: Airbnb Design Overhaul
Design Strategy:
Simplified Booking: Streamlined steps for easier navigation and user understanding.
Consistent Visual Language: Unified colors, typography, and buttons for intuitive use.
Modular Design System: Flexible and scalable, allowing easy updates and feature additions.
// After applying KISSclass Calculator {doubleadd(double a,double b){return a + b;// Simple and straightforward}}
YAGNI Principle
Definition: Implement only those features that are necessary. Avoid adding functionality until it is required.
Impact:
Prevents over-engineering and keeps the codebase lean and focused. Ensures resources are used effectively on whatâs truly needed.
YAGNI Principle
// Before applying YAGNIclass FeatureSet {voidessentialFeature(){/*...*/}voidfutureFeature(){/* Might be used later */}}
// After applying YAGNIclass FeatureSet {voidessentialFeature(){/*...*/}// Remove futureFeature() until it's needed}
More Design Principles
Separation of Concerns: Different functionality managed by separate code.
Principle of Least Astonishment: Software behaves how users will expect it to.
Law of Demeter: Object should assume as little as possible about the structure or properties of anything else.
Separation of Concerns
Definition:
Divide a program into distinct sections, each handling a specific aspect of the applicationâs functionality.
Impact:
Helps in isolating issues, streamlining updates, and collaborate more effectively.
Separation of Concerns
// Before: Mixing data access with business logicclass UserHandler {voidcreateUser(String username){// Database connection code// User creation logic}}
// After: Separated data access and business logicclass UserDataAccess {voidsaveUser(User user){/* Database code to save user */}}class UserHandler { UserDataAccess dataAccess =newUserDataAccess();voidcreateUser(String username){ User user =newUser(username); dataAccess.saveUser(user);// Separated concern}}
Principle of Least Astonishment
Definition:
Software should behave in a way that users predictably expect. The design should match common user expectations to prevent confusion and errors.
Impact:
Enhances user satisfaction and system intuitiveness.
// After: Method name reflects its actionclass FileProcessor {voidsave(File file){/* Clearly saves the file */}}
Law of Demeter
Definition:
Minimal knowledge between objects. An object should only interact with its direct components and not concern itself with the internal details of other objects.
Impact:
Reduces the dependencies between components of a system, leading to a looser coupling and more modular architecture.
// After: Adhering to Law of Demeterclass Customer { Wallet wallet;doublepayAmount(double amount){return wallet.deduct(amount);}}class Shop {voidchargeCustomer(Customer customer){ customer.payAmount(50);// Interacting only with the Customer interface}}
GRASP Principles
GRASP Principles - Core Concepts
What is GRASP?
General Responsibility Assignment Software Patterns (GRASP) are guidelines for assigning responsibilities in object-oriented design to improve robustness and maintainability.
GRASP Principles
Principles Overview:
Information Expert: Responsibilities go to the class with the most related information.
Creator: The class that needs an object or has initializing data should create it.
Controller: Designate a class to handle system events and user input.
Low Coupling: Minimize class interdependencies for flexibility.
High Cohesion: Keep related functions together for focused class design.
Polymorphism: Handle alternatives based on object types dynamically.
Pure Fabrication: Create classes for better design, even if they donât represent real-world entities.
Information Expert
Principle: Assign responsibility to the class that has the necessary information to fulfill it.
Classes should be assigned responsibilities based on the data they hold or the information they can access.
Information Expert
Benefits:
Reduces redundancy and complexity.
Enhances maintainability and cohesion.
Makes the system more modular and easier to navigate.
Application:
Typically applied during the design phase to ensure that each class has a clear and focused role, handling operations that are directly related to its information.
Information Expert - Before
class Order {privateList<Item> items;// Other Order related methods...}class OrderCalculator {// This class is wrongly taking the responsibility of calculating the totaldoublecalculateTotalCost(Order order){double total =0;for(Item item : order.getItems()){ total += item.getPrice();}return total;}}
OrderCalculator is taking the responsibility that naturally belongs to the Order class, leading to a less intuitive and more fragmented design.
Information Expert - After
Assign the responsibility of calculating the total cost to the class with the most knowledge required to perform it - the Order class.
class Order {privateList<Item> items;// Information Expert for calculating total costdoublecalculateTotalCost(){double total =0;for(Item item : items){ total += item.getPrice();}return total;}}class OrderCalculator {// No longer responsible for calculating total cost}
Creator - Overview
Principle: Assign class B the responsibility to create an instance of class A if B closely uses A or holds the data that will initialize A.
Ensure that objects are created by the classes that use them most or have the necessary data to initialize them.
Creator - Overview
Benefits:
Enhances encapsulation and clarity.
Reduces dependencies and coupling between classes.
Simplifies the systemâs structure.
Application:
Useful in deciding where to put creation logic, especially in complex systems where the right placement of object creation can significantly affect the designâs clarity and maintainability.
Creator - Before
// Order is responsible for creating Item instances, but it doesn't directly contain or closely use themclass Order {voidaddNewItem(){ Item newItem =newItem();// Order creates Item instances// ...}}class ShoppingCart {privateList<Item> items;// ShoppingCart logic...}
Problem:
The Order class is creating Item instances, but it doesnât have a logical or direct relationship with Item, leading to poor encapsulation and design.
Creator - After Applying
Assign the responsibility to create Item instances to the ShoppingCart class, which logically contains and manages items.
class Order {// No longer responsible for creating Item instances}class ShoppingCart {privateList<Item> items;// Creator for Item instancesvoidaddItem(){ Item newItem =newItem();// ShoppingCart creates Item instances items.add(newItem);}}
Controller - Overview
Principle: Assign the responsibility of handling a system event to a class representing the overall system, a root object, or a subsystem.
Designate a âcontrollerâ class to handle system events or user input, serving as an intermediary between the UI and the systemâs business logic.
Controller - Overview
Benefits:
Centralizes control logic.
Decouples the UI from the underlying business logic.
Simplifies maintenance and enhances scalability.
Application:
Typically used to define how the system will respond to user actions or other events, ensuring thereâs a clear and consistent way to manage these interactions.
Controller - Before Applying
class UserInterface {// Directly handling user input and business logicvoidonLoginButtonClick(String username,String password){// Validate credentials// Directly access the database to verify user// Manage session}}
The UserInterface class is overloaded with responsibilities, handling UI events, business logic, and data access, making the system hard to maintain and scale.
Controller - After Applying
Solution:
Introduce a controller class to act as an intermediary between the UI and the system logic.
class UserInterface {// Delegates handling of the login event to the LoginControllervoidonLoginButtonClick(String username,String password){ LoginController controller =newLoginController(); controller.handleLoginRequest(username, password);}}class LoginController { AuthenticationService authService;voidhandleLoginRequest(String username,String password){// Handle the login process User user = authService.authenticate(username, password);// Manage session and other login-related tasks}}
Low Coupling
Understanding Coupling
Definition:
Coupling is the measure of how interdependent classes or modules are. Low coupling means that a change in one class has minimal impact on other classes.
Understanding Coupling
// High Coupling: Direct dependencyclass Order { Payment payment;voidprocessOrder(){ payment.processPayment();}}// Low Coupling: Reduced dependencyclass Order { PaymentProcessor processor;voidprocessOrder(){ processor.processPayment();}}class PaymentProcessor {voidprocessPayment(){/*...*/}}
Low Coupling
Principle: Design to minimize the dependencies between classes to reduce the impact of changes and improve reusability.
Low Coupling involves reducing the interconnectedness of classes so that changes in one class have minimal impacts on others.
Low Coupling
Benefits:
Increases the flexibility of the system.
Makes the codebase more resilient to changes.
Facilitates easier testing and maintenance.
Application:
Critical in designing systems where change is anticipated, ensuring that modifications in one part of the system donât cause widespread issues.
Low Coupling - Before Applying
class User {// Directly dependent on specific storage implementation FileStorage storage;voidsaveUserData(){// Directly uses FileStorage methods storage.writeData(this);}}
User class is highly coupled with FileStorage, making it difficult to change storage methods or test the User class independently.
Low Coupling - After Applying
Solution:
Introduce an interface to abstract the storage mechanism, reducing the direct dependency between User and FileStorage.
interface Storage {voidwriteData(User user);}class FileStorage implements Storage {voidwriteData(User user){/*...*/}}class User { Storage storage;voidsaveUserData(){// Uses Storage interface, not directly tied to FileStorage storage.writeData(this);}}
High Cohesion
Understanding Cohesion
Definition: - Cohesion refers to the degree to which elements of a module or class belong together.
A class with high cohesion performs a small range of tasks related to a particular purpose or concept.
Understanding Cohesion
// Low Cohesion: Handles unrelated tasksclass Utility {voidhandleDataProcessing(){/*...*/}voidmanageUserInterface(){/*...*/}}// High Cohesion: Focused on a single taskclass DataProcessor {voidhandleDataProcessing(){/*...*/}}
High Cohesion - Overview
Principle: Keep related and similar functionalities together in a class, ensuring that each class has a clear, narrowly focused role.
Classes should not take on responsibilities that could be better handled by others.
High Cohesion - Overview
Benefits:
Simplifies understanding of the system.
Enhances the ability to manage and modify code.
Promotes single responsibility and focused class design.
Application: - Essential in ensuring that the system remains organized and each part is as independent and focused as possible, promoting better design and easier future changes.
High Cohesion - Before Applying
class UserManager {voidcreateUser(){/*...*/}voiddeleteUser(){/*...*/}voidgenerateReport(){/* Unrelated to user management */}}
UserManager is handling both user management and report generation, making it less cohesive and more complex.
High Cohesion - After Applying
class UserManager {voidcreateUser(){/*...*/}voiddeleteUser(){/*...*/}// Removed report generation}class ReportGenerator {voidgenerateReport(){/* Focused on report generation */}}
Higher cohesion in UserManager and ReportGenerator makes each class more focused, understandable, and maintainable.
Polymorphism - Overview
Principle: Use polymorphism to handle alternatives based on object type, where the behavior varies depending on the class of the object.
Polymorphism in object-oriented programming allows objects of different classes to be treated as objects of a common superclass. Itâs a way to use a single interface to represent different underlying forms (data types).
Polymorphism
Benefits:
Enhances flexibility and reusability by allowing different classes to be used interchangeably.
Simplifies code by eliminating the need for multiple conditional statements.
Application:
Commonly used when implementing system behaviors that can vary across different classes but are accessed through a common interface.
Polymorphism - Before Applying
class AnimalSound {voidmakeSound(Animal animal){if(animal instanceof Dog){System.out.println("Woof");}elseif(animal instanceof Cat){System.out.println("Meow");}// More conditions for other animal types}}class Dog extends Animal {/*...*/}class Cat extends Animal {/*...*/}
AnimalSound class becomes cumbersome and difficult to maintain as more animal types are added.
Polymorphism - After Applying
abstractclass Animal {abstractvoidmakeSound();}class Dog extends Animal {voidmakeSound(){System.out.println("Woof");}}class Cat extends Animal {voidmakeSound(){System.out.println("Meow");}}class AnimalSound {voidmakeSound(Animal animal){ animal.makeSound();// Polymorphism in action}}
Each animal class knows how to make its sound, eliminating the need for conditional logic in AnimalSound and making the code more scalable and maintainable.
Pure Fabrication - Overview
Principle: Create a class that doesnât represent a concept in the problem domain, particularly to achieve low coupling, high cohesion, or to encapsulate change.
Pure Fabrication is a made-up class that doesnât represent anything in the real world!
Pure Fabrication
Benefits:
Enhances maintainability and reusability.
Allows for better separation of concerns and encapsulation.
Application:
Useful when a behavior doesnât fit well into existing real-world domain classes or when a particular design problem is best solved independently of the domain model.
Pure Fabrication - Before Applying
class Order {// Order related data and methods...voidsaveOrder(){// Direct database access code to save the order// This mixes business logic with data access logic}}
The Order class is directly handling database operations, which is not its primary responsibility, leading to a design thatâs hard to maintain and scale.
Pure Fabrication - After Applying
class Order {// Order related data and methods...}class OrderRepository {voidsaveOrder(Order order){// Specific database access code to save the order}}
OrderRepository is a pure fabrication that takes over database responsibilities, leading to a cleaner separation of concerns and a more maintainable design.
SOLID Principles
SOLID Principles - Core Concepts
What is SOLID?
SOLID represents five fundamental principles in object-oriented programming and design that promote software maintainability and extensibility.
SOLID Principles - Core Concepts
Key Objectives:
Single Responsibility: Encourage classes to have one reason to change.
Open/Closed: Design modules that are open for extension but closed for modification.
Liskov Substitution: Ensure subclasses can replace their superclasses without altering the programâs correctness.
Interface Segregation: Favor client-specific interfaces over general-purpose ones.
Dependency Inversion: Depend on abstractions rather than concrete implementations.
Single Responsibility Principle (SRP) - Overview
Definition:
A class should have one, and only one, reason to change, promoting modularity and separation of concerns.
Importance:
Simplifies understanding and modification of classes.
Enhances cohesion and reduces the impact of changes.
SRP - Before Applying
class UserManager {voidcreateUser(){/*...*/}voidsendEmail(String message){/*...*/}// Unrelated to user management}
UserManager handling both user creation and email sending mixes different concerns, making it less cohesive and more prone to errors.
SRP - After Applying
class UserManager {voidcreateUser(){/*...*/}}class EmailService {voidsendEmail(String message){/*...*/}}
Each class now has a single responsibility, making the system more maintainable and understandable.
Open/Closed Principle (OCP) - Overview
Definition:
Software entities should be open for extension but closed for modification, promoting flexible and scalable systems.
Use abstraction and polymorphism to extend behavior without altering existing code.
How?:
Designing a class hierarchy where new functionality can be added through subclasses or implementing interfaces without changing existing code.
OCP - Before Applying
class GraphicEditor {voiddrawShape(Shape shape){if(shape.type==1){drawRectangle(shape);}elseif(shape.type==2){drawCircle(shape);}// Adding a new shape requires modifying this method}}
Adding a new shape requires changes to GraphicEditor, violating the open/closed principle.
OCP - After Applying
abstractclassShape{abstractvoiddraw();}classRectangleextendsShape{voiddraw(){/* Draw rectangle */}}class Circle extendsShape{voiddraw(){/* Draw circle */}}class GraphicEditor {voiddrawShape(Shape shape){ shape.draw();// No modification needed for new shapes}}
New shapes can be added without modifying GraphicEditor, adhering to the open/closed principle and making the system more extensible.
Liskov Substitution Principle (LSP) - Overview
Definition:
Subclasses should be substitutable for their base classes without affecting the programâs correctness.
Ensures that a subclass can stand in for its parent class without causing unexpected behavior.
LSP - Before Applying
Java Example:
class Bird {voidfly(){/*...*/}}class Ostrich extends Bird {// Ostriches can't fly, but they're subclassed from Birdvoidfly(){thrownewUnsupportedOperationException();}}
Using an Ostrich object where a Bird is expected can cause unexpected errors due to the overridden fly method.
LSP - After Applying
abstractclass Bird {// Some common bird behavior}class FlyingBird extends Bird {voidfly(){/* Implement flying */}}class Ostrich extends Bird {// Ostrich-specific behavior, no fly method}
FlyingBird and Ostrich are now both substitutable for Bird without causing unexpected behavior, adhering to the Liskov Substitution Principle.
Interface Segregation Principle (ISP) - Overview
Definition:
Clients should not be forced to depend on interfaces they do not use, encouraging fine-grained interfaces over general-purpose ones.
Designing small, focused interfaces that classes can implement without being forced to include unnecessary methods.
Robot now only implements the relevant Worker interface, and Human implements the extended HumanWorker interface. This segregation makes the system more flexible and maintainable.
Dependency Inversion Principle (DIP) - Overview
Definition:
High-level modules should not depend on low-level modules; both should depend on abstractions. Abstractions should not depend on details; details should depend on abstractions.
Using interfaces or abstract classes to define high-level policies, which are then implemented by concrete classes without the high-level modules knowing the details of the implementation.
DIP - Before Applying
class OrderProcessor { MySQLDatabase database;// Directly dependent on a specific database implementationvoidprocessOrder(Order order){ database.save(order);// Tightly coupled to MySQLDatabase}}
OrderProcessor is directly dependent on MySQLDatabase, making it hard to switch to a different database or test the order processing independently.
DIP - After Applying
interface Database {voidsave(Order order);}class MySQLDatabase implements Database {voidsave(Order order){/*...*/}}class OrderProcessor { Database database;// Depends on the abstractionvoidprocessOrder(Order order){ database.save(order);// Not tied to a specific implementation}}
OrderProcessor now depends on the Database abstraction, not the concrete MySQLDatabase implementation. This makes the system more flexible and easier to modify or test.