Composition
Composition
Last updated: 2026-06-08 · 25 min read
Composition is an approach where an object holds references to other objects internally and delegates work to them as needed.
In object-oriented design, it is typically contrasted with inheritance.
In short:
Approach | Meaning |
|---|---|
Inheritance | Inherits the type relationship and implementation of a parent class |
Composition | Assembles other objects as components and delegates work to them |
Where inheritance generally expresses an is-a relationship, composition expresses has-a, part-of, or uses-a relationships.
Sentence | Usually the more natural design |
|---|---|
| Inheritance candidate |
| Composition candidate |
| Composition candidate |
| Composition candidate |
Note that is-a and has-a are not formal grammar rules. They are simplified heuristics meant to make explanations easier, not official definitions.
Personal note: This kind of thing is actually common. When conveying certain information, some details get stripped away for clarity, and in that process information is lost, distorted, and eventually turned into a meme.
Even when you can say "A is a B," composition is often the better choice in actual code. What matters is how the code will change, that is, the axis of change.
Terminology clarification
The composition discussed in this post refers to object-oriented composition. It is distinct from function composition in functional programming, that is, f(g(x)) or f ∘ g.
Both share the sense of "assembling larger things from smaller pieces," but it is important to note that what they operate on is different.
There is one more distinction to draw. Composition and interface-based polymorphism are not the same concept.
Name | Meaning | What you gain |
|---|---|---|
Concrete object composition |
| Code separation, responsibility separation |
Interface-based composition |
| Replaceability, testability, runtime policy changes |
Composition itself is often represented as a has-a structure.
In a broad object-oriented sense, composition is a structure in which one object references, owns, or uses another object to collaborate with it.
In the narrow UML sense, composition implies strong ownership and lifecycle dependency, but in practice, when people say "prefer composition over inheritance," they typically mean a broader scope that includes delegation, aggregation, and Dependency Injection (DI).
Polymorphism comes from substitutable contracts such as interface, abstract class, trait, and function pointer. In practice, when people say "prefer composition over inheritance," they usually mean interface-based composition, which combines both.
Personal note: interface-based composition is a phrase commonly used in practice, but it has no established academic term; the academic literature uses terms like polymorphic delegation or delegation through interfaces.
Why the advice "prefer composition over inheritance" arose
In early object-oriented programming, inheritance was extremely appealing. Cla
ss hierarchies could model real-world taxonomies and reuse code from parent classes. Structures like Animal -> Dog, Vehicle -> Car, and Order -> DiscountOrder look natural at first glance.
The problem was that over time, a number of recurring issues became apparent.
1. Inheritance bundles multiple meanings into one
In a 1996 ACM Computing Surveys paper, Taivalsaari noted that while inheritance is a key concept that distinguishes object-oriented programming, even researchers struggle to reach consensus on its meaning and proper use.1
Inheritance bundles code reuse, type hierarchy, conceptual classification, and programming-by-difference into a single syntax.
There is no problem as long as these four concerns always align, but in most cases, as a program grows, the reasons for code reuse, type relationships, domain classification, and change begin to diverge from one another.
Why you use inheritance | What actually comes along with it |
|---|---|
You want to reuse code | A type relationship is created as well |
You want to express domain classification | You become bound to the parent's implementation |
You want to use polymorphism | You must honor behavioral contracts |
You want to change only part of the behavior | You become dependent on override ordering and internal call sequences |
This is actually the key to understanding Composition. It does not mean inheritance is bad, but inheritance bundles too many meanings into a single syntax.
Composition is the method for separating reuse, role assembly, and policy replacement from the type hierarchy.
2. Inheritance can weaken encapsulation
In his 1986 OOPSLA paper, Alan Snyder observed that while many object-oriented languages provide data abstraction, the benefits of encapsulation are severely weakened once inheritance enters the picture.2
Subclasses often need to know the parent's protected fields, internal call sequences, virtual method call timing, and constructor rules. Many people wonder why this is a problem, but according to Parnas's information hiding theory, which has had a profound influence on OOP, this can be seen as a form of information hiding failure.
In other words, inheritance tends to mean not "collaborate by looking only at the public API" but rather "extend by knowing the parent's internal details."
public abstract class Order
{
// `protected` is not `public`, but it still exposes internal state to subclasses.
// Once a subclass starts depending on that field, changing the parent's implementation becomes difficult.
protected decimal price;
public decimal FinalPrice()
{
// The parent may think of this ordering as an internal implementation detail.
// However, if a subclass overrides `CalculateDiscount()`, that ordering effectively becomes part of the contract.
return price - CalculateDiscount();
}
protected abstract decimal CalculateDiscount();
}
public sealed class PremiumOrder : Order
{
protected override decimal CalculateDiscount()
{
// The subclass must know when and in what unit the parent populates `price`.
// At that moment, the parent's internal representation leaks into the subclass's code.
return price * 0.2m;
}
}
In this example, is the Template Method pattern itself the real problem?
Not at all. The core issue is that two concerns have been mixed together, making things compound.
The parent exposes its internal state to subclasses via
protected price.Because the parent's
FinalPrice()calls the overridableCalculateDiscount(), the parent's execution order effectively becomes a contract with subclasses.
If the parent changes price to a Money value object, alters the order of tax calculation, or moves the discount calculation to a different step, all subclasses are affected. This is the classic point at which inheritance breaks encapsulation.
Personal note: Genetically, it makes sense for parents to pass things down to children, not the other way around. Culturally, though, you could argue it works both ways, since children do bring change to their parents. As for me, I haven't managed to get myself stable at this age, and I think I've picked my parents clean of a few vertebrae or so, which is probably why their backs are bent.
3. Subtypes must honor behavior, not just structure
Inheritance is not simply code reuse.
It typically creates a type relationship. The 1994 paper by Liskov and Wing explains that for a subtype to be substitutable for its supertype, any property proven about the supertype must hold for the subtype as well.3
In practice, this translates as follows.
A subclass doing a convincing impression of its parent class is not enough; it must also honor the behavioral contracts the parent class has promised.
Note, however, that this is not a problem unique to inheritance. If a ReadOnlyList that implements IList throws an exception in Add(), the same problem arises, because an interface implementation is also a subtype.
So the Liskov Substitution Principle is not a direct argument for "composition is superior to inheritance." A more precise conclusion is the following.
Whether you use inheritance or an interface, creating a subtype relationship means you must honor the behavioral contract.
If you need a read-only collection, you should expose a narrower contract like
IReadOnlyListrather thanIList.Where composition helps is that it lets you avoid forcing a subtype relationship in the first place, hiding the internal implementation while exposing a smaller API.
4. Axes of change become trapped in a single inheritance tree
An inheritance tree is fundamentally well-suited to classification along one dimension.
In practice, however, objects change along multiple dimensions driven by different requirements.
Consider a payment example.
Pushing through with inheritance produces increasingly long names: Payment, CardPayment, BankTransferPayment, CouponCardPayment, PremiumCouponCardPayment, InternationalPremiumCouponCardPayment, and so on.
Initially there might have been only CardPayment and BankTransferPayment. As coupons, membership tiers, and regional policies get added, class names become increasingly combinatorial. The result is that several distinct axes of change, payment method, discount, user tier, and country, all end up crammed into a single inheritance tree.
Personal note: The vast majority of Korean online shops set artificially high list prices and then use coupons to create an illusion of a bargain. Given actual wholesale costs, who knows how much margin is left. Whatever anchoring effect those coupons may buy in customer goodwill, they are remarkably effective at buying rage from the programmers who have to implement them.
Composition splits those axes apart, like this.
The role of | Reason for change |
|---|---|
| Payment methods such as credit card, bank transfer, and points |
| Coupons, events, and membership-tier discounts |
| Policies for general, premium, and corporate customers |
| Domestic, international, tax, and currency policies |
This way, changing the payment method and changing the discount policy are less likely to conflict with each other.
The direction the GoF laid out
The GoF's Design Patterns (1994) introduced the principle of favoring "object composition" over "class inheritance."4 This advice does not mean inheritance should be forbidden.
To put it more precisely, the intent would be something like the following.
Do not inherit solely for implementation reuse.
Separate behavior that must change at runtime into its own object.
When class hierarchies lead to a combinatorial explosion, split the axes using object composition.
If you need to know the internal implementation of a parent class, treat that as a warning sign.
This is why a large number of GoF patterns are implemented using composition.
Patterns | How composition is used |
|---|---|
Strategy pattern | Extracts an algorithm into an object so it can be swapped out |
Decorator pattern | Wraps an existing object to layer on additional behavior |
Adapter pattern | Converts an external interface into an internal interface |
Bridge | Separates abstraction from implementation so each can vary independently |
Composite | Represents part-whole relationships through object composition |

(Effective Java, 3rd Edition, Item 18)
Joshua Bloch also dedicates a separate item in Effective Java to "Favor composition over inheritance."5 He particularly emphasizes that when you extend a public class through inheritance, changes to the parent's internal implementation can easily break the child.
Basic structure of Composition
Composition is typically carried out in three steps.
Identify the behavior that varies.
Extract that behavior into a small object.
The host object delegates work to that object.
The basic form is concrete-object delegation.
public sealed class TaxCalculator
{
public decimal Calculate(decimal price)
{
return price * 0.1m;
}
}
public sealed class Order
{
private readonly TaxCalculator taxCalculator;
public Order(TaxCalculator taxCalculator)
{
// `Order` does not directly contain the tax calculation logic.
// It delegates the tax calculation responsibility to a separate object.
this.taxCalculator = taxCalculator;
}
public decimal TotalPrice(decimal price)
{
return price + taxCalculator.Calculate(price);
}
}
This structure separates responsibilities, but it provides no polymorphism that lets you easily swap TaxCalculator for a different implementation. If replaceability is also required, move to interface-based composition.
For example, a discount policy is not intrinsic to an order; it is a policy that the order uses.
// The discount policy expresses "what discount rule an order will use."
// It is an interface for separating the order itself from the discount algorithm.
public interface IDiscountPolicy
{
decimal CalculateDiscount(decimal price);
}
public sealed class NoDiscountPolicy : IDiscountPolicy
{
public decimal CalculateDiscount(decimal price)
{
// No discount is also treated as one policy.
// This avoids scattering null checks and `if` branches throughout `Order`.
return 0;
}
}
public sealed class RateDiscountPolicy : IDiscountPolicy
{
private readonly decimal rate;
public RateDiscountPolicy(decimal rate)
{
// `rate` is the immutable configuration value for this policy.
// `Order` does not need to know whether this value is 10% or 20%.
this.rate = rate;
}
public decimal CalculateDiscount(decimal price)
{
return price * rate;
}
}
public sealed class Order
{
private readonly IDiscountPolicy discountPolicy;
public Order(IDiscountPolicy discountPolicy)
{
// `Order` does not create a concrete discount policy.
// When a policy is injected from the outside, `Order` simply uses that policy.
this.discountPolicy = discountPolicy;
}
public decimal FinalPrice(decimal price)
{
// The responsibility of `Order` is to control the overall price calculation flow.
// The detailed discount rules are delegated to `discountPolicy`.
var discount = discountPolicy.CalculateDiscount(price);
return price - discount;
}
}
In this structure, Order has no knowledge of whether the discount strategy is percentage-based, coupon-based, or event-based. You only need to swap the discount policy object.
Here, flexibility emerges from two elements working together.
Composition:
Orderholds a reference to the discount policy object internally and delegates to it.Interface: the
IDiscountPolicyimplementation can be swapped out.
The two must be distinguished. Composition alone yields separation of responsibilities; adding an interface on top enables replaceable polymorphism.
Combining multiple axes of change
The power of Composition shows its greatest effect when there are two or more axes of change.
public interface IPaymentMethod
{
void Pay(decimal amount);
}
public interface IShippingFeePolicy
{
decimal CalculateShippingFee(decimal orderPrice);
}
public interface IDiscountPolicy
{
decimal CalculateDiscount(decimal orderPrice);
}
public sealed class CheckoutService
{
private readonly IPaymentMethod paymentMethod;
private readonly IShippingFeePolicy shippingFeePolicy;
private readonly IDiscountPolicy discountPolicy;
public CheckoutService(
IPaymentMethod paymentMethod,
IShippingFeePolicy shippingFeePolicy,
IDiscountPolicy discountPolicy)
{
// Payment method, shipping fee policy, and discount policy are distinct axes of change.
// Rather than placing them in a single inheritance tree, compose them independently.
this.paymentMethod = paymentMethod;
this.shippingFeePolicy = shippingFeePolicy;
this.discountPolicy = discountPolicy;
}
public void Checkout(decimal orderPrice)
{
var discount = discountPolicy.CalculateDiscount(orderPrice);
var shippingFee = shippingFeePolicy.CalculateShippingFee(orderPrice);
var finalAmount = orderPrice - discount + shippingFee;
// The payment method can vary across options such as credit card, bank transfer, and points redemption.
// `CheckoutService` has no knowledge of the internal implementation of the payment method.
paymentMethod.Pay(finalAmount);
}
}
With inheritance, you end up with combinations like CardPremiumFreeShippingCheckout and BankCouponNormalShippingCheckout. Composition lets you vary payment, discount, and shipping independently.
Connection to the Law of Demeter
The Law of Demeter is often summarized as "don't talk too deeply to strangers." It is a principle formalized in Lieberherr and Holland's 1989 paper Assuring Good Style for Object-Oriented Programs.6
Bad example:
// `OrderService` reaches directly into `customer`'s `wallet` to access the internal `creditCard`.
// It knows far too much about the internal structure.
var cardNumber = order.Customer.Wallet.CreditCard.Number;
Better example:
// `OrderService` does not pry into the internal structure of the customer.
// The decision on whether payment is possible is delegated to `customer` or `paymentMethod`.
var canPay = order.Customer.CanPay(order.TotalPrice);
Composition connects to the Law of Demeter in that it means "rather than digging into internals yourself, you delegate the work to a nearby collaborating object."
However, this does not automatically produce good design. Slicing objects too finely can actually increase delegation hops and produce train-wrecks like a.GetB().GetC().Do().
The core of the Law of Demeter, therefore, is not about counting dots. It is about reducing the amount of state that external code must know about internal structure in order to make decisions.
When to use Composition
If you answer "yes" to most of the following questions, consider Composition first.
Does this behavior need to change at runtime?
Is this behavior repeated across multiple classes?
Does using inheritance cause a combinatorial explosion in the number of subclasses?
Can you collaborate without knowing the internal implementation of the parent class?
Do you want to swap this part out with a fake object in tests?
Is it a replaceable role, like a policy, strategy, adapter, repository, or renderer?
In particular, when you see the following names, you have sufficient reason to treat the class as a composition candidate.
That said, a name is a smell, not a justification. Call it programmer intuition. The situations you encounter while working accumulate and eventually become code smells.
Simply naming something Policy does not automatically make it worth separating.
Name | Typical meaning |
|---|---|
| Decision rule |
| Replaceable algorithm |
| External interface conversion |
| Output format conversion |
| Display format conversion |
| Validation rules |
| Storage boundary |
| External value provision |
| Execution timing control |
| Data serialization |
These are generally roles that an object uses, not part of what that object fundamentally is.
When is Inheritance the better choice?
Composition is not always the right answer. There are cases where inheritance is the more natural choice.
When a genuine subtype relationship is stable
When the parent class is explicitly designed for extension
When you need to fix the skeleton of an algorithm while varying only certain steps, as in the Template Method pattern
When a framework requires inheritance-based extension points
When the type hierarchy itself is central to the domain model
When the parent and child are maintained together by the same team within the same package or module
When you need to avoid virtual dispatch and pointer indirection in a performance-critical inner loop
The real problem is not inheritance itself, but the habit of using inheritance as a shortcut for implementation reuse. When a parent class is not designed as an extension API, child classes end up clinging to the parent's internal details.
Personal note: Inheritance sometimes looks like "free reuse," but in practice you often inherit the parent class's entire life history along with it. Just as a child happily accepts an inheritance from a wealthy parent but would rather escape a poor one, it is best to inherit only when there is actually something worth inheriting from a good parent.
Where inheritance is especially natural: open recursion
The most representative case where inheritance is more natural than composition is open recursion: the property by which, when a parent method calls this.SomeStep(), the call dispatches to the override on the actual runtime type.
Cook, Hill, and Canning's Inheritance Is Not Subtyping also treats inheritance and subtyping as distinct concepts.7
This is precisely why the Template Method pattern wants inheritance.
public abstract class ImportJob
{
public void Run()
{
// `Run()` lives in the parent, while `Parse()` and `Save()` are left open for child overrides.
// This self-dispatch is open recursion.
var rows = Parse();
Save(rows);
}
protected abstract IReadOnlyList<Row> Parse();
protected abstract void Save(IReadOnlyList<Row> rows);
}
You can approximate the same effect with composition, but when you delegate to an inner object, that object's this refers to the inner object itself, not the wrapper. Self-dispatch that returns back up to the wrapper does not come for free. For this reason, inheritance can be simpler in structures that fix an algorithmic skeleton while leaving certain steps open, such as framework hooks, UI lifecycle callbacks, test fixtures, and import/export pipelines.
That said, exposing too much protected state in the parent brings back the encapsulation problems discussed earlier. A good Template Method minimizes shared state and keeps the contracts for each overridable step narrow.
Practical Limitations of Composition
Composition has its own significant limitations. It does not eliminate the complexities of inheritance; it relocates them. The difference is that it moves them somewhere less immediately visible to the programmer.
1. Physical Memory Cost
Composition multiplies object references. If Order holds an IDiscountPolicy, and that policy in turn holds other objects, the execution flow may look clean, but memory accesses become scattered.
From the CPU's perspective, this can introduce the following costs:
Many small objects are allocated on the heap.
Execution follows pointers from one object to another.
Data is not stored in contiguous arrays, which weakens spatial locality.
Interface calls or virtual dispatches repeat inside inner loops.
The costs of cache misses, TLB misses, and branch misprediction can grow significantly.
There is research suggesting that object-oriented programs can be particularly vulnerable to cache and TLB misses due to their heavy reliance on objects and pointers.8 The existence of separate research efforts aimed at improving locality in pointer-based data structures reflects the same concern.9
For this reason, in domains that traverse large amounts of data every frame or tick, such as game engines, simulations, and high-performance financial systems, data-oriented approaches like Structure of Arrays (SoA), Entity-Component System (ECS), and Job Systems can be more appropriate than object-reference-based composition.10
An example of composition used poorly:
foreach (var particle in particles)
{
// Each loop iteration follows object references and performs interface dispatch.
// This is fine with a small number of objects, but with hundreds of thousands processed every frame, it can become a bottleneck.
particle.Integrator.Update(particle, deltaTime);
}
For hot loops, a structure like this may be preferable:
for (var i = 0; i < count; i++)
{
// It reads the required data from a contiguous array.
// While this reduces the flexibility of swapping out policy objects, memory access becomes far more predictable.
velocityX[i] += accelerationX[i] * deltaTime;
positionX[i] += velocityX[i] * deltaTime;
}
In short, Composition gains design flexibility but may trade away data locality in return. This cost is barely visible in web CRUD applications, but it shows up immediately in inner loops.
Personal note: The essence of programming is ultimately trade-offs. What looks clean to the programmer often makes the machine uncomfortable, and the reverse is equally true.
2. Product lifecycle cost
The advice to "extract changing behavior behind an interface" is sound. However, in the MVP stage, it is often unclear what will actually change.
Splitting payment, shipping, discounts, inventory, and notifications into separate interfaces from the start and wiring them together with a Dependency Injection (DI) container slows development. You end up growing the architecture for future extensibility while there is still no market feedback to justify it.
For an early-stage product, the following choices may serve you better:
Start with a simple
if.Use concrete classes directly.
Wait until duplication appears two or three times.
Extract a policy object only after the axis of change has actually revealed itself.
This is not a matter of being ignorant of design principles; it is a matter of not spending resources on axes of change that are not yet known.
Personal note: A startup programmer is fine hardcoding a simple, fast MVP. But the more you need to scale or, like me, deliver to multiple clients, the more you need to templatize and abstract.
3. Complexity does not disappear; it moves
Inheritance hides complexity in the class hierarchy. Composition shifts complexity into the object assembly process.
Approach | Where complexity hides |
|---|---|
Inheritance | Parent/child hierarchy, override order, protected state |
Composition | Constructor injection, DI container, runtime implementation selection |
Having many small objects can make testing easier, but tracking which implementation was injected at runtime becomes harder. When DI configuration is scattered across multiple modules, anyone reading the code first has to figure out "what's the actual implementation of this interface?" If you slice things too finely, a single policy change ends up touching multiple objects and DI configurations simultaneously, which can produce the smell of anti-patterns like Shotgun Surgery.
4. Misuse of the Law of Demeter
The Law of Demeter is a principle about reducing exposure to internal structure, not a directive to wrap every access in a forwarding method.
A bad misuse example:
public sealed class Order
{
private readonly Customer customer;
public string GetCustomerName()
{
// The result is a proliferation of forwarding calls just to extract simple data.
// When dozens of such methods accumulate, the object becomes a relay station rather than an encapsulated unit.
return customer.Name;
}
}
order.Customer.Name is not always bad. The problem is when external code has to know about deeply nested internal structure and policy decisions, like order.Customer.Wallet.CreditCard.Provider.RetryPolicy. Trying to hide even simple lookups leads to an explosion of wrapper methods.
One way to reduce this dilemma is to apply the 'Tell, Don't Ask' principle. Rather than extracting data from an object (Ask) and handling the logic externally, you should delegate the computation or behavior to the object that knows its data best (Tell).
This naturally preserves encapsulation and allows Composition responsibilities to be divided without a wrapper explosion.
5. Framework Hooks and Inner Loops
When a framework requires hook methods on a specific base class, inheritance can be the practical answer. UI frameworks, game engines, test frameworks, and some ORM extension points are designed around the contract of "override this method." Unity, the game engine widely used in C#, is a prime example of this.
In performance-critical inner loops, you may also need to remove Composition entirely. Policy objects split finely behind interfaces work well at outer boundaries, but inside a computation that runs millions of times, direct functions, structs, arrays, SIMD, and Structure of Arrays (SoA) layouts may be a better fit.
Good Composition separates axes of change that have been confirmed to vary. Bad Composition turns straightforward code into a Dependency Injection (DI) container maze in anticipation of a future that hasn't materialized yet.
My criteria
When I evaluate whether to use Composition, I generally ask the following questions.
Is this an intrinsic identity or a role?
Does this axis of change vary independently of other axes of change?
If I model this with Inheritance, will the number of classes explode combinatorially?
Do I want to swap this part out in tests?
Are there so many delegation relationships that the overall flow becomes hard to follow?
Is this code in an inner loop, or at an outer policy boundary?
Does this abstraction actually help the product move faster right now?
In summary
Use Inheritance sparingly when expressing "identity," and reach for Composition first when assembling "roles" and "policies."
That said, in hot loops and early-stage products, simplicity and data locality may matter more than flexibility.
See also
Object-oriented programming
Programming patterns
Strategy pattern
Decorator pattern
Adapter pattern
Encapsulation
Inheritance
Polymorphism
Dependency inversion
Data-oriented programming
References
Ralph E. Johnson; Brian Foote. "Designing Reusable Classes". Journal of Object-Oriented Programming, 1(2), 22-35, 1988. A discussion of reusable class design and frameworks.
Ulrich Drepper. "What Every Programmer Should Know About Memory". 2007. A classic reference for understanding CPU caches, locality, and memory hierarchies.
Footnotes
- Antero Taivalsaari. "On the Notion of Inheritance". ACM Computing Surveys, 28(3), 438-479, 1996. DOI: 10.1145/243439.243441. https://courses.cs.umbc.edu/331/resources/papers/Inheritance.pdf ↩
- Alan Snyder. "Encapsulation and Inheritance in Object-Oriented Programming Languages". OOPSLA 1986. DOI: 10.1145/28697.28702. An early discussion of how inheritance can weaken the advantages of encapsulation. ↩
- Barbara Liskov; Jeannette Wing. "A Behavioral Notion of Subtyping". ACM Transactions on Programming Languages and Systems, 16(6), 1811-1841, 1994. DOI: 10.1145/197320.197383. ↩
- Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1994. Online bibliography: InformIT ↩
- Joshua Bloch. Effective Java, 3rd edition. Addison-Wesley, 2018. Item 18: "Favor composition over inheritance." ↩
- Karl J. Lieberherr; Ian M. Holland. "Assuring Good Style for Object-Oriented Programs." IEEE Software, 1989. Reference: Law of Demeter bibliography, Law of Demeter: Principle of Least Knowledge ↩
- William R. Cook; Walter L. Hill; Peter S. Canning. "Inheritance Is Not Subtyping". POPL 1990. DOI: 10.1145/96709.96721. A seminal discussion arguing that inheritance and subtyping should not be conflated. ↩
- Martin Hirzel et al. "Data layouts for object-oriented programs". SIGMETRICS 2007. A study examining object and pointer dependencies in object-oriented programs and their impact on cache and TLB misses. ↩
- Trishul M. Chilimbi; Mark D. Hill; James R. Larus. "Making Pointer-Based Data Structures Cache Conscious". IEEE Computer, 33, 67-74, 2000. Discussion of improving locality in pointer-based data structures. ↩
- Shawn M. Harris. "Implementation and Analysis of the Entity Component System Architecture". California Polytechnic State University, 2022. A master's thesis examining the Entity-Component System as a data-oriented composition pattern and comparing its performance characteristics with traditional object-oriented structures. ↩