Table of Contents

    Composition

    Chapter 16.12

    Composition in Object-Oriented Programming

    Learn what composition is in Object-Oriented Programming, how objects can be built using other objects, why composition represents a has-a relationship, how it differs from inheritance, and how it helps create flexible, reusable, and maintainable software designs.

    Introduction

    Composition is an important concept in Object-Oriented Programming where one class is built by using objects of other classes. Instead of inheriting behavior from a parent class, a class can contain another object as one of its parts. This allows complex objects to be created from smaller, reusable objects.

    In simple terms, composition means building a class using other classes. It represents a has-a relationship. For example, a Car has an Engine, a Student has an Address, a Library has Books, and a Computer has a Processor. These are examples where one object contains or uses another object.

    Composition is very useful because it helps developers create flexible designs. Instead of creating long inheritance chains, developers can combine smaller objects together to build larger systems. This makes code easier to reuse, test, modify, and maintain.

    "Composition is an OOP concept where one class contains objects of other classes to build complex behavior."

    Simple Definition of Composition

    Composition is a technique in Object-Oriented Programming where a class contains one or more objects of other classes as its members. These contained objects become parts of the main object.

    In beginner-friendly words:

    • Composition means creating complex objects from smaller objects.
    • It represents a has-a relationship.
    • One class can contain another class object as a property.
    • It promotes code reuse without using inheritance.
    • It makes software design more flexible.
    • It helps separate responsibilities between different classes.
    COMPOSITION CONCEPT
    Composition = Object + Object + Object
    Important: Composition is commonly described as a has-a relationship. If one object has another object as a part, composition may be a suitable design choice.

    Real-World Analogy of Composition

    A car is a great real-world example of composition. A car is not just one single thing. It is made up of many smaller components such as engine, wheels, seats, steering wheel, battery, and brakes.

    In programming, we can model this by creating separate classes such as Engine, Wheel, Seat, and Battery. Then the Car class can contain objects of these classes. This is composition.

    Car as a Composition Example

    A car has an engine, wheels, seats, and a battery. Similarly, in programming, a Car object can be composed of Engine, Wheel, Seat, and Battery objects.

    Real-World Object Contained Parts OOP Relationship
    Car Engine, Wheel, Battery, Seat Car has an Engine, Car has Wheels
    Computer Processor, RAM, Hard Disk, Monitor Computer has a Processor
    Student Address, Course, ID Card Student has an Address
    Library Books, Members, Librarian Library has Books
    Order Customer, Products, Payment, Delivery Order has Customer and Payment

    Why Do We Need Composition?

    Composition is needed because not every relationship between classes should be represented using inheritance. Inheritance is used for is-a relationships, but many real-world relationships are has-a relationships. Composition helps model these relationships properly.

    For example, a Car is a Vehicle, so inheritance may be suitable between Car and Vehicle. But a Car has an Engine, so composition is better between Car and Engine. It would not be correct to say Car is an Engine.

    Without Composition

    • Classes may become too large and difficult to manage.
    • Developers may incorrectly use inheritance for has-a relationships.
    • Code can become tightly coupled and less flexible.
    • Changing one responsibility may affect the whole class.
    • Reusing parts of a system becomes harder.
    • Testing individual parts becomes difficult.

    With Composition

    • Complex objects are built from smaller objects.
    • Each class can focus on one responsibility.
    • Objects can be reused in different classes.
    • Code becomes more flexible and maintainable.
    • Parts can be replaced without changing the entire system.
    • Testing smaller classes becomes easier.

    Main Idea Behind Composition

    The main idea behind composition is to create a class by combining other objects. Instead of saying "this class is a type of another class", composition says "this class has another object as a part".

    COMPOSITION RULE
    Main Object has Supporting Object

    For example:

    • A Car has an Engine.
    • A Student has an Address.
    • An Order has a Payment.
    • A Library has Books.
    • A Computer has a Processor.
    Beginner Tip: If the sentence sounds correct with has-a, composition is often a better choice than inheritance.

    Basic Syntax Concept of Composition

    In composition, one class contains another class object as a property or field. The main class can then use the methods and data of the contained object.

    
    // General conceptual syntax
    
    Class SupportingClass
    {
        method()
        {
            // supporting behavior
        }
    }
    
    Class MainClass
    {
        SupportingClass objectName
    
        mainMethod()
        {
            objectName.method()
        }
    }
    

    In this structure, MainClass has an object of SupportingClass. This means MainClass can use SupportingClass functionality through composition.

    Example: Car Has an Engine

    Let us understand composition with a simple Car and Engine example. A car has an engine. The engine has its own responsibility: starting and stopping. The car can use the engine object to start or stop itself.

    
    // Conceptual composition example
    
    Class Engine
    {
        start()
        {
            print "Engine started"
        }
    
        stop()
        {
            print "Engine stopped"
        }
    }
    
    Class Car
    {
        Engine engine
    
        startCar()
        {
            engine.start()
            print "Car started"
        }
    
        stopCar()
        {
            engine.stop()
            print "Car stopped"
        }
    }
    

    In this example, Car does not inherit from Engine. Instead, Car contains an Engine object. This is composition because Car has an Engine.

    Java Example of Composition

    The following Java example shows how composition works using Car and Engine classes.

    Prerequisites: To understand this example, you should know classes, objects, properties, methods, constructors, object creation, and basic Java syntax.
    
    class Engine {
    
        private String engineType;
    
        Engine(String type) {
            engineType = type;
        }
    
        void start() {
            System.out.println(engineType + " engine started");
        }
    
        void stop() {
            System.out.println(engineType + " engine stopped");
        }
    }
    
    class Car {
    
        private String brand;
        private Engine engine;
    
        Car(String carBrand, Engine carEngine) {
            brand = carBrand;
            engine = carEngine;
        }
    
        void startCar() {
            engine.start();
            System.out.println(brand + " car started");
        }
    
        void stopCar() {
            engine.stop();
            System.out.println(brand + " car stopped");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
    
            Engine petrolEngine = new Engine("Petrol");
    
            Car car1 = new Car("Toyota", petrolEngine);
    
            car1.startCar();
            car1.stopCar();
        }
    }
    

    In this example, the Car class contains an Engine object. The Car class uses the Engine class to start and stop the engine. This is a clear example of composition.

    JavaScript Example of Composition

    JavaScript also supports composition. We can create one object and pass it into another object. The main object can then use the composed object to perform specific tasks.

    Prerequisites: To understand this example, you should know JavaScript classes, constructors, objects, methods, the this keyword, and console output.
    
    class Engine {
    
        constructor(type) {
            this.type = type;
        }
    
        start() {
            console.log(this.type + " engine started");
        }
    
        stop() {
            console.log(this.type + " engine stopped");
        }
    }
    
    class Car {
    
        constructor(brand, engine) {
            this.brand = brand;
            this.engine = engine;
        }
    
        startCar() {
            this.engine.start();
            console.log(this.brand + " car started");
        }
    
        stopCar() {
            this.engine.stop();
            console.log(this.brand + " car stopped");
        }
    }
    
    const petrolEngine = new Engine("Petrol");
    
    const car1 = new Car("Honda", petrolEngine);
    
    car1.startCar();
    car1.stopCar();
    

    In this JavaScript example, Car receives an Engine object through its constructor. The Car object uses the Engine object to perform engine-related tasks.

    Has-A Relationship

    Composition represents a has-a relationship. This means one object contains or owns another object as a part of itself.

    HAS-A RELATIONSHIP
    Class A has a Class B Object
    Main Class Composed Object Has-A Statement
    Car Engine A Car has an Engine.
    Student Address A Student has an Address.
    Order Payment An Order has a Payment.
    Computer Processor A Computer has a Processor.
    Library Book A Library has Books.

    Composition vs Inheritance

    Composition and inheritance are both used to reuse code and organize classes, but they represent different relationships. Inheritance represents an is-a relationship, while composition represents a has-a relationship.

    For example, a Car is a Vehicle, so inheritance may be suitable. A Car has an Engine, so composition is suitable. Choosing the correct relationship is very important for good OOP design.

    DESIGN COMPARISON
    Inheritance = Is-A   |   Composition = Has-A
    Basis Inheritance Composition
    Relationship Is-A relationship Has-A relationship
    Meaning Child class inherits from parent class. One class contains object of another class.
    Example Car is a Vehicle. Car has an Engine.
    Code Reuse Reuse through parent class features. Reuse through contained objects.
    Flexibility Can become rigid if hierarchy is deep. Usually more flexible because parts can be replaced.
    Coupling Child class is tightly related to parent class. Main class can be loosely connected to component classes.
    Best Use When one class is truly a type of another class. When one class uses or contains another class.

    Composition Over Inheritance

    In software design, you may hear the phrase "favor composition over inheritance". This does not mean inheritance is bad. It means developers should not use inheritance everywhere just to reuse code. Composition is often more flexible because behavior can be changed by replacing composed objects.

    Inheritance creates a fixed parent-child relationship. Composition allows objects to be assembled from reusable parts. This makes it easier to change behavior without changing the entire class hierarchy.

    Overusing Inheritance

    • Can create deep class hierarchies.
    • Can make child classes tightly dependent on parent classes.
    • Can become difficult to change later.
    • May force incorrect is-a relationships.
    • Can make testing and debugging harder.

    Using Composition Wisely

    • Allows building objects from reusable parts.
    • Makes behavior easier to replace or modify.
    • Keeps classes focused on specific responsibilities.
    • Reduces unnecessary inheritance chains.
    • Improves flexibility and maintainability.

    Example: Student Has an Address

    A student has an address. This is a has-a relationship, so composition is a good design choice. Address can be a separate class because it has its own data such as city, state, country, and postal code.

    
    class Address {
    
        private String city;
        private String state;
        private String country;
    
        Address(String studentCity, String studentState, String studentCountry) {
            city = studentCity;
            state = studentState;
            country = studentCountry;
        }
    
        void displayAddress() {
            System.out.println("City: " + city);
            System.out.println("State: " + state);
            System.out.println("Country: " + country);
        }
    }
    
    class Student {
    
        private String name;
        private int rollNumber;
        private Address address;
    
        Student(String studentName, int studentRollNumber, Address studentAddress) {
            name = studentName;
            rollNumber = studentRollNumber;
            address = studentAddress;
        }
    
        void displayStudentDetails() {
            System.out.println("Name: " + name);
            System.out.println("Roll Number: " + rollNumber);
            address.displayAddress();
        }
    }
    
    public class Main {
        public static void main(String[] args) {
    
            Address address1 = new Address("Kolkata", "West Bengal", "India");
    
            Student student1 = new Student("Rahul", 101, address1);
    
            student1.displayStudentDetails();
        }
    }
    

    In this example, Student contains an Address object. Address is a separate class, so it can be reused in other classes such as Teacher, Employee, or Customer.

    Example: Order Has Customer, Product, and Payment

    In an e-commerce system, an Order object can be composed of Customer, Product, and Payment objects. Each class has its own responsibility, and Order combines them to complete the order process.

    Main Class Composed Classes Responsibility
    Order Customer Stores customer details.
    Order Product Stores product details.
    Order Payment Handles payment details.
    Order Delivery Handles delivery status.
    
    class Customer {
    
        String name;
    
        Customer(String customerName) {
            name = customerName;
        }
    
        void displayCustomer() {
            System.out.println("Customer: " + name);
        }
    }
    
    class Product {
    
        String productName;
        double price;
    
        Product(String name, double productPrice) {
            productName = name;
            price = productPrice;
        }
    
        void displayProduct() {
            System.out.println("Product: " + productName);
            System.out.println("Price: " + price);
        }
    }
    
    class Payment {
    
        String paymentMode;
    
        Payment(String mode) {
            paymentMode = mode;
        }
    
        void processPayment() {
            System.out.println("Payment processed using " + paymentMode);
        }
    }
    
    class Order {
    
        Customer customer;
        Product product;
        Payment payment;
    
        Order(Customer orderCustomer, Product orderProduct, Payment orderPayment) {
            customer = orderCustomer;
            product = orderProduct;
            payment = orderPayment;
        }
    
        void placeOrder() {
            customer.displayCustomer();
            product.displayProduct();
            payment.processPayment();
            System.out.println("Order placed successfully");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
    
            Customer customer1 = new Customer("Ayesha");
            Product product1 = new Product("Keyboard", 1200);
            Payment payment1 = new Payment("UPI");
    
            Order order1 = new Order(customer1, product1, payment1);
    
            order1.placeOrder();
        }
    }
    

    This example shows how a larger object can be built using smaller objects. Each class has a clear responsibility, and the Order class coordinates them.

    Composition and Code Reusability

    Composition improves code reusability because small component classes can be reused in many different contexts. For example, an Address class can be used by Student, Teacher, Employee, Customer, and Supplier.

    Instead of rewriting address-related properties in every class, we create one Address class and use its object wherever required.

    Reusable Component Can Be Used In Benefit
    Address Student, Teacher, Customer, Employee Avoids duplicate address fields.
    Payment Order, Invoice, Subscription Centralizes payment behavior.
    NotificationService Order, Account, Report, Login System Reuses notification logic.
    Logger Almost any application class Reuses logging behavior.
    FileStorage Document, ImageUpload, ReportExport Reuses file saving logic.

    Composition and Single Responsibility

    Composition supports the idea that each class should focus on one clear responsibility. Instead of creating one big class that does everything, we divide responsibilities into smaller classes and compose them together.

    For example, an Order class should not directly handle customer data, payment processing, invoice generation, and delivery tracking all by itself. These responsibilities can be separated into Customer, Payment, Invoice, and Delivery classes.

    Poor Design

    • One large class handles many responsibilities.
    • Code becomes difficult to read.
    • Testing becomes harder.
    • Changing one part may affect many features.
    • Reusability becomes low.

    Better Composition Design

    • Responsibilities are divided into smaller classes.
    • Each class has a clear purpose.
    • Objects work together through composition.
    • Testing individual classes becomes easier.
    • Code becomes more reusable and maintainable.

    Replacing Composed Objects

    One major advantage of composition is that a composed object can often be replaced with another object that provides similar behavior. This makes the system more flexible.

    For example, a Car may use a PetrolEngine today and an ElectricEngine tomorrow. If the Car class depends on a general engine behavior, the engine implementation can be changed more easily.

    
    class Engine {
    
        void start() {
            System.out.println("Generic engine started");
        }
    }
    
    class PetrolEngine extends Engine {
    
        void start() {
            System.out.println("Petrol engine started");
        }
    }
    
    class ElectricEngine extends Engine {
    
        void start() {
            System.out.println("Electric engine started");
        }
    }
    
    class Car {
    
        private Engine engine;
    
        Car(Engine carEngine) {
            engine = carEngine;
        }
    
        void startCar() {
            engine.start();
            System.out.println("Car started");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
    
            Car petrolCar = new Car(new PetrolEngine());
            Car electricCar = new Car(new ElectricEngine());
    
            petrolCar.startCar();
            electricCar.startCar();
        }
    }
    

    This example combines composition with inheritance and polymorphism. The Car class has an Engine object, and different engine types can be passed to it.

    Aggregation vs Composition

    In OOP discussions, you may also hear the term aggregation. Aggregation and composition both describe has-a relationships, but they differ in the strength of ownership between objects.

    In composition, the contained object is usually strongly owned by the main object. In aggregation, the contained object can exist independently of the main object.

    Basis Composition Aggregation
    Relationship Strength Strong has-a relationship Weak has-a relationship
    Ownership Main object strongly owns the part. Main object uses or references another object.
    Object Lifetime Part may depend strongly on whole object. Part can usually exist independently.
    Example House has Rooms. Team has Players.
    Simple Meaning Part-of relationship. Uses or contains relationship.
    Beginner Tip: For beginner-level learning, focus first on understanding composition as a has-a relationship. Aggregation is a more advanced design distinction.

    Advantages of Composition

    Composition provides many advantages in software development. It helps create flexible, reusable, and maintainable systems by combining smaller objects.

    Benefits of Composition

    • Code Reusability: Smaller classes can be reused in multiple places.
    • Flexibility: Components can often be replaced or modified more easily.
    • Better Organization: Responsibilities are divided into separate classes.
    • Maintainability: Changes in one component are easier to manage.
    • Testability: Smaller classes can be tested separately.
    • Loose Coupling: Classes can depend on components rather than rigid inheritance chains.
    • Avoids Deep Inheritance: Reduces unnecessary parent-child class hierarchies.
    • Real-World Modeling: Many real-world objects naturally have other objects as parts.
    • Supports Clean Design: Encourages clear separation of responsibilities.
    • Works Well with Interfaces: Components can be replaced using common contracts.

    Limitations of Composition

    Composition is powerful, but it should also be used thoughtfully. Overusing composition without proper design can make code harder to follow.

    More Classes Composition may require creating multiple smaller classes, which can feel complex for beginners.
    Object Coordination The main class may need to coordinate several component objects carefully.
    Design Planning Required Developers must decide which responsibilities belong to which component class.
    Can Be Overused Creating separate classes for very small or unnecessary parts may make simple programs too complicated.

    Composition Best Practices

    Good composition design helps build clean and flexible applications. The goal is to create meaningful component classes that represent real responsibilities.

    Best Practices for Composition

    • Use composition when one object has or uses another object.
    • Keep each component class focused on one responsibility.
    • Use meaningful class names such as Engine, Address, Payment, or Logger.
    • Avoid putting too many unrelated responsibilities in one class.
    • Use interfaces when composed objects need interchangeable behavior.
    • Prefer composition over inheritance when there is no true is-a relationship.
    • Keep object creation simple and understandable for beginners.
    • Pass required component objects through constructors when appropriate.
    • Make components reusable across different classes.
    • Do not create unnecessary component classes for very small problems.

    Common Mistakes Beginners Make

    Beginners often confuse composition with inheritance or create designs where responsibilities are not clearly separated. Understanding common mistakes helps improve OOP design skills.

    Common Mistakes

    • Using inheritance for has-a relationships.
    • Thinking composition and inheritance are the same.
    • Creating one large class instead of smaller component classes.
    • Creating too many unnecessary component classes.
    • Making component objects public without control.
    • Not initializing composed objects properly.
    • Putting unrelated responsibilities inside a component class.
    • Not understanding object ownership and lifetime.

    Better Approach

    • Use inheritance only for clear is-a relationships.
    • Use composition for has-a relationships.
    • Break large classes into meaningful smaller classes.
    • Keep component classes focused and reusable.
    • Initialize composed objects using constructors.
    • Use private fields and public methods for controlled access.
    • Use interfaces for replaceable components.
    • Design relationships before writing code.

    How to Identify Composition in a Problem

    To identify composition, read the problem statement and look for objects that contain, use, or depend on other objects. If you can naturally say "has-a", then composition may be suitable.

    IDENTIFICATION RULE
    If Object A has Object B, use Composition

    Consider this requirement:

    "A student record should store personal details and address details."

    Here, Student has Address. So, Address can be a separate class and Student can contain an Address object.

    Another example:

    "An order should contain customer details, product details, payment details, and delivery details."

    Here, Order has Customer, Product, Payment, and Delivery objects. This is a strong case for composition.

    Example: Composition in Student Management System

    In a student management system, a Student object may have Address, Course, IDCard, and Result objects. Instead of putting everything inside one Student class, we can divide responsibilities into separate classes.

    Main Class Composed Class Reason
    Student Address Address details can be managed separately.
    Student Course Course details can be reused for many students.
    Student IDCard ID card details have their own responsibility.
    Student Result Result calculation and grade details can be separated.
    
    class Course {
    
        private String courseName;
        private int durationInMonths;
    
        Course(String name, int duration) {
            courseName = name;
            durationInMonths = duration;
        }
    
        void displayCourse() {
            System.out.println("Course: " + courseName);
            System.out.println("Duration: " + durationInMonths + " months");
        }
    }
    
    class Result {
    
        private int marks;
    
        Result(int studentMarks) {
            marks = studentMarks;
        }
    
        String calculateGrade() {
            if (marks >= 90) {
                return "A";
            } else if (marks >= 75) {
                return "B";
            } else if (marks >= 60) {
                return "C";
            } else if (marks >= 40) {
                return "D";
            } else {
                return "Fail";
            }
        }
    
        void displayResult() {
            System.out.println("Marks: " + marks);
            System.out.println("Grade: " + calculateGrade());
        }
    }
    
    class Student {
    
        private String name;
        private Course course;
        private Result result;
    
        Student(String studentName, Course studentCourse, Result studentResult) {
            name = studentName;
            course = studentCourse;
            result = studentResult;
        }
    
        void displayStudentProfile() {
            System.out.println("Student Name: " + name);
            course.displayCourse();
            result.displayResult();
        }
    }
    
    public class Main {
        public static void main(String[] args) {
    
            Course course1 = new Course("Programming Fundamentals", 6);
            Result result1 = new Result(85);
    
            Student student1 = new Student("Rahul", course1, result1);
    
            student1.displayStudentProfile();
        }
    }
    

    In this example, Student is composed of Course and Result objects. This keeps the Student class cleaner and separates responsibilities properly.

    Real-World Software Examples of Composition

    Composition is used in almost every real-world software application. Large systems are commonly built by connecting smaller objects and services together.

    System Main Object Composed Objects
    E-Commerce System Order Customer, Product, Payment, Delivery, Invoice
    School System Student Address, Course, Result, Attendance
    Banking System Account Customer, TransactionHistory, Card, Loan
    Hospital System Patient Address, Appointment, MedicalRecord, Bill
    Report System ReportGenerator DataFetcher, Formatter, Exporter, Logger
    Authentication System LoginService UserRepository, PasswordValidator, TokenGenerator

    Mini Practice Activity

    Try to identify composition relationships from the following scenarios. This activity will help you understand has-a relationships practically.

    Scenario Main Class Composed Classes Has-A Statement
    Car System Car Engine, Wheel, Battery Car has an Engine.
    Student System Student Address, Course, Result Student has an Address.
    Online Shopping Order Customer, Product, Payment Order has a Payment.
    Computer System Computer Processor, RAM, Storage Computer has a Processor.
    Library System Library Book, Member, Librarian Library has Books.
    Report System ReportGenerator DataFetcher, Formatter, Exporter ReportGenerator has a Formatter.

    Frequently Asked Questions

    1. What is composition in simple words?

    Composition means building one class using objects of other classes. It represents a has-a relationship, such as a Car has an Engine or a Student has an Address.

    2. Why is composition important?

    Composition is important because it helps create flexible, reusable, and maintainable code. It allows complex objects to be built from smaller, focused objects.

    3. What relationship does composition represent?

    Composition represents a has-a relationship. For example, an Order has a Customer, a Product, and a Payment.

    4. What is the difference between inheritance and composition?

    Inheritance represents an is-a relationship, while composition represents a has-a relationship. For example, a Car is a Vehicle, but a Car has an Engine.

    5. Why do developers say favor composition over inheritance?

    Developers say this because composition is often more flexible than inheritance. It allows behavior to be reused and replaced without creating rigid class hierarchies.

    6. Can composition and inheritance be used together?

    Yes. Composition and inheritance can be used together. For example, a Car may inherit from Vehicle and also contain an Engine object through composition.

    7. Is composition only for large projects?

    No. Composition can be useful in small and large projects. However, very small programs may not need many separate component classes.

    8. What is aggregation?

    Aggregation is also a has-a relationship, but it is usually weaker than composition. In aggregation, the contained object can often exist independently from the main object.

    9. What is an example of composition in a student system?

    A Student class can contain Address, Course, and Result objects. This means a Student has an Address, has a Course, and has a Result.

    10. How do I identify composition?

    If you can naturally say "Object A has Object B", then composition may be suitable. For example, a Computer has RAM, a Car has Wheels, and an Order has Payment.

    Summary

    Composition is an important Object-Oriented Programming concept where one class contains objects of other classes. It is used to build complex objects from smaller, reusable components.

    Composition represents a has-a relationship. For example, a Car has an Engine, a Student has an Address, and an Order has a Payment. This is different from inheritance, which represents an is-a relationship.

    Composition makes software design more flexible because components can be reused, replaced, and tested independently. It also helps avoid unnecessary inheritance chains and keeps classes focused on specific responsibilities.

    Key Takeaway

    Composition is the OOP concept of building complex objects using smaller objects. It represents a has-a relationship and helps create flexible, reusable, testable, and maintainable software designs. Use composition when one class contains or uses another class.