Is-a는 타입의 관계로, inheritance로 모델링됩니다(Car은 Vehicle). 는 의 관계로, 으로 모델링됩니다(은 을 ). 올바른 것을 선택하는 것은 핵심 모델링 결정입니다.
Is-a는 타입의 관계로, inheritance로 모델링됩니다(Car은 Vehicle). 는 의 관계로, 으로 모델링됩니다(은 을 ). 올바른 것을 선택하는 것은 핵심 모델링 결정입니다.
CarEngine// IS-A → inheritance
class Vehicle { void move() {} }
class Car extends Vehicle { } // Car 은 Vehicle 이다
// HAS-A → composition
class Engine { void start() {} }
class Car2 {
private Engine engine = new Engine(); // Car 은 Engine 을 가진다
void start() { engine.start(); } // 부품에게 위임
}
질문하세요: "X가 Y의 일종인가, 아니면 X가 Y를 가지거나 사용하는가?"
Dog IS-A Animal → inheritance ✅
Car HAS-A Engine → composition ✅
Square IS-A Shape → inheritance ✅
Manager HAS Employees → composition (리스트) ✅
Stack HAS-A 리스트 (IS-A 아님) → composition (앞의 함정 참조) ✅
사람들은 관계가 실제로 has-a일 때조차 코드를 재사용하려고 inheritance에 손을 댑니다. 서브클래스를 모든 곳에서 기반 대신 대체하지 않을 거라면, 그것은 아마 is-a가 아닙니다 — composition을 사용하세요.
이 구분은 "inheritance보다 composition을 선호하라" 뒤에 있는 실용적인 결정 규칙입니다: 몇 줄을 아끼는 관계가 아니라 진실한 관계를 선택하세요.
올바르게 하면 계층 구조가 얕고 정직하게 유지되며, "서브타입"이 실제로 부모를 대체할 수 없는 Liskov 위반을 방지합니다.