Mixin pattern

~15 daq · JavaScript · OOP

Muammodan boshlaylik

Dasturda ikki xil turdagi ob'ekt bor: Foydalanuvchi va Mahsulot. Ular bir-biriga aloqasi yo'q.

Lekin ikkalasida ham bir xil narsa kerak: harakatlarni jurnalga yozish. Foydalanuvchi tizimga kirdi — yozilsin. Mahsulot ko'rildi — yozilsin.

extends bilan yechishga urindingiz:

class Foydalanuvchi extends Jurnal { ... }
class Mahsulot  extends Jurnal { ... }

Ishlaydi. Lekin keyinchalik Foydalanuvchi ga yana bir narsa kerak bo'ldi — Serializatsiya. extends bilan ikkita ota class bo'lmaydi:

class Foydalanuvchi extends Jurnal, Serializatsiya { } // ❌ — JavaScript da bunday bo'lmaydi

JavaScript da bitta classdan meros olish mumkin. Bu — yagona meros (single inheritance) cheklovi.

Mixin — bu cheklovni aylanib o'tishning yo'li.


Mixin nima?

Mixin — classga meros olmay turib, tayyor metodlar "to'plamini" qo'shish usuli.

Hayotiy o'xshatma

Ko'ylak va shim — ikki xil kiyim. Lekin ikkalasiga ham bir xil narsa qo'shish mumkin: "issiqlatuvchi" yostiq. Issiqlatuvchi — na ko'ylak, na shim. U faqat "qo'shimcha imkoniyat". Xohlagan kiyimga tikib qo'yiladi.

Mixin — o'sha "qo'shimcha qism". U classning o'zi emas — faqat metodlar to'plami. Istalgan classga qo'shib beriladi, meros olmay.


Eng oddiy mixin

Mixin — metodlar yozilgan oddiy ob'ekt. Uni classning prototype iga qo'shish — barcha ob'ektlar o'sha metodlarga ega bo'ladi.

// Mixin — oddiy ob'ekt, metodlar to'plami
const JurnalMixin = {
  log(xabar) {
    const vaqt = new Date().toLocaleTimeString();
    console.log(`[${vaqt}] ${this.constructor.name}: ${xabar}`);
  }
};

Bu metodda this bor — lekin this hali noaniq. U qaysi classga qo'shilsa, o'sha classning ob'ektini ko'rsatadi. Mixin yozilayotganda this kim bo'lishi muhim emas — qo'shilganda aniqlanadi.

class Foydalanuvchi {
  constructor(ism) {
    this.ism = ism;
  }
}

class Mahsulot {
  constructor(nomi) {
    this.nomi = nomi;
  }
}

// Ikkala classga bir xil mixin qo'shildi
Object.assign(Foydalanuvchi.prototype, JurnalMixin);
Object.assign(Mahsulot.prototype,      JurnalMixin);

const jasur   = new Foydalanuvchi("Jasur");
const noutbuk = new Mahsulot("Noutbuk");

jasur.log("Tizimga kirdi");
// "[10:32:15] Foydalanuvchi: Tizimga kirdi"

noutbuk.log("Ko'rildi");
// "[10:32:17] Mahsulot: Ko'rildi"

Nima uchun Object.assign(prototype, mixin)?

prototype ga qo'shilganda — barcha keyingi ob'ektlar ham metodga ega bo'ladi. Agar to'g'ridan Foydalanuvchi.log = ... qilinsa — bu static metod bo'lib qolardi, ob'ektlarni ko'ra olmasdi.


Bir nechta mixin

Bitta classga bir nechta mixin qo'shish mumkin:

const JurnalMixin = {
  log(xabar) {
    console.log(`[LOG] ${this.constructor.name}: ${xabar}`);
  }
};

const ValidationMixin = {
  tekshir(maydon, qiymat) {
    if (!qiymat || qiymat.toString().trim() === "") {
      throw new Error(`${maydon} bo'sh bo'lmasligi kerak`);
    }
    return true;
  }
};

class Foydalanuvchi {
  constructor(ism, email) {
    this.tekshir("ism",   ism);   // ValidationMixin dan
    this.tekshir("email", email); // ValidationMixin dan
    this.ism   = ism;
    this.email = email;
    this.log("Yaratildi"); // JurnalMixin dan
  }
}

// Ikkala mixin bir vaqtda qo'shildi
Object.assign(Foydalanuvchi.prototype, JurnalMixin, ValidationMixin);

const jasur = new Foydalanuvchi("Jasur", "jasur@mail.com");
// "[LOG] Foydalanuvchi: Yaratildi"

new Foydalanuvchi("", "jasur@mail.com");
// ❌ Error: ism bo'sh bo'lmasligi kerak

Mixin va extends — qachon qaysi?

Bu savol tez-tez chiqadi. Oddiy qoida:

extends — "bu nima?" savoliga javob bersa. Admin — Foydalanuvchi. Mushuk — Hayvon. Bir narsa boshqasining turi.

Mixin — "bu nima qila oladi?" savoliga javob bersa. Foydalanuvchi jurnal yoza oladi. Mahsulot ham jurnal yoza oladi. Tur emas — qobiliyat.

Admin extends Foydalanuvchi  ✅ — Admin IS A Foydalanuvchi
Foydalanuvchi + JurnalMixin  ✅ — Foydalanuvchi CAN log

Tez-tez uchraydigan xatolar

Mixin ichida arrow function ishlatish:

Mixin metodlarida this muhim — u qaysi ob'ektga qo'shilganini ko'rsatishi kerak. Arrow function esa this ni tashqaridan oladi — mixin yozilgan joydan. Bu noto'g'ri ishlaydi.

// ❌ — arrow function, this noto'g'ri
const Mixin = {
  log: () => {
    console.log(this.ism); // undefined — this = global yoki undefined
  }
};

// ✅ — oddiy funksiya, this = chaqiruvchi ob'ekt
const Mixin = {
  log() {
    console.log(this.ism); // ✅
  }
};

Mixin classning mavjud metodini yozib ketishi:

class Foydalanuvchi {
  info() { return `Foydalanuvchi: ${this.ism}`; }
}

const Mixin = {
  info() { return "Mixin info"; } // ← Foydalanuvchi.info ni yozib ketadi!
};

Object.assign(Foydalanuvchi.prototype, Mixin);

new Foydalanuvchi("Jasur").info(); // "Mixin info" ← kutilmagan natija

Mixin qo'shishdan oldin method nomlarining to'qnashmasligi tekshirilishi kerak.


Xulosa

Mixin — classga meros olmay turib metodlar qo'shish usuli. Object.assign(Class.prototype, mixin) orqali.

JavaScript da bitta classdan meros olish mumkin — mixin bu cheklovni yechadi. Bir nechta "qobiliyat to'plami"ni istalgan classga qo'shish mumkin.

extends — "bu nima" (IS-A munosabat). Mixin — "bu nima qila oladi" (CAN-DO qobiliyat).


Keyingi dars

34-dars: Sinxron va Asinxron — farqi va nima uchun kerak