पिछले भाग: क्लीन कोड: टाइपस्क्रिप्ट में कार्य और विधियाँ [भाग 1] स्वच्छ कोड: टाइपस्क्रिप्ट में नामकरण और कोड संरचना [भाग 2] क्लीन कोड: टाइपस्क्रिप्ट में कक्षाएं और ऑब्जेक्ट [भाग 3] हम अपने कोड को स्वच्छ, लचीला और रखरखाव योग्य बनाने के तरीकों पर विचार करना जारी रख रहे हैं। और अब, आइए स्वच्छ वास्तुशिल्प समाधानों की जांच शुरू करें। SOLID सिद्धांत रॉबर्ट सी. मार्टिन द्वारा पेश किए गए थे और इन्हें व्यापक रूप से ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग और सॉफ़्टवेयर डिज़ाइन में सर्वोत्तम प्रथाओं के रूप में माना जाता है। इस लेख में, हम व्यावहारिक उदाहरणों पर तीन पहले ठोस सिद्धांतों पर एक नज़र डालेंगे: एकल जिम्मेदारी सिद्धांत, खुला/बंद सिद्धांत, और लिस्कोव प्रतिस्थापन सिद्धांत। विषयसूची: एकल उत्तरदायित्व सिद्धांत खुला/बंद सिद्धांत लिस्कोव प्रतिस्थापन सिद्धांत 1. एकल उत्तरदायित्व सिद्धांत (एसआरपी) एक वर्ग के पास परिवर्तन का केवल एक ही कारण होना चाहिए दूसरे शब्दों में, एक वर्ग के पास एक ही जिम्मेदारी या कार्य होना चाहिए। यह महत्वपूर्ण है क्योंकि एकल जिम्मेदारी वाले वर्ग को समझना, संशोधित करना और बनाए रखना आसान होता है। कोड के एक क्षेत्र में परिवर्तन से पूरे सिस्टम पर असर नहीं पड़ेगा, जिससे बग आने का जोखिम कम हो जाएगा। आइए एक व्यावहारिक उदाहरण और एकल जिम्मेदारी सिद्धांत का पालन करने के तरीकों पर एक नज़र डालें: // Bad class UserSettingsService { constructor(user: IUser) { this.user = user; } changeSettings(settings: IUserSettings): void { if (this.isUserValidated()) { // ... } } getUserInfo(): Promise<IUserSettings> { // ... } async isUserValidated(): Promise<boolean> { const userInfo = await this.getUserInfo(); // ... } } इस उदाहरण में, हमारा वर्ग विभिन्न दिशाओं में कार्य करता है: यह संदर्भ स्थापित करता है, इसे बदलता है, और इसे मान्य करता है। एसआरपी का पालन करने के लिए, हमें इन विभिन्न जिम्मेदारियों को विभाजित करने की आवश्यकता है। // Better class UserAuth { constructor(user: IUser) { this.user = user; } getUserInfo(): Promise<IUserSettings> { // ... } async isUserValidated(): boolean { const userInfo = await this.getUserInfo(); // ... } } class UserSettings { constructor(user: IUser) { this.user = user; this.auth = new UserAuth(user); } changeSettings(settings: IUserSettings): void { if (this.auth.isUserValidated()) { // ... } } } यह महत्वपूर्ण क्यों है? एसआरपी का लक्ष्य अंतर-निर्भरता से उत्पन्न होने वाली चुनौतियों को कम करते हुए, कोड मॉड्यूलैरिटी को बढ़ाना है। कोड को फ़ंक्शंस, अलग-अलग कक्षाओं में व्यवस्थित करने और मॉड्यूलरिटी को बढ़ावा देने से, यह अधिक पुन: प्रयोज्य हो जाता है, जिससे समय की बचत होती है जो अन्यथा मौजूदा कार्यक्षमता को फिर से कोड करने में खर्च हो सकता है। 2. खुला/बंद सिद्धांत (ओसीपी) सॉफ़्टवेयर इकाइयाँ (कक्षाएँ, मॉड्यूल, फ़ंक्शंस) विस्तार के लिए खुली होनी चाहिए लेकिन संशोधन के लिए बंद होनी चाहिए आपको मौजूदा कोड को बदले बिना नई कार्यक्षमता जोड़ने में सक्षम होना चाहिए। यह महत्वपूर्ण है क्योंकि इस सिद्धांत का पालन करके, आप मौजूदा कोड की स्थिरता को जोखिम में डाले बिना अपने सिस्टम में नई सुविधाएँ या घटक पेश कर सकते हैं। यह कोड पुन: प्रयोज्य को बढ़ावा देता है और कार्यक्षमता का विस्तार करते समय व्यापक परिवर्तनों की आवश्यकता को कम करता है। // Bad class Product { id: number; name: string[]; price: number; protected constructor(id: number, name: string[], price: number) { this.id = id; this.name = name; this.price = price; } } class Ananas extends Product { constructor(id: number, name: string[], price: number) { super(id, name, price); } } class Banana extends Product { constructor(id: number, name: string[], price: string) { super(id, name, price); } } class HttpRequestCost { constructor(product: Product) { this.product = product; } getDeliveryCost(): number { if (product instanceOf Ananas) { return requestAnanas(url).then(...); } if (product instanceOf Banana) { return requestBanana(url).then(...); } } } function requestAnanas(url: string): Promise<ICost> { // logic for ananas } function requestBanana(url: string): Promise<ICost> { // logic for bananas } इस उदाहरण में, समस्या वर्ग में है, जिसमें विधि में विभिन्न प्रकार के उत्पादों की गणना के लिए शर्तें शामिल हैं, और हम प्रत्येक प्रकार के उत्पाद के लिए अलग-अलग तरीकों का उपयोग करते हैं। इसलिए, यदि हमें एक नए प्रकार का उत्पाद जोड़ने की आवश्यकता है, तो हमें वर्ग को संशोधित करना चाहिए, और यह सुरक्षित नहीं है; हमें अप्रत्याशित परिणाम मिल सकते हैं. HttpRequestCost getDeliveryCost HttpRequestCost इससे बचने के लिए, हमें बिना किसी अहसास के वर्ग में एक अमूर्त विधि बनाना चाहिए। विशेष बोध को ये वर्ग विरासत में मिले होंगे: अनानास और केला। उन्हें अपने लिए अनुरोध का एहसास होगा। Product request वर्ग इंटरफ़ेस के बाद पैरामीटर लेगा, और जब हम में विशेष निर्भरता पास करते हैं, तो यह पहले से ही अपने लिए विधि का एहसास कर लेगा। HttpRequestCost Product product HttpRequestCost request // Better abstract class Product { id: number; name: string[]; price: string; constructor(id: number, name: string[], price: string) { this.id = id; this.name = name; this.price = price; } abstract request(url: string): void; } class Ananas extends Product { constructor(id: number, name: string[], price: string) { super(id, name, price); } request(url: string): void { // logic for ananas } } class Banana extends Product { constructor(id: number, name: string[], price: string) { super(id, name, price); } request(url: string): void { // logic for bananas } } class HttpRequestCost { constructor(product: Product) { this.product = product; } request(): Promise<void> { return this.product.request(url).then(...); } } यह महत्वपूर्ण क्यों है? इस सिद्धांत का पालन करके, आप कोड युग्मन को कम करेंगे और सिस्टम को अप्रत्याशित व्यवहार से बचाएंगे। 3. लिस्कोव प्रतिस्थापन सिद्धांत (एलएसपी) सुपरक्लास की वस्तुओं को एप्लिकेशन को तोड़े बिना उसके उपवर्गों की वस्तुओं से बदला जाना चाहिए। इस सिद्धांत को समझने के लिए, आइए इस उदाहरण पर एक नज़र डालें: // Bad class Worker { work(): void {/../} access(): void { console.log('Have an access to closed perimeter'); } } class Programmer extends Worker { createDatabase(): void {/../} } class Seller extends Worker { sale(): void {/../} } class Designer extends Worker { access(): void { throwError('No access'); } } इस उदाहरण में, हमारे पास वर्ग के साथ एक समस्या है। , और सभी श्रमिक हैं, और उन्हें मूल वर्ग से विरासत में मिला है। लेकिन साथ ही, डिजाइनरों के पास बंद परिधि तक पहुंच नहीं है क्योंकि वे ठेकेदार हैं, कर्मचारी नहीं। और हमने विधि को ओवरराइड कर दिया है और लिस्कोव प्रतिस्थापन सिद्धांत को तोड़ दिया है। Contractor Designer Programmer Seller Worker access यह सिद्धांत हमें बताता है कि यदि हम सुपरक्लास उसके उपवर्ग, उदाहरण के लिए वर्ग, से प्रतिस्थापित करते हैं, तो कार्यक्षमता नहीं टूटनी चाहिए। लेकिन अगर हम ऐसा करते हैं, तो वर्ग की कार्यक्षमता टूट जाएगी - विधि में वर्ग से अप्रत्याशित प्राप्ति होगी। Worker Designer Programmer access Designer लिस्कोव प्रतिस्थापन सिद्धांत का पालन करते हुए, हमें उपवर्गों में अहसासों को दोबारा नहीं लिखना चाहिए बल्कि अमूर्तता की नई परतें बनाने की जरूरत है जहां हम प्रत्येक प्रकार के अमूर्तन के लिए विशेष अहसासों को परिभाषित करते हैं। आइए इसे ठीक करें: // Better class Worker { work(): void {/../} } class Employee extends Worker { access(): void { console.log('Have an access to closed perimeter'); } } class Contractor extends Worker { addNewContract(): void {/../} } class Programmer extends Employee { createDatabase(): void {/../} } class Saler extends Employee { sale(): void {/../} } class Designer extends Contractor { makeDesign(): void {/../} } हमने अमूर्त और की नई परतें बनाईं और पद्धति को वर्ग में स्थानांतरित कर दिया और विशिष्ट अहसास को परिभाषित किया। यदि हम क्लास को सबक्लास से बदल देते हैं, तो कार्यक्षमता नहीं टूटेगी। Employee Contractor access Employee Worker Contractor Worker यह महत्वपूर्ण क्यों है? यदि आप एलएसपी का पालन करते हैं, तो आप किसी भी उपवर्ग उदाहरण को प्रतिस्थापित कर सकते हैं जहां बेस क्लास उदाहरण अपेक्षित है, और प्रोग्राम को अभी भी इच्छित के अनुसार काम करना चाहिए। यह कोड के पुन: उपयोग, मॉड्यूलरिटी को बढ़ावा देता है और आपके कोड को परिवर्तनों के प्रति अधिक लचीला बनाता है। अगले लेख में, हम इंटरफ़ेस पृथक्करण और निर्भरता व्युत्क्रम SOLID सिद्धांतों पर एक नज़र डालेंगे।