| Inheritance ("is-a") | Composition ("has-a") | |
|---|---|---|
| Cơ chế | class B extends A | class B { A a; } |
| Coupling | Chặt — biết internal parent | Lỏng — chỉ phụ thuộc interface |
| Đổi runtime | Không | Có (đổi member) |
Nguyên tắc Effective Java: Favor composition over inheritance.
Ví dụ kinh điển vì sao inheritance dễ sai:
java
// ❌ Extends HashSet để đếm — DOUBLE COUNT bug
class InstrumentedHashSet<E> extends HashSet<E> {
private int addCount = 0;
@Override public boolean add(E e) { addCount++; return super.add(e); }
@Override public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c); // super.addAll gọi NỘI BỘ add() → đếm 2 lần
}
}Composition là fix:
java
// ✅ Delegation — không phụ thuộc internal
class InstrumentedSet<E> implements Set<E> {
private final Set<E> delegate;
private int addCount = 0;
public boolean add(E e) { addCount++; return delegate.add(e); }
}Khi dùng inheritance: quan hệ "is-a" thật sự, cùng team/package, parent designed for extension (vd AbstractList).
Khi dùng composition: quan hệ "has-a"/"uses-a", cần đổi behavior runtime, class third-party, Decorator/Adapter/Proxy pattern.
Quy tắc cuối: extends class không cùng package mà parent không nói "designed for extension" → gần như chắc chắn nên composition.