Understanding the Composite Reuse Principle (CRP): Concepts, Benefits, UML, and Implementation Examples
This article provides a comprehensive explanation of the Composite Reuse Principle (CRP), covering its definition, background, advantages over inheritance, UML representation, practical Java code examples, and guidelines for choosing between composition and inheritance in object‑oriented design.
Composite Reuse Principle (CRP) , also known as the Composition/Aggregation Reuse Principle, advocates using composition or aggregation before inheritance to achieve code reuse.
01. What is the Composite Reuse Principle?
The CRP states that when reusing code, developers should first consider composition or aggregation relationships, and only resort to inheritance if necessary.
02. Origin of the Composite Reuse Principle
Early object‑oriented programming relied heavily on inheritance for reuse, which introduced problems such as tight coupling, complex inheritance hierarchies, exposure of internal implementation (white‑box reuse), and limited flexibility because inheritance is static.
To address these issues, the Composite Reuse Principle was introduced, promoting more elegant code reuse through composition.
Advantages of Composition
Low coupling: components are loosely connected, reducing dependency.
Black‑box reuse: internal details of composed objects remain hidden, preserving encapsulation.
Higher flexibility: composition is not constrained by static inheritance relationships.
Supports the Single Responsibility Principle for each component.
03. Choosing Between Composition and Inheritance
Composition is suitable when requirements change frequently or when a class needs functionality from other classes without inheriting all their members. Inheritance is appropriate for extending existing class behavior, but must obey the Liskov Substitution Principle and the Coad rules.
3.1 Liskov Substitution Principle
Subtypes must be replaceable for their base types without altering expected behavior.
3.2 Coad Rules
Use inheritance only for true "is‑a" relationships; use composition for "has‑a" relationships.
Avoid inheritance when future substitution of subclasses is uncertain.
Subclasses should extend, not replace, parent responsibilities.
Do not inherit from utility classes unless they represent a taxonomy.
04. Implementation Example
Assume a car management program with two classification dimensions: power source (electric, petrol) and color (white, black, red). Using inheritance would require six subclasses, leading to class explosion and violation of the Open/Closed Principle.
Using composition, we define a Color interface with concrete implementations (White, Black, Red) and inject a Color object into the Car class.
Code using inheritance:
public abstract class Car {
abstract void run();
}
public class ElectricCar extends Car {
@Override
void run() {
System.out.println("电动汽车");
}
}
public class PetrolCar extends Car {
@Override
void run() {
System.out.println("汽油汽车");
}
}
public class BlackElectricCar extends ElectricCar {
public void appearance() {
System.out.print("黑色");
super.run();
}
}
... (other color‑specific subclasses) ...
public class Test {
public static void main(String[] args) {
RedElectricCar redElectricCar = new RedElectricCar();
redElectricCar.appearance(); // 红色电动汽车
}
}Code using composition:
public abstract class Car {
abstract void run();
Color color;
public Color getColor() { return color; }
public void setColor(Color color) { this.color = color; }
}
public interface Color {
void colorKind();
}
public class ElectricCar extends Car {
@Override
void run() { System.out.println("电动汽车"); }
}
public class PetrolCar extends Car {
@Override
void run() { System.out.println("汽油汽车"); }
}
public class White implements Color {
@Override
public void colorKind() { System.out.println("白色"); }
}
public class Black implements Color {
@Override
public void colorKind() { System.out.println("黑色"); }
}
public class Red implements Color {
@Override
public void colorKind() { System.out.println("红色"); }
}
public class Test {
public static void main(String[] args) {
ElectricCar electricCar = new ElectricCar();
White white = new White();
electricCar.setColor(white);
electricCar.getColor().colorKind(); // 白色
electricCar.run(); // 电动汽车
}
}With composition, only five classes are needed, and adding new colors or power sources requires creating new Color implementations without modifying existing code, thus adhering to the Open/Closed Principle.
Summary
The Composite Reuse Principle encourages using composition over inheritance for code reuse, offering lower coupling, better encapsulation, and greater flexibility; it should be applied after evaluating the Liskov Substitution Principle and Coad rules, and illustrated with Java examples that contrast inheritance‑heavy designs with composition‑based solutions.
Mike Chen's Internet Architecture
Over ten years of BAT architecture experience, shared generously!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.