Mixins & Composition
ปัญหาของ inheritance ที่ลึก
หัวข้อที่มีชื่อว่า “ปัญหาของ inheritance ที่ลึก”single inheritance บังคับให้ทุกความสามารถอยู่ใน chain เดียวที่แข็งทื่อ ถ้า Timestamped, Serializable และ Comparable เป็น behavior ที่คุณอยากแชร์ข้าม class ที่ไม่เกี่ยวกัน hierarchy แบบเส้นตรงแสดงออกไม่ได้ — คุณต้องเลือก parent ตัวเดียวแล้ว duplicate ที่เหลือ
mixin แก้ปัญหานี้ด้วยการทำให้ behavior เป็น function ที่เพิ่ม feature ให้ base class ใดก็ได้ ดังนั้น class หนึ่งจึง compose ได้หลายอัน
mixin คือ class factory
หัวข้อที่มีชื่อว่า “mixin คือ class factory”mixin คือ function ที่รับ base class (constructor) แล้ว return class ใหม่ที่ extends base นั้นพร้อม behavior เพิ่ม
type Constructor = new (...args: any[]) => {};
function Timestamped<TBase extends Constructor>(Base: TBase) { return class extends Base { createdAt = new Date(); };}
function Activatable<TBase extends Constructor>(Base: TBase) { return class extends Base { isActive = false; activate() { this.isActive = true; } };}
class User { constructor(public name: string) {}}
// Compose several behaviors onto User:const SmartUser = Activatable(Timestamped(User));
const u = new SmartUser("Ada");u.createdAt; // from Timestampedu.activate(); // from Activatableu.name; // from Userconstraint แบบ generic TBase extends Constructor คือสิ่งที่ทำให้ mixin type-safe: constraint นี้บอกว่า “class ไหนก็ตามที่คุณส่งเข้ามา ฉัน return class ที่มี member ของ class นั้นทั้งหมดบวกตัวใหม่” TypeScript track shape ที่สะสมผ่านแต่ละชั้นการ wrap
flowchart LR base["class User"] --> m1["Timestamped(...) + createdAt"] m1 --> m2["Activatable(...) + activate()"] m2 --> result["SmartUser has all three"]
constrained mixin
หัวข้อที่มีชื่อว่า “constrained mixin”บางที mixin ต้องการให้ base class มี member บางอย่างอยู่แล้ว constrain base ให้เป็น constructor ที่ produce shape ที่ต้องการ:
type Positioned = new (...args: any[]) => { x: number; y: number };
function Movable<TBase extends Positioned>(Base: TBase) { return class extends Base { move(dx: number, dy: number) { this.x += dx; // ✅ allowed — the constraint guarantees x exists this.y += dy; } };}ตอนนี้ Movable apply ได้แค่กับ base ที่ instance มี x และ y และภายใน mixin นั้น member เหล่านี้ถือว่ามีอยู่แน่นอน
composition over inheritance
หัวข้อที่มีชื่อว่า “composition over inheritance”mixin เป็นรูปแบบหนึ่งของหลักการที่กว้างกว่า: เลือกประกอบ behavior จากชิ้นเล็ก ๆ ที่อิสระต่อกัน แทนการ derive ผ่าน hierarchy ที่แข็งทื่อ บ่อยครั้งคุณไม่ต้องใช้ mixin ด้วยซ้ำ — plain object composition (ถือ collaborator เป็น field แล้ว delegate งานให้) ง่ายกว่าและเลี่ยงกลไก class-factory ไปเลย ใช้ mixin เมื่อคุณต้องการให้ผลลัพธ์ที่ compose แล้ว เป็น class ที่มีทุก behavior merge อยู่บน instance ของตัวเองจริง ๆ