Object oriented Design
Table of Contents
- Java Basics
- Object-Oriented Programming Fundamentals
- Main Design Principles
- Advanced OOP Concepts
- Design Quality Metrics
- Best Practices
Java Basics
Objects
An object is a fundamental unit in object-oriented programming that represents a real-world entity. Objects have:
- State: Represented by attributes/fields
- Behavior: Represented by methods/functions
- Identity: Each object has a unique identity
Example:
// Object example - a Car object
Car myCar = new Car("Toyota", "Camry", 2023);
myCar.start(); // behavior
myCar.accelerate(50); // behavior
Classes
A class is a blueprint or template that defines the structure and behavior of objects. It describes:
- What attributes an object will have
- What methods an object can perform
- How objects should be created
public class Car {
// Attributes
private String brand;
private String model;
private int year;
// Constructor
public Car(String brand, String model, int year) {
this.brand = brand;
this.model = model;
this.year = year;
}
// Methods
public void start() {
System.out.println("Car is starting...");
}
}
Object-Oriented Programming Fundamentals
Core Concept
Object-Oriented Programming (OOP) is a programming paradigm that:
- Organizes code into objects
- Models real-world entities as software objects
- Promotes code reusability and maintainability
- Objects contain both attributes (data) and methods (behavior)
Class vs Object Relationship
Class:
- Describes the structure of an object
- Acts as a template or blueprint
- Defines what attributes and methods objects will have
Object:
- An instance of a class
- Has actual values for the attributes defined in the class
- Can invoke the methods defined in the class
- Multiple objects can be created from a single class
// Class definition
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
// Creating objects (instances)
Student student1 = new Student("Alice", 20);
Student student2 = new Student("Bob", 22);
Constructor
A constructor is a special method that:
- Has the same name as the class
- Is called automatically when an object is created
- Initializes the object’s attributes
- Simplifies object creation process
- Can be overloaded to provide different ways of creating objects
public class Rectangle {
private double width;
private double height;
// Default constructor
public Rectangle() {
this.width = 1.0;
this.height = 1.0;
}
// Parameterized constructor
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
}
Java Access Modifiers
Public
- Scope: Accessible from anywhere
- Usage: Methods and attributes that need to be accessed by other classes
- Example: Public methods that form the class interface
Private
- Scope: Accessible only within the same class
- Usage: Internal implementation details that should be hidden
- Example: Instance variables that store object state
Protected
- Scope: Accessible within the same package and by subclasses
- Usage: Members that should be inherited but not publicly accessible
Default (Package-Private)
- Scope: Accessible within the same package only
- Usage: Classes and members that should only be used within the package
public class BankAccount {
private double balance; // Private - internal state
protected String accountType; // Protected - for inheritance
String bankName; // Default - package access
public void deposit(double amount) { // Public - external interface
balance += amount;
}
}
Static Keyword
The static keyword indicates that a member belongs to the class itself rather than to instances:
Static Variables:
- Shared among all instances of the class
- Also called class variables
- Initialized when the class is first loaded
Static Methods:
- Can be called without creating an object
- Cannot access non-static (instance) members directly
- Commonly used for utility methods
public class MathUtils {
public static final double PI = 3.14159; // Static constant
private static int instanceCount = 0; // Static variable
public static double calculateArea(double radius) { // Static method
return PI * radius * radius;
}
public MathUtils() {
instanceCount++; // Increment when new instance created
}
public static int getInstanceCount() {
return instanceCount;
}
}
// Usage without creating objects
double area = MathUtils.calculateArea(5.0);
int count = MathUtils.getInstanceCount();
Main Design Principles
1. Decomposition
Definition: Breaking down complex problems into smaller, manageable components.
Benefits:
- Easier to understand and solve individual pieces
- Promotes code reusability
- Enables parallel development
- Simplifies testing and debugging
Types of Relationships:
Association
- Definition: A general relationship where objects interact with each other
- Characteristics: Objects can exist independently
- Example: A Student takes a Course
public class Student {
private List<Course> courses;
public void enrollInCourse(Course course) {
courses.add(course);
}
}
Aggregation (“Has-a” relationship)
- Definition: A specialized association representing a “whole-part” relationship
- Characteristics: Parts can exist without the whole
- Example: A Department has Employees
public class Department {
private List<Employee> employees;
// Employees can exist even if department is dissolved
}
Composition (“Part-of” relationship)
- Definition: A strong aggregation where parts cannot exist without the whole
- Characteristics: When the whole is destroyed, parts are also destroyed
- Example: A House contains Rooms
public class House {
private List<Room> rooms;
public House() {
rooms = new ArrayList<>();
rooms.add(new Room("Living Room"));
rooms.add(new Room("Bedroom"));
// Rooms are created with the house and destroyed with it
}
}
2. Abstraction
Definition: Focusing on essential features while hiding unnecessary implementation details.
Key Concepts:
- Show “what” an object can do, not “how” it does it
- Simplifies design and usage
- Reduces complexity for users of the class
Implementation Methods:
- Abstract classes
- Interfaces
- Method signatures without implementation details
// Abstract class example
public abstract class Vehicle {
protected String brand;
public abstract void start(); // Abstract method - no implementation
public void displayInfo() { // Concrete method
System.out.println("Brand: " + brand);
}
}
// Interface example
public interface Drawable {
void draw(); // Implicitly abstract
default void erase() { // Default implementation
System.out.println("Erasing...");
}
}
3. Encapsulation
Definition: Bundling data and methods together while hiding internal implementation details.
Key Components:
- Bundle: Data (attributes) and functions (methods) together
- Expose: Only necessary interfaces/methods
- Restrict: Access to internal state and implementation
Benefits:
- Data protection and integrity
- Flexibility to change implementation
- Reduced coupling between classes
- Better maintainability
public class BankAccount {
private double balance; // Hidden internal state
private String accountNumber;
// Controlled access through methods
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
public double getBalance() {
return balance; // Read-only access
}
}
4. Generalization
Definition: Extracting common behaviors and attributes from multiple classes into a generalized superclass.
Process:
- Identify common features across classes
- Create a parent class with shared attributes/methods
- Make specific classes inherit from the parent
Benefits:
- Code reuse
- Consistent interface
- Easier maintenance
- Natural hierarchy representation
// Generalized superclass
public class Animal {
protected String name;
protected int age;
public void eat() {
System.out.println(name + " is eating");
}
public abstract void makeSound(); // Each animal sounds different
}
// Specialized subclasses
public class Dog extends Animal {
public void makeSound() {
System.out.println("Woof!");
}
}
public class Cat extends Animal {
public void makeSound() {
System.out.println("Meow!");
}
}
Advanced OOP Concepts
Inheritance
Definition: Establishing a parent-child relationship where child classes inherit attributes and methods from parent classes.
Types:
- Single Inheritance: One parent class
- Multilevel Inheritance: Chain of inheritance
- Hierarchical Inheritance: Multiple children from one parent
Keywords:
extends
: Used to inherit from a classsuper
: Reference to parent class@Override
: Indicates method overriding
public class Vehicle {
protected String brand;
protected int year;
public void start() {
System.out.println("Vehicle starting...");
}
}
public class Car extends Vehicle {
private int doors;
public Car(String brand, int year, int doors) {
super(); // Call parent constructor
this.brand = brand;
this.year = year;
this.doors = doors;
}
@Override
public void start() {
super.start(); // Call parent method
System.out.println("Car engine started");
}
}
Polymorphism
Definition: The ability of objects to respond differently to the same method call based on their actual type.
Types:
Method Overloading (Compile-time Polymorphism)
- Same method name with different parameters
- Resolved at compile time
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
public int add(int a, int b, int c) {
return a + b + c;
}
}
Method Overriding (Runtime Polymorphism)
- Child class provides specific implementation of parent method
- Resolved at runtime based on actual object type
public class Shape {
public double calculateArea() {
return 0;
}
}
public class Circle extends Shape {
private double radius;
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
public class Rectangle extends Shape {
private double width, height;
@Override
public double calculateArea() {
return width * height;
}
}
// Polymorphic usage
Shape[] shapes = {new Circle(), new Rectangle()};
for (Shape shape : shapes) {
System.out.println(shape.calculateArea()); // Calls appropriate method
}
Design Quality Metrics
Cohesion
Definition: Measures how closely related and focused the responsibilities of a single module are.
Types:
- High Cohesion (Good): Methods work together toward a single, well-defined purpose
- Low Cohesion (Bad): Methods perform unrelated tasks
// High Cohesion - all methods related to user management
public class UserManager {
public void createUser(String username) { }
public void deleteUser(String username) { }
public void updateUser(String username, UserData data) { }
public User findUser(String username) { return null; }
}
// Low Cohesion - mixed responsibilities
public class BadClass {
public void calculateTax() { } // Tax calculation
public void sendEmail() { } // Email service
public void parseXML() { } // XML parsing
}
Coupling
Definition: Measures the degree of interdependence between software modules.
Types:
- Low Coupling (Good): Modules are independent and changes in one don’t affect others
- High Coupling (Bad): Modules are tightly connected and changes cascade
Ways to Reduce Coupling:
- Use interfaces instead of concrete classes
- Dependency injection
- Observer pattern
- Facade pattern
// High Coupling - direct dependency
public class OrderProcessor {
private EmailService emailService = new EmailService(); // Tight coupling
public void processOrder(Order order) {
// Process order
emailService.sendConfirmation(order.getCustomerEmail());
}
}
// Low Coupling - using interface
public class OrderProcessor {
private NotificationService notificationService; // Interface dependency
public OrderProcessor(NotificationService service) {
this.notificationService = service; // Dependency injection
}
public void processOrder(Order order) {
// Process order
notificationService.sendNotification(order.getCustomerEmail());
}
}
Best Practices
1. Design Principles (SOLID)
- Single Responsibility Principle: A class should have only one reason to change
- Open/Closed Principle: Open for extension, closed for modification
- Liskov Substitution Principle: Objects should be replaceable with instances of their subtypes
- Interface Segregation Principle: Many specific interfaces are better than one general interface
- Dependency Inversion Principle: Depend on abstractions, not concretions
2. Naming Conventions
- Use meaningful and descriptive names
- Classes: PascalCase (e.g.,
CustomerOrder
) - Methods and variables: camelCase (e.g.,
calculateTotal()
) - Constants: UPPER_SNAKE_CASE (e.g.,
MAX_RETRY_COUNT
)
3. Method Design
- Keep methods short and focused (single responsibility)
- Use descriptive parameter names
- Minimize the number of parameters
- Return meaningful values or use void appropriately
4. Error Handling
- Use exceptions for exceptional conditions
- Create custom exception classes when needed
- Always clean up resources (use try-with-resources)
- Don’t catch exceptions you can’t handle
5. Documentation
- Write clear javadoc comments for public methods
- Explain complex algorithms and business logic
- Keep comments up-to-date with code changes
- Use meaningful variable and method names to reduce need for comments