Object-Oriented Programming (OOP) isn’t limited to the four main principles—encapsulation, inheritance, polymorphism, and abstraction. There are additional concepts that, when combined with the core principles, help build well-structured, maintainable, and reusable code. In this post, we'll cover composition, association, aggregation, cohesion, and coupling—key ideas that further enhance your OOP knowledge.
1. Composition: "Has-a" Relationship
Definition:
Composition is a design principle where one class contains an instance of another class. Instead of inheriting behavior, the object "has-a" relationship with another object.
Why It Matters:
- Promotes flexibility by enabling object reuse.
- Avoids the downsides of deep inheritance hierarchies.
C# Example:
public class Engine
{
public void Start() => Console.WriteLine("Engine started.");
}
public class Car
{
private readonly Engine engine = new Engine(); // Car "has-a" Engine.
public void StartCar()
{
engine.Start();
Console.WriteLine("Car is running.");
}
}
// Usage
var car = new Car();
car.StartCar(); // Output: "Engine started." "Car is running."
2. Association: General Relationship Between Classes
Definition:
Association represents a general "uses" relationship between two classes where one class uses or interacts with another.
- Unidirectional Association: One class knows about the other (e.g.,
Doctor
knows aboutPatient
). - Bidirectional Association: Both classes know about each other.
C# Example:
public class Doctor
{
public string Name { get; set; }
public void Treat(Patient patient)
{
Console.WriteLine($"{Name} is treating {patient.Name}.");
}
}
public class Patient
{
public string Name { get; set; }
}
// Usage
var doctor = new Doctor { Name = "Dr. Sarah" };
var patient = new Patient { Name = "John Doe" };
doctor.Treat(patient); // Output: "Dr. Sarah is treating John Doe."
3. Aggregation: "Whole-Part" Relationship (Weak Ownership)
Definition:
Aggregation is a type of association where one class represents a "whole-part" relationship, but the parts can exist independently of the whole.
Why It Matters:
- Supports loose coupling between the container and its contained classes.
C# Example:
public class Team
{
public List<Employee> Members { get; } = new List<Employee>();
public void AddMember(Employee employee)
{
Members.Add(employee);
}
}
public class Employee
{
public string Name { get; set; }
}
// Usage
var team = new Team();
var employee = new Employee { Name = "Alice" };
team.AddMember(employee); // The `Employee` exists independently of the `Team`.
In this example, the Employee
can exist without being part of a Team
.
4. Cohesion: Single Responsibility of a Class
Definition:
Cohesion measures how closely related the responsibilities of a class are. High cohesion means that the class performs a single, well-defined task.
Why It Matters:
- High cohesion improves code readability and maintainability.
- A cohesive class is easier to understand and debug.
Example:
public class InvoiceService
{
public void GenerateInvoice()
{
Console.WriteLine("Generating invoice...");
}
public void EmailInvoice()
{
Console.WriteLine("Emailing invoice...");
}
}
If you add unrelated methods (like database management) in this class, cohesion decreases. Instead, break responsibilities into different classes.
5. Coupling: Dependency Between Classes
Definition:
Coupling refers to the degree of dependency between classes.
- Tightly Coupled: Classes are strongly dependent on each other.
- Loosely Coupled: Classes can function independently of each other.
Why It Matters:
- Loose coupling improves flexibility and makes the code more adaptable to changes.
- Tight coupling makes the system harder to modify and maintain.
C# Example:
public class ReportGenerator
{
private readonly IReportFormatter formatter;
public ReportGenerator(IReportFormatter reportFormatter)
{
formatter = reportFormatter; // Loose coupling via interface
}
public void Generate()
{
formatter.FormatReport();
Console.WriteLine("Report generated.");
}
}
public interface IReportFormatter
{
void FormatReport();
}
public class PDFReportFormatter : IReportFormatter
{
public void FormatReport() => Console.WriteLine("Formatting report as PDF...");
}
// Usage
var pdfFormatter = new PDFReportFormatter();
var generator = new ReportGenerator(pdfFormatter);
generator.Generate(); // Output: "Formatting report as PDF..." "Report generated."
By using an interface (IReportFormatter
), the ReportGenerator
class is loosely coupled to the formatter. You can easily swap out the implementation without changing the ReportGenerator
class.
Conclusion
Understanding these additional OOP concepts helps you write better-structured, maintainable, and more reusable code. While the core principles of OOP lay the foundation, concepts like composition, association, aggregation, cohesion, and coupling further enrich your design approach.
EmojiEmoji