Composition Design Pattern in Java with Real-World Examples
Design patterns play a critical role in crafting clean, maintainable, and scalable code. In my previous posts, I’ve covered several key patterns like Factory Method, Builder, Proxy, and Chain of Responsibility. Today, let’s explore another powerful concept: the Composition design pattern.
What is Composition?
Composition is a design principle that favors “has-a” relationships over “is-a” relationships (inheritance). Instead of extending functionality through subclassing, composition achieves it by containing instances of other classes that implement the desired functionality.
There’s a popular saying in object-oriented design: “Favor composition over inheritance.” But why?
Composition vs. Inheritance
Before diving deeper, let’s clarify the difference between these two approaches:
Inheritance: A class extends another class, inheriting its behavior and properties.
Composition: A class contains objects of other classes as members, delegating tasks to those objects.
Consider these key advantages of composition:
- Flexibility: You can change behavior at runtime by swapping component objects
- Reduced coupling: Components are independent and can evolve separately
- No fragile base class problem: Changes to a base class don’t inadvertently affect all subclasses
- Multiple source behaviors: You can incorporate functionality from multiple components, unlike single inheritance
Real-World Problem: Building a Content Management System
Let’s examine a practical scenario: building a flexible Content Management System (CMS) that handles different types of content—articles, videos, podcasts—each with unique features but sharing common operations.
A naive approach might use inheritance:
ContentItem (base class)
↓
├── Article
├── Video
└── Podcast
But what if we need combinations of features? What if some articles have videos? What if we need to add new content types or features without disrupting existing code?
This is where composition shines.
Implementing the CMS with Composition
Let’s build our CMS using composition. First, we’ll define the core interfaces:
public interface Content {
String getTitle();
String getDescription();
String render();
}
public interface ContentFeature {
String render();
}
Now, let’s implement some concrete content features:
public class TextFeature implements ContentFeature {
private String body;
public TextFeature(String body) {
this.body = body;
}
@Override
public String render() {
return "<div class='text-content'>" + body + "</div>";
}
}
public class ImageFeature implements ContentFeature {
private String url;
private String altText;
public ImageFeature(String url, String altText) {
this.url = url;
this.altText = altText;
}
@Override
public String render() {
return "<img src='" + url + "' alt='" + altText + "' class='content-image'>";
}
}
public class VideoFeature implements ContentFeature {
private String videoUrl;
private boolean autoplay;
public VideoFeature(String videoUrl, boolean autoplay) {
this.videoUrl = videoUrl;
this.autoplay = autoplay;
}
@Override
public String render() {
return "<video src='" + videoUrl + "' " +
(autoplay ? "autoplay" : "") +
" class='content-video' poster='" + videoUrl + "'></video>";
}
}
public class CommentFeature implements ContentFeature {
private List<String> comments;
public CommentFeature() {
this.comments = new ArrayList<>();
}
public void addComment(String comment) {
comments.add(comment);
}
@Override
public String render() {
StringBuilder sb = new StringBuilder("<div class='comments-section'>");
for (String comment : comments) {
sb.append("<div class='comment'>").append(comment).append("</div>");
}
sb.append("</div>");
return sb.toString();
}
}
Now, let’s create our base content item class that will use composition:
public class ContentItem implements Content {
private String title;
private String description;
private List<ContentFeature> features;
public ContentItem(String title, String description) {
this.title = title;
this.description = description;
this.features = new ArrayList<>();
}
public void addFeature(ContentFeature feature) {
features.add(feature);
}
@Override
public String getTitle() {
return title;
}
@Override
public String getDescription() {
return description;
}
@Override
public String render() {
StringBuilder result = new StringBuilder();
result.append("<article class='content-item'>");
result.append("<h1>").append(title).append("</h1>");
result.append("<p class='description'>").append(description).append("</p>");
for (ContentFeature feature : features) {
result.append(feature.render());
}
result.append("</article>");
return result.toString();
}
}
The class diagram for this implementation would look like:
Using the Composition-Based CMS
Let’s see how we can use our composition-based CMS to create different types of content:
public class CompositionMain {
public static void main(String[] args) {
// Create a simple article
ContentItem article = new ContentItem("Java Composition Pattern",
"Learn how to use composition in Java");
article.addFeature(new TextFeature("Composition is a design principle that..."));
article.addFeature(new CommentFeature());
// Create a video tutorial
ContentItem videoTutorial = new ContentItem("Video Tutorial: Composition in Action",
"Watch how to implement composition");
videoTutorial.addFeature(new TextFeature("Brief introduction to composition..."));
videoTutorial.addFeature(new VideoFeature("https://prasadct.com/wp-content/uploads/2025/04/CompositionDesignPatternVideoThumbnail.mp4", false));
videoTutorial.addFeature(new CommentFeature());
// Create a rich media article
ContentItem richArticle = new ContentItem("Advanced Composition Techniques",
"Mastering composition in large systems");
richArticle.addFeature(new TextFeature("When working with large systems..."));
richArticle.addFeature(new ImageFeature("https://prasadct.com/wp-content/uploads/2025/04/CompositionDesignPatternImage.jpg", "Composition diagram"));
richArticle.addFeature(new VideoFeature("https://prasadct.com/wp-content/uploads/2025/04/CompositionDesignPatternVideoThumbnail.jpg", false));
CommentFeature comments = new CommentFeature();
comments.addComment("Great article!");
comments.addComment("This helped me understand composition better.");
richArticle.addFeature(comments);
// Render all content items
System.out.println(article.render());
System.out.println(videoTutorial.render());
System.out.println(richArticle.render());
}
}
Output is in html format. Below is the visualisation of the output:
Java Composition Pattern
Learn how to use composition in Java
Composition is a design principle that…
Video Tutorial: Composition in Action
Watch how to implement composition
Brief introduction to composition…
Advanced Composition Techniques
Mastering composition in large systems
When working with large systems…

Great article!
This helped me understand composition better.
Extending with New Features
One of the key benefits of composition is how easily we can add new functionality. Let’s say we want to add a rating feature to our CMS:
public class RatingFeature implements ContentFeature {
private int starCount;
private List<Integer> userRatings;
public RatingFeature() {
this.userRatings = new ArrayList<>();
}
public void addRating(int stars) {
if (stars >= 1 && stars <= 5) {
userRatings.add(stars);
updateAverageRating();
}
}
private void updateAverageRating() {
if (userRatings.isEmpty()) {
starCount = 0;
return;
}
int sum = 0;
for (int rating : userRatings) {
sum += rating;
}
starCount = Math.round((float) sum / userRatings.size());
}
@Override
public String render() {
StringBuilder sb = new StringBuilder("<div class='rating'>");
sb.append("<span>Rating: </span>");
for (int i = 1; i <= 5; i++) {
if (i <= starCount) {
sb.append("★"); // Filled star
} else {
sb.append("☆"); // Empty star
}
}
sb.append(" (").append(userRatings.size()).append(" votes)");
sb.append("</div>");
return sb.toString();
}
}
// Adding rating feature to existing content
RatingFeature articleRating = new RatingFeature();
articleRating.addRating(4);
articleRating.addRating(5);
article.addFeature(articleRating);
RatingFeature videoRating = new RatingFeature();
videoRating.addRating(3);
videoRating.addRating(4);
videoRating.addRating(5);
videoTutorial.addFeature(videoRating);
Real-World Application: Enterprise CMS System
In a production environment, this pattern really shines. Let’s look at an enterprise-grade implementation with more sophisticated features.
public class AnalyticsFeature implements ContentFeature {
private String contentId;
private int viewCount;
public AnalyticsFeature(String contentId) {
this.contentId = contentId;
this.viewCount = 0;
}
public void incrementViews() {
viewCount++;
// In a real implementation, you might push this data to an analytics service
System.out.println("Content " + contentId + " views: " + viewCount);
}
@Override
public String render() {
incrementViews(); // Track that content was viewed
return "<script>trackContentView('" + contentId + "');</script>";
}
}
public class AccessControlFeature implements ContentFeature {
public enum AccessLevel {PUBLIC, REGISTERED, PREMIUM, ADMIN}
private AccessLevel requiredLevel;
public AccessControlFeature(AccessLevel requiredLevel) {
this.requiredLevel = requiredLevel;
}
public boolean canAccess(AccessLevel userLevel) {
return userLevel.ordinal() >= requiredLevel.ordinal();
}
@Override
public String render() {
// In a real application, this would integrate with your authentication system
return "<div class='access-level'>Access level: " + requiredLevel + "</div>";
}
}
Creating a premium content
ContentItem premiumTutorial = new ContentItem("Advanced Java Patterns",
"Premium tutorial for design patterns");
premiumTutorial.addFeature(new TextFeature("This premium content covers..."));
premiumTutorial.addFeature(new VideoFeature("https://example.com/premium.mp4", false));
premiumTutorial.addFeature(new AccessControlFeature(AccessControlFeature.AccessLevel.PREMIUM));
premiumTutorial.addFeature(new AnalyticsFeature("premium-tutorial-001"));
Performance Considerations
While composition offers great flexibility, it’s worth noting potential performance impacts:
- Memory Overhead: Each feature is a separate object, which may increase memory usage compared to inheritance-based solutions.
- Delegation Overhead: Method calls must traverse the delegation chain, which can add a small performance cost.
For most applications, these costs are minimal compared to the architectural benefits. However, in performance-critical systems, you might need to balance flexibility with performance needs.
When to Use Composition
Composition is particularly useful when:
- You need to combine behaviors from multiple sources
- You want to change behavior at runtime
- You’re dealing with a complex feature matrix that would be unwieldy with inheritance
- You need to avoid the “fragile base class problem”
Conclusion
Composition offers a powerful, flexible alternative to inheritance-based designs. By focusing on “has-a” relationships rather than “is-a” hierarchies, we can create more adaptable, maintainable systems.
Our CMS example demonstrates how composition allows us to:
- Combine features in creative ways
- Add new functionality without changing existing code
- Create more specialized content types without complex inheritance chains
Next time you’re facing a design challenge that might traditionally call for inheritance, consider whether composition might offer a more flexible solution.