SOLID tamoyillari: amaliy misollar bilan tushuntirish
Amaliyot4 daqInnovialabs tahririyati
Qisqacha
SOLID — kodni o‘zgartirishni oson qiladigan beshta qoida: klassni o‘zgartirish uchun bitta sabab, tahrirsiz kengaytirish, xavfsiz almashtirish, kichik interfeyslar va aniq klasslarga emas, abstraksiyalarga bog‘liqlik.
SOLID amalda nima beradi
SOLID — obyektga yo‘naltirilgan kodni loyihalashning beshta tamoyili. Umumiy maqsad oddiy: bir joydagi o‘zgarish boshqa joylarni buzmasligi yoki u yerda tahrir talab qilmasligi kerak. Quyida har bir tamoyil uchun TypeScript’da «oldin» va «keyin» misoli hamda aniq foydasi berilgan.
S: yagona mas’uliyat tamoyili
Klassni o‘zgartirish uchun bitta sabab bo‘lishi kerak.
// Oldin: hisobot hisobi, formatlash va pochta bitta klassda
class Report {
build(data: Order[]) { /* jami summalarni hisoblash */ }
toPdf() { /* PDF yaratish */ }
sendByEmail(to: string) { /* SMTP */ }
}
// Keyin: har bir klass o‘z sababi bilan o‘zgaradi
class ReportBuilder { build(data: Order[]): ReportData { /* ... */ } }
class PdfRenderer { render(r: ReportData): Buffer { /* ... */ } }
class Mailer { send(to: string, file: Buffer) { /* ... */ } }
Foydasi: PDF maketini o‘zgartirish endi jami summalar hisobini buzmaydi, har bir qismni alohida test qilish mumkin.
O: ochiqlik/yopiqlik tamoyili
Kod kengaytirish uchun ochiq, o‘zgartirish uchun yopiq: yangi xatti-harakat ishlayotgan kodni tahrirlash bilan emas, yangi kod bilan qo‘shiladi.
// Oldin: har bir yangi yetkazish usuli — shu funksiyani tahrirlash
function deliveryCost(type: string, weight: number) {
if (type === "courier") return 20 + weight * 2;
if (type === "pickup") return 0;
throw new Error("unknown type");
}
// Keyin: yangi yetkazish turi — yangi klass
interface Delivery { cost(weight: number): number }
class Courier implements Delivery { cost(w: number) { return 20 + w * 2; } }
class Pickup implements Delivery { cost() { return 0; } }
Foydasi: yangi yetkazish turi test qilingan kodga tegmaydi, shuning uchun regressiyalar kamroq.
L: Liskov almashtirish tamoyili
Quyi klass ota klass kutilgan har qanday joyda kutilmagan holatlarsiz ishlashi kerak.
// Oldin: ReadOnlyFile File kontraktini buzadi
class File { write(text: string) { /* ... */ } }
class ReadOnlyFile extends File {
write() { throw new Error("read-only"); }
}
// Keyin: imkoniyatlar ajratilgan, va’dalar buzilmaydi
interface Readable { read(): string }
interface Writable { write(text: string): void }
class ReadOnlyFile implements Readable { read() { return "..."; } }
Foydasi: ota turini qabul qiladigan kodga «maxsus» quyi klasslar uchun instanceof tekshiruvlari va try/catch kerak bo‘lmaydi.
I: interfeyslarni ajratish tamoyili
Mijozlar foydalanmaydigan metodlarga bog‘liq bo‘lmasligi kerak.
// Oldin: bitta «semiz» interfeys
interface Worker { code(): void; test(): void; manageBudget(): void }
// Keyin: kichik, aniq interfeyslar
interface Coder { code(): void }
interface Tester { test(): void }
interface BudgetOwner { manageBudget(): void }
Foydasi: klasslar faqat o‘zlariga kerakli narsani amalga oshiradi, bitta interfeysning o‘zgarishi kamroq faylga ta’sir qiladi.
D: bog‘liqliklarni teskari aylantirish tamoyili
Biznes mantiq aniq implementatsiyalarga emas, abstraksiyalarga bog‘liq bo‘lishi kerak.
// Oldin: servis bitta bazaga qattiq bog‘langan
class OrderService {
private db = new PostgresClient();
save(o: Order) { this.db.insert("orders", o); }
}
// Keyin: bog‘liqlik interfeys orqali uzatiladi
interface OrderRepository { save(o: Order): Promise<void> }
class OrderService {
constructor(private repo: OrderRepository) {}
place(o: Order) { return this.repo.save(o); }
}
Foydasi: testlarda xotiradagi repozitoriy uzatiladi, omborni almashtirish esa biznes mantiqqa tegmaydi.
Qisqa qo‘llanma
| Tamoyil | Ogohlantiruvchi belgi | Odatiy yechim |
|---|---|---|
| SRP | Klass bir-biriga bog‘liq bo‘lmagan bir nechta sabab bilan o‘zgaradi | Mas’uliyat bo‘yicha ajratish |
| OCP | Tur bo‘yicha o‘sib borayotgan if/switch | Polimorfizm yoki strategiyalar xaritasi |
| LSP | Quyi klass «qo‘llab-quvvatlanmaydi» xatosini tashlaydi | Ierarxiyani qayta ko‘rib chiqish |
| ISP | Bo‘sh yoki zaglushka metodlar | Interfeysni ajratish |
| DIP | Biznes mantiq ichida infratuzilmani new qilish | Interfeys orqali uzatish |
Ko‘p uchraydigan xatolar
- SOLID’ni juda erta qo‘llash: bitta implementatsiyasi bor va o‘zgarish rejasi yo‘q funksiya uchun uchta interfeys faqat shovqin qo‘shadi.
- Uni qonun deb hisoblash: bu tez-tez o‘zgaradigan kod uchun yo‘l-yo‘riq, har bir fayl uchun nazorat ro‘yxati emas.
- Juda mayda bo‘lish: bir qatorli o‘nlab klasslarni o‘qish bitta ulkan klassni o‘qishdek qiyin.
Amaliy qoida: SOLID tomon refaktoringni o‘zgarish og‘riq keltirganda qiling — bir joyni ikkinchi-uchinchi marta tahrirlaganda.