Composition in .NET Object-Oriented Programming

A Simple, Powerful Approach

DotNet Full Stack Dev
5 min readOct 5, 2024

Have you ever felt a bit lost when hearing about composition in programming? Maybe it sounds like a fancy concept, but you’re unsure how to apply it effectively? Don’t worry, you’re not alone. In this blog, we’ll dive into composition, explaining it in a way that will help you get excited about using it in your projects.

Let’s walk through what composition is, why it’s so useful, and how it’s different from inheritance. We’ll also show you with examples that will make things crystal clear.

Embark on a journey of continuous learning and exploration with DotNet-FullStack-Dev. Uncover more by visiting our https://dotnet-fullstack-dev.blogspot.com reach out for further information.

What is Composition?

To put it simply, composition is about building complex things from simpler ones. In programming, composition means that instead of inheriting from a base class, your class contains objects that bring in the functionality you need.

Here’s a metaphor to make it even clearer: Imagine building a house. You don’t inherit a “wall” or “roof” from a parent house. Instead, you compose a house by assembling walls, a roof, windows, and doors. Each part (like a wall) can stand on its own, but together, they make a complete house. This is composition!

Real-World Example: A Computer

Let’s take a real-world analogy that you probably use every day — a computer. A computer is not built by inheriting components from a “MasterComputerClass.” Instead, it’s made up of smaller components: a CPU, RAM, Motherboard, and a Power Supply. Each part serves a different purpose, but when combined, they create a fully functional machine.

In terms of code, you can think of it like this:

public class CPU { 
public void Process() {
Console.WriteLine("Processing data...");
}
}

public class RAM {
public void Store() {
Console.WriteLine("Storing data...");
}
}

public class PowerSupply {
public void SupplyPower() {
Console.WriteLine("Powering the system...");
}
}

public class Computer {
private CPU _cpu;
private RAM _ram;
private PowerSupply _powerSupply;

public Computer(CPU cpu, RAM ram, PowerSupply powerSupply) {
_cpu = cpu;
_ram = ram;
_powerSupply = powerSupply;
}

public void Operate() {
_powerSupply.SupplyPower();
_cpu.Process();
_ram.Store();
Console.WriteLine("Computer is running...");
}
}

Here, the Computer class doesn’t inherit from CPU, RAM, or PowerSupply. Instead, it’s composed of these different objects. Each object has its own responsibility, and they work together to operate the computer.

Why Use Composition?

You might be thinking, “Okay, so it’s about putting pieces together. But why should I care about composition when inheritance exists?”

Here’s where it gets interesting. Composition offers flexibility and modularity. With inheritance, you get locked into a parent-child relationship, and any changes to the parent class might force unwanted behavior on child classes. On the other hand, composition is like a puzzle — you can replace any piece without messing up the entire structure.

For example, if you want to upgrade your computer with a new RAM, you just swap it out:

Computer myComputer = new Computer(new CPU(), new RAM(), new PowerSupply());

You don’t need to change the core structure of the Computer class; you simply plug in a different RAM object. That’s the beauty of composition—it’s modular.

Composition vs. Inheritance: What’s the Difference?

At this point, you might wonder: How is composition different from inheritance? Both allow you to reuse code, right?

Yes, but the how is important.

  • Inheritance: Creates a rigid hierarchy. You’re saying, “A car is a type of vehicle,” which means a car inherits everything from the vehicle class, even things that might not apply.
  • Composition: Assembles a class from smaller components. Instead of inheriting a vehicle’s properties, a car is built from an engine, wheels, and a steering wheel. This modular approach gives you more freedom and control.

Let’s see this in action with a code example comparing both approaches.

Inheritance Example (Less Flexible)

public class Vehicle {
public virtual void Move() {
Console.WriteLine("Vehicle is moving...");
}
}

public class Car : Vehicle {
public override void Move() {
Console.WriteLine("Car is driving...");
}
}

public class Boat : Vehicle {
public override void Move() {
Console.WriteLine("Boat is sailing...");
}
}

If you want to change how a car moves, you’ll need to override the Move method, but you’re still stuck with the constraints of the Vehicle class.

Composition Example (More Flexible)

public class Engine {
public void Start() {
Console.WriteLine("Engine started...");
}
}

public class Wheels {
public void Roll() {
Console.WriteLine("Wheels are rolling...");
}
}

public class Car {
private Engine _engine;
private Wheels _wheels;

public Car(Engine engine, Wheels wheels) {
_engine = engine;
_wheels = wheels;
}

public void Drive() {
_engine.Start();
_wheels.Roll();
Console.WriteLine("Car is driving...");
}
}

In this case, you can replace or upgrade any part of the car (like swapping out wheels or the engine) without touching the overall structure of the Car class. That’s the power of composition!

If you want to dig more into composition vs inheritance, refer: https://dotnetfullstackdev.substack.com/p/composition-over-inheritance-why

Garbage Collection and Composition

Now, let’s talk about what happens under the hood with memory management. In composition, since we’re creating objects that contain other objects, the .NET Garbage Collector (GC) manages the memory for us. When your object (like Car) goes out of scope, the garbage collector cleans up all the objects it references, such as Engine and Wheels, as long as they’re no longer in use.

Because composition often deals with smaller, reusable objects, it can lead to more efficient memory management. And since each object is independently managed, memory usage is often more predictable compared to large inheritance chains where objects can become tightly coupled.

Wrapping It All Up

Composition might sound like a simple idea, but its power lies in how it lets you build flexible, modular applications. It gives you the freedom to swap out parts, scale, and extend without the rigid constraints that often come with inheritance. And in the real world of software development, this flexibility can make a huge difference in how maintainable and adaptable your code becomes.

So, next time you’re designing your classes, think about how composition can simplify your code and provide you with greater control. After all, building great software is all about assembling the right parts!

What are your thoughts on composition? Have you used it in any of your projects? Let me know in the comments below — I’d love to hear your experience!

--

--

DotNet Full Stack Dev
DotNet Full Stack Dev

Written by DotNet Full Stack Dev

Join me to master .NET Full Stack Development & boost your skills by 1% daily with insights, examples, and techniques! https://dotnet-fullstack-dev.blogspot.com

No responses yet