User Defined Types  «Prev  Next»
Lesson 9 Object composition
Objective Describe how to use composition to build more complex objects from other objects.

Use Composition to build more Complex Objects from other Objects

In Object-Oriented Design (OOD), composition is a fundamental concept that allows you to build more complex objects by combining simpler objects. Composition is a "has-a" relationship, where an object is composed of one or more other objects. This approach promotes code reuse, modularity, and flexibility, making it easier to manage and extend complex systems.
Here's a detailed explanation of how to use composition to build more complex objects:
1. Understanding Composition
Composition is a way to design classes where an object of one class contains objects of other classes as members. This is in contrast to "inheritance", which represents an "is-a" relationship. Composition is preferred in many cases because it provides greater flexibility and avoids the pitfalls of deep inheritance hierarchies.
Key Characteristics of Composition:
  • "Has-a" Relationship: An object contains or is composed of other objects.
  • Modularity: Each component can be developed, tested, and maintained independently.
  • Flexibility: Components can be replaced or modified without affecting the entire system.
  • Reusability: Components can be reused across different classes and systems.
2. Steps to Use Composition
  1. Step 1: Identify the Components
    Break down the complex object into smaller, reusable components. Each component should represent a single responsibility or functionality.
  2. Step 2: Define the Component Classes
    Create classes for each of the components. These classes should be self-contained and focused on a specific task.
  3. Step 3: Compose the Complex Object
    Create a class for the complex object and include instances of the component classes as member variables. Use these components to implement the behavior of the complex object.
  4. Step 4: Delegate Functionality
    Delegate tasks to the component objects. The complex object should rely on its components to perform specific operations.

3. Example of Composition in Action
Let’s say we want to model a `Car` object using composition. A car is composed of several components, such as an `Engine`, `Wheels`, and `Transmission`.
Step 1: Identify the Components
  • Engine: Handles the car's power generation.
  • Wheels: Handle movement and stability.
  • Transmission: Manages gear shifting.

Step 2: Define the Component Classes
class Engine:
    def start(self):
        print("Engine started.")

    def stop(self):
        print("Engine stopped.")

class Wheels:
    def rotate(self):
        print("Wheels rotating.")

class Transmission:
    def shift_gear(self, gear):
        print(f"Shifted to gear {gear}.")

Step 3: Compose the Complex Object
class Car:
    def __init__(self):
        self.engine = Engine()
        self.wheels = Wheels()
        self.transmission = Transmission()

    def drive(self):
        self.engine.start()
        self.transmission.shift_gear(1)
        self.wheels.rotate()
        print("Car is moving.")

    def stop(self):
        self.engine.stop()
        print("Car has stopped.")

Step 4: Delegate Functionality
The `Car` class delegates tasks to its components:
  • Starting the car involves starting the Engine.
  • Driving the car involves shifting gears in the Transmission and rotating the Wheels.


Usage Example:
my_car = Car()
my_car.drive()  # Output: Engine started. Shifted to gear 1. Wheels rotating. Car is moving.
my_car.stop()   # Output: Engine stopped. Car has stopped.

4. Advantages of Composition
  • Flexibility: Components can be easily replaced or modified. For example, you can replace the Engine with a more powerful one without changing the Car class.
  • Reusability: Components like Engine, Wheels, and Transmission can be reused in other classes (e.g., Truck, Motorcycle).
  • Maintainability: Each component is independent, making it easier to debug and maintain.
  • Avoids Inheritance Issues: Composition avoids the tight coupling and complexity that can arise from deep inheritance hierarchies.

5. Composition vs. Inheritance
While inheritance represents an "is-a" relationship (e.g., a `Dog` is an `Animal`), composition represents a "has-a" relationship (e.g., a `Car` has an `Engine`). Use composition when:
  • You want to build complex objects from simpler, reusable components.
  • You need flexibility to change or replace components at runtime.
  • You want to avoid the rigidity of inheritance.

6. Best Practices for Using Composition
  1. Single Responsibility Principle: Ensure each component class has a single responsibility.
  2. Encapsulation: Hide the internal details of components and expose only necessary functionality.
  3. Dependency Injection: Pass components to the complex object rather than creating them internally. This makes the system more testable and flexible.
  4. Interfaces: Use interfaces or abstract classes to define contracts for components, allowing for interchangeable implementations.

7. Advanced Example: Dependency Injection
To make the `Car` class even more flexible, use dependency injection to pass the components:
class Car:
    def __init__(self, engine, wheels, transmission):
        self.engine = engine
        self.wheels = wheels
        self.transmission = transmission

    def drive(self):
        self.engine.start()
        self.transmission.shift_gear(1)
        self.wheels.rotate()
        print("Car is moving.")

# Usage
engine = Engine()
wheels = Wheels()
transmission = Transmission()
my_car = Car(engine, wheels, transmission)
my_car.drive()

Conclusion Composition is a powerful technique in Object-Oriented Design for building complex objects from simpler, reusable components. By breaking down a system into modular parts and combining them through composition, you can create flexible, maintainable, and scalable software systems. This approach is widely used in modern software development to manage complexity and promote code reuse.

Build More Complex Objects

Object diagrams demonstrating composition are similar to class diagrams showing composition. Most of the time, however, each separate object is given its own box, the actual values of the attributes are shown more often, and the relationship is generally not explicitly specified.
Object composition example
The given class diagram represents a "composition" between a `Line` class (labeled as `ray:Line`) and two `Point` objects (`start:Point` and `end:Point`). Here’s a breakdown of its attributes:
Classes and Attributes:
  1. Point Class:
    • Attributes:
      • x: A floating-point value representing the x-coordinate.
      • y: A floating-point value representing the y-coordinate.
  2. Line Class (ray:Line):
    • Attributes:
      • start: A Point object representing the starting position of the line.
      • end: A Point object representing the ending position of the line.

Instance Values:
  • The start:Point has:
    • x = 0.00
    • y = 0.00
  • The end:Point has:
    • x = 32.5
    • y = 987.34

Relationships: - The `ray:Line` class **contains** two `Point` instances (`start` and `end`), which suggests **composition** (i.e., a `Line` is composed of two `Point` objects). Object composition example


Depending on circumstances, different attributes of a class or object may or may not be shown in any one diagram. Much of the time, you are trying to show only certain attributes. In these cases, it's permissible to omit the attributes you aren't interested in. Simplifying a diagram by omitting the irrelevant parts of an object or class should make a diagram more intelligible.

Class Object Composition - Quiz


Click the Quiz link below to take a short quiz on class and object composition.
Class Object Composition - Quiz

SEMrush Software Target 9SEMrush Software Banner 9