मेटाडेटा प्रोग्रामिंग, आस्पेक्ट-ओरिएंटेड प्रोग्रामिंग, आणि डिक्लरेटिव्ह पॅटर्न्ससह कोड सुधारण्यासाठी टाइपस्क्रिप्ट डेकोरेटर्सची शक्ती जाणून घ्या. जागतिक डेव्हलपर्ससाठी एक सर्वसमावेशक मार्गदर्शक.
टाइपस्क्रिप्ट डेकोरेटर्स: मजबूत ॲप्लिकेशन्ससाठी मेटाडेटा प्रोग्रामिंग पॅटर्न्समध्ये प्राविण्य मिळवणे
आधुनिक सॉफ्टवेअर डेव्हलपमेंटच्या विशाल क्षेत्रात, स्वच्छ, स्केलेबल आणि व्यवस्थापित करण्यायोग्य कोडबेस राखणे अत्यंत महत्त्वाचे आहे. टाइपस्क्रिप्ट, त्याच्या शक्तिशाली टाइप सिस्टीम आणि प्रगत वैशिष्ट्यांसह, डेव्हलपर्सना हे साध्य करण्यासाठी साधने प्रदान करते. त्याच्या सर्वात मनोरंजक आणि परिवर्तनकारी वैशिष्ट्यांपैकी एक म्हणजे डेकोरेटर्स. लिहिण्याच्या वेळी हे वैशिष्ट्य अजूनही प्रायोगिक (ECMAScript साठी स्टेज 3 प्रस्ताव) असले तरी, डेकोरेटर्स अँँग्युलर आणि टाइपओआरएम (TypeORM) सारख्या फ्रेमवर्कमध्ये मोठ्या प्रमाणावर वापरले जातात, ज्यामुळे डिझाइन पॅटर्न्स, मेटाडेटा प्रोग्रामिंग आणि आस्पेक्ट-ओरिएंटेड प्रोग्रामिंग (AOP) कडे पाहण्याचा आपला दृष्टिकोन मुळापासून बदलतो.
हे सर्वसमावेशक मार्गदर्शक टाइपस्क्रिप्ट डेकोरेटर्सच्या গভীरतेत जाईल, त्यांचे कार्य, विविध प्रकार, व्यावहारिक उपयोग आणि सर्वोत्तम पद्धतींचा शोध घेईल. तुम्ही मोठ्या प्रमाणातील एंटरप्राइझ ॲप्लिकेशन्स, मायक्रो सर्व्हिसेस किंवा क्लायंट-साइड वेब इंटरफेस तयार करत असाल, तरीही डेकोरेटर्स समजून घेतल्याने तुम्हाला अधिक डिक्लरेटिव्ह, व्यवस्थापित करण्यायोग्य आणि शक्तिशाली टाइपस्क्रिप्ट कोड लिहिण्यास सक्षम बनवेल.
मूळ संकल्पना समजून घेणे: डेकोरेटर म्हणजे काय?
मूळतः, डेकोरेटर हे एक विशेष प्रकारचे डिक्लरेशन आहे जे क्लास डिक्लरेशन, मेथड, ॲक्सेसर, प्रॉपर्टी किंवा पॅरामीटरला जोडले जाऊ शकते. डेकोरेटर्स हे फंक्शन्स असतात जे त्यांच्या लक्ष्यासाठी एक नवीन मूल्य परत करतात (किंवा विद्यमान मूल्यात बदल करतात). त्यांचा मुख्य उद्देश म्हणजे त्यांच्याशी संलग्न डिक्लरेशनमध्ये मेटाडेटा जोडणे किंवा त्याचे वर्तन बदलणे, थेट मूळ कोड स्ट्रक्चरमध्ये बदल न करता. कोड वाढवण्याचा हा बाह्य, डिक्लरेटिव्ह मार्ग अत्यंत शक्तिशाली आहे.
डेकोरेटर्सना ॲनोटेशन्स किंवा लेबल्स म्हणून विचार करा जे तुम्ही तुमच्या कोडच्या भागांवर लावता. हे लेबल्स नंतर तुमच्या ॲप्लिकेशनच्या इतर भागांद्वारे किंवा फ्रेमवर्कद्वारे वाचले किंवा त्यावर कार्यवाही केली जाऊ शकते, अनेकदा रनटाइममध्ये, अतिरिक्त कार्यक्षमता किंवा कॉन्फिगरेशन प्रदान करण्यासाठी.
डेकोरेटरचे सिंटॅक्स
डेकोरेटर्सच्या आधी @
चिन्ह असते, त्यानंतर डेकोरेटर फंक्शनचे नाव येते. ते ज्या डिक्लरेशनला डेकोरेट करत आहेत त्याच्या लगेच आधी ठेवले जातात.
@MyDecorator
class MyClass {
@AnotherDecorator
myMethod() {
// ...
}
}
टाइपस्क्रिप्टमध्ये डेकोरेटर्स सक्षम करणे
तुम्ही डेकोरेटर्स वापरण्यापूर्वी, तुम्हाला तुमच्या tsconfig.json
फाइलमध्ये experimentalDecorators
कंपाइलर पर्याय सक्षम करणे आवश्यक आहे. याव्यतिरिक्त, प्रगत मेटाडेटा रिफ्लेक्शन क्षमतेसाठी (जे फ्रेमवर्कद्वारे वारंवार वापरले जाते), तुम्हाला emitDecoratorMetadata
आणि reflect-metadata
पॉलीफिलची देखील आवश्यकता असेल.
// tsconfig.json
{
"compilerOptions": {
"target": "ES2017",
"module": "commonjs",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
तुम्हाला reflect-metadata
इन्स्टॉल करण्याची देखील आवश्यकता आहे:
npm install reflect-metadata --save
# किंवा
yarn add reflect-metadata
आणि ते तुमच्या ॲप्लिकेशनच्या एंट्री पॉइंटच्या सर्वात वरच्या बाजूला (उदा. main.ts
किंवा app.ts
) इम्पोर्ट करा:
import "reflect-metadata";
// तुमचा ॲप्लिकेशन कोड पुढे येईल
डेकोरेटर फॅक्टरीज: तुमच्या बोटांच्या टोकावर सानुकूलन
एक साधा डेकोरेटर एक फंक्शन असला तरी, अनेकदा तुम्हाला डेकोरेटरच्या वर्तनाला कॉन्फिगर करण्यासाठी त्याला आर्गुमेंट्स पास करण्याची आवश्यकता असेल. हे डेकोरेटर फॅक्टरी वापरून साध्य केले जाते. डेकोरेटर फॅक्टरी हे एक फंक्शन आहे जे प्रत्यक्ष डेकोरेटर फंक्शन परत करते. जेव्हा तुम्ही डेकोरेटर फॅक्टरी लागू करता, तेव्हा तुम्ही त्याला त्याच्या आर्गुमेंट्ससह कॉल करता आणि ते नंतर डेकोरेटर फंक्शन परत करते जे टाइपस्क्रिप्ट तुमच्या कोडवर लागू करते.
एक साधी डेकोरेटर फॅक्टरी उदाहरण तयार करणे
चला एका Logger
डेकोरेटरसाठी एक फॅक्टरी तयार करूया जी वेगवेगळ्या प्रीफिक्ससह संदेश लॉग करू शकते.
function Logger(prefix: string) {
return function (target: Function) {
console.log(`[${prefix}] क्लास ${target.name} परिभाषित झाला आहे.`);
};
}
@Logger("APP_INIT")
class ApplicationBootstrap {
constructor() {
console.log("ॲप्लिकेशन सुरू होत आहे...");
}
}
const app = new ApplicationBootstrap();
// आउटपुट:
// [APP_INIT] क्लास ApplicationBootstrap परिभाषित झाला आहे.
// ॲप्लिकेशन सुरू होत आहे...
या उदाहरणात, Logger("APP_INIT")
हा डेकोरेटर फॅक्टरी कॉल आहे. ते प्रत्यक्ष डेकोरेटर फंक्शन परत करते जे target: Function
(क्लास कन्स्ट्रक्टर) त्याच्या आर्गुमेंट म्हणून घेते. हे डेकोरेटरच्या वर्तनाच्या डायनॅमिक कॉन्फिगरेशनला परवानगी देते.
टाइपस्क्रिप्टमधील डेकोरेटर्सचे प्रकार
टाइपस्क्रिप्ट पाच वेगवेगळ्या प्रकारच्या डेकोरेटर्सना समर्थन देते, प्रत्येक एका विशिष्ट प्रकारच्या डिक्लरेशनला लागू होतो. डेकोरेटर फंक्शनची सिग्नेचर ती ज्या संदर्भात लागू केली आहे त्यानुसार बदलते.
१. क्लास डेकोरेटर्स
क्लास डेकोरेटर्स क्लास डिक्लरेशन्सना लागू केले जातात. डेकोरेटर फंक्शनला क्लासचा कन्स्ट्रक्टर त्याचा एकमेव आर्गुमेंट म्हणून मिळतो. क्लास डेकोरेटर क्लासच्या व्याख्येचे निरीक्षण करू शकतो, त्यात बदल करू शकतो किंवा ती बदलूही शकतो.
सिग्नेचर:
function ClassDecorator(target: Function) { ... }
परत मिळणारे मूल्य:
जर क्लास डेकोरेटरने मूल्य परत केले, तर ते क्लास डिक्लरेशनला प्रदान केलेल्या कन्स्ट्रक्टर फंक्शनने बदलेल. हे एक शक्तिशाली वैशिष्ट्य आहे, जे अनेकदा मिक्सिन्स किंवा क्लास ऑगमेंटेशनसाठी वापरले जाते. जर कोणतेही मूल्य परत केले नाही, तर मूळ क्लास वापरला जातो.
उपयोगाची प्रकरणे:
- डिपेंडेंसी इंजेक्शन कंटेनरमध्ये क्लासेसची नोंदणी करणे.
- क्लासमध्ये मिक्सिन्स किंवा अतिरिक्त कार्यक्षमता लागू करणे.
- फ्रेमवर्क-विशिष्ट कॉन्फिगरेशन्स (उदा., वेब फ्रेमवर्कमधील राउटिंग).
- क्लासेसमध्ये लाइफसायकल हुक्स जोडणे.
क्लास डेकोरेटर उदाहरण: सर्व्हिस इंजेक्ट करणे
एका साध्या डिपेंडेंसी इंजेक्शन परिस्थितीची कल्पना करा जिथे तुम्हाला एका क्लासला "इंजेक्टीबल" म्हणून चिन्हांकित करायचे आहे आणि पर्यायाने त्याला कंटेनरमध्ये एक नाव द्यायचे आहे.
const InjectableServiceRegistry = new Map<string, Function>();
function Injectable(name?: string) {
return function<T extends { new(...args: any[]): {} }>(constructor: T) {
const serviceName = name || constructor.name;
InjectableServiceRegistry.set(serviceName, constructor);
console.log(`नोंदणीकृत सर्व्हिस: ${serviceName}`);
// पर्यायाने, तुम्ही येथे वर्तन वाढवण्यासाठी एक नवीन क्लास परत करू शकता
return class extends constructor {
createdAt = new Date();
// सर्व इंजेक्ट केलेल्या सर्व्हिसेससाठी अतिरिक्त प्रॉपर्टीज किंवा मेथड्स
};
};
}
@Injectable("UserService")
class UserDataService {
getUsers() {
return [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }];
}
}
@Injectable()
class ProductDataService {
getProducts() {
return [{ id: 101, name: "Laptop" }, { id: 102, name: "Mouse" }];
}
}
console.log("--- सर्व्हिसेस नोंदणीकृत ---");
console.log(Array.from(InjectableServiceRegistry.keys()));
const userServiceConstructor = InjectableServiceRegistry.get("UserService");
if (userServiceConstructor) {
const userServiceInstance = new userServiceConstructor();
console.log("युजर्स:", userServiceInstance.getUsers());
// console.log("युजर सर्व्हिस तयार करण्याची वेळ:", userServiceInstance.createdAt); // जर परत केलेला क्लास वापरला असेल तर
}
हे उदाहरण दाखवते की क्लास डेकोरेटर कसा क्लासची नोंदणी करू शकतो आणि त्याच्या कन्स्ट्रक्टरमध्ये बदलही करू शकतो. Injectable
डेकोरेटर क्लासला एका सैद्धांतिक डिपेंडेंसी इंजेक्शन सिस्टीमद्वारे शोधण्यायोग्य बनवतो.
२. मेथड डेकोरेटर्स
मेथड डेकोरेटर्स मेथड डिक्लरेशन्सना लागू केले जातात. त्यांना तीन आर्गुमेंट्स मिळतात: लक्ष्य ऑब्जेक्ट (स्टॅटिक सदस्यांसाठी, कन्स्ट्रक्टर फंक्शन; इन्स्टन्स सदस्यांसाठी, क्लासचा प्रोटोटाइप), मेथडचे नाव आणि मेथडचे प्रॉपर्टी डिस्क्रिप्टर.
सिग्नेचर:
function MethodDecorator(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) { ... }
परत मिळणारे मूल्य:
एक मेथड डेकोरेटर एक नवीन PropertyDescriptor
परत करू शकतो. जर त्याने असे केले, तर हा डिस्क्रिप्टर मेथड परिभाषित करण्यासाठी वापरला जाईल. हे तुम्हाला मूळ मेथडच्या अंमलबजावणीमध्ये बदल किंवा ते बदलण्याची परवानगी देते, ज्यामुळे ते AOP साठी अत्यंत शक्तिशाली बनते.
उपयोगाची प्रकरणे:
- मेथड कॉल्स आणि त्यांचे आर्गुमेंट्स/परिणाम लॉग करणे.
- कार्यक्षमता सुधारण्यासाठी मेथडचे परिणाम कॅश करणे.
- मेथड कार्यान्वित करण्यापूर्वी ऑथोरायझेशन तपासणी लागू करणे.
- मेथड कार्यान्वित होण्याचा वेळ मोजणे.
- मेथड कॉल्सना डिबाउन्स किंवा थ्रॉटल करणे.
मेथड डेकोरेटर उदाहरण: कार्यप्रदर्शन देखरेख
चला एका मेथडचा कार्यान्वयन वेळ लॉग करण्यासाठी MeasurePerformance
डेकोरेटर तयार करूया.
function MeasurePerformance(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
const start = process.hrtime.bigint();
const result = originalMethod.apply(this, args);
const end = process.hrtime.bigint();
const duration = Number(end - start) / 1_000_000;
console.log(`मेथड "${propertyKey}" ${duration.toFixed(2)} ms मध्ये कार्यान्वित झाली`);
return result;
};
return descriptor;
}
class DataProcessor {
@MeasurePerformance
processData(data: number[]): number[] {
// एक जटिल, वेळखाऊ ऑपरेशन सिम्युलेट करा
for (let i = 0; i < 1_000_000; i++) {
Math.sin(i);
}
return data.map(n => n * 2);
}
@MeasurePerformance
fetchRemoteData(id: string): Promise<string> {
return new Promise(resolve => {
setTimeout(() => {
resolve(`ID साठी डेटा: ${id}`);
}, 500);
});
}
}
const processor = new DataProcessor();
processor.processData([1, 2, 3]);
processor.fetchRemoteData("abc").then(result => console.log(result));
MeasurePerformance
डेकोरेटर मूळ मेथडला टाइमिंग लॉजिकसह गुंडाळतो, मेथडमधील बिझनेस लॉजिकमध्ये गोंधळ न घालता कार्यान्वयन कालावधी मुद्रित करतो. हे आस्पेक्ट-ओरिएंटेड प्रोग्रामिंग (AOP) चे एक उत्कृष्ट उदाहरण आहे.
३. ॲक्सेसर डेकोरेटर्स
ॲक्सेसर डेकोरेटर्स ॲक्सेसर (get
आणि set
) डिक्लरेशन्सना लागू केले जातात. मेथड डेकोरेटर्सप्रमाणेच, त्यांना लक्ष्य ऑब्जेक्ट, ॲक्सेसरचे नाव आणि त्याचे प्रॉपर्टी डिस्क्रिप्टर मिळतात.
सिग्नेचर:
function AccessorDecorator(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) { ... }
परत मिळणारे मूल्य:
एक ॲक्सेसर डेकोरेटर एक नवीन PropertyDescriptor
परत करू शकतो, जो ॲक्सेसर परिभाषित करण्यासाठी वापरला जाईल.
उपयोगाची प्रकरणे:
- प्रॉपर्टी सेट करताना प्रमाणीकरण.
- मूल्य सेट करण्यापूर्वी किंवा मिळवल्यानंतर त्याचे रूपांतर करणे.
- प्रॉपर्टीजसाठी प्रवेश परवानग्या नियंत्रित करणे.
ॲक्सेसर डेकोरेटर उदाहरण: गेटर्स कॅश करणे
चला एक डेकोरेटर तयार करूया जो महागड्या गेटर गणनेचा निकाल कॅश करतो.
function CachedGetter(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalGetter = descriptor.get;
const cacheKey = `_cached_${String(propertyKey)}`;
if (originalGetter) {
descriptor.get = function() {
if (this[cacheKey] === undefined) {
console.log(`[कॅशे मिस] ${String(propertyKey)} साठी मूल्य गणना करत आहे`);
this[cacheKey] = originalGetter.apply(this);
} else {
console.log(`[कॅशे हिट] ${String(propertyKey)} साठी कॅश केलेले मूल्य वापरत आहे`);
}
return this[cacheKey];
};
}
return descriptor;
}
class ReportGenerator {
private data: number[];
constructor(data: number[]) {
this.data = data;
}
// एक महागडी गणना सिम्युलेट करते
@CachedGetter
get expensiveSummary(): number {
console.log("महागडी सारांश गणना करत आहे...");
return this.data.reduce((sum, current) => sum + current, 0) / this.data.length;
}
}
const generator = new ReportGenerator([10, 20, 30, 40, 50]);
console.log("पहिला प्रवेश:", generator.expensiveSummary);
console.log("दुसरा प्रवेश:", generator.expensiveSummary);
console.log("तिसरा प्रवेश:", generator.expensiveSummary);
हा डेकोरेटर सुनिश्चित करतो की expensiveSummary
गेटरची गणना फक्त एकदाच चालते, त्यानंतरचे कॉल्स कॅश केलेले मूल्य परत करतात. हा पॅटर्न कार्यक्षमता ऑप्टिमाइझ करण्यासाठी खूप उपयुक्त आहे जिथे प्रॉपर्टी ॲक्सेसमध्ये मोठी गणना किंवा बाह्य कॉल्स समाविष्ट असतात.
४. प्रॉपर्टी डेकोरेटर्स
प्रॉपर्टी डेकोरेटर्स प्रॉपर्टी डिक्लरेशन्सना लागू केले जातात. त्यांना दोन आर्गुमेंट्स मिळतात: लक्ष्य ऑब्जेक्ट (स्टॅटिक सदस्यांसाठी, कन्स्ट्रक्टर फंक्शन; इन्स्टन्स सदस्यांसाठी, क्लासचा प्रोटोटाइप), आणि प्रॉपर्टीचे नाव.
सिग्नेचर:
function PropertyDecorator(target: Object, propertyKey: string | symbol) { ... }
परत मिळणारे मूल्य:
प्रॉपर्टी डेकोरेटर्स कोणतेही मूल्य परत करू शकत नाहीत. त्यांचा प्राथमिक उपयोग प्रॉपर्टीबद्दल मेटाडेटा नोंदणी करणे आहे. ते थेट प्रॉपर्टीचे मूल्य किंवा डेकोरेशनच्या वेळी त्याचे डिस्क्रिप्टर बदलू शकत नाहीत, कारण प्रॉपर्टी डेकोरेटर्स चालतात तेव्हा प्रॉपर्टीसाठी डिस्क्रिप्टर अद्याप पूर्णपणे परिभाषित केलेला नसतो.
उपयोगाची प्रकरणे:
- सिरियलायझेशन/डिसिरियलायझेशनसाठी प्रॉपर्टीजची नोंदणी करणे.
- प्रॉपर्टीजवर प्रमाणीकरण नियम लागू करणे.
- प्रॉपर्टीजसाठी डीफॉल्ट मूल्ये किंवा कॉन्फिगरेशन सेट करणे.
- ORM (ऑब्जेक्ट-रिलेशनल मॅपिंग) कॉलम मॅपिंग (उदा., TypeORM मधील
@Column()
).
प्रॉपर्टी डेकोरेटर उदाहरण: आवश्यक फील्ड प्रमाणीकरण
चला एका प्रॉपर्टीला "आवश्यक" म्हणून चिन्हांकित करण्यासाठी आणि नंतर रनटाइममध्ये त्याचे प्रमाणीकरण करण्यासाठी एक डेकोरेटर तयार करूया.
interface ValidationRule {
property: string | symbol;
validate: (value: any) => boolean;
message: string;
}
const validationRules: Map<Function, ValidationRule[]> = new Map();
function Required(target: Object, propertyKey: string | symbol) {
const rules = validationRules.get(target.constructor) || [];
rules.push({
property: propertyKey,
validate: (value: any) => value !== null && value !== undefined && value !== "",
message: `${String(propertyKey)} आवश्यक आहे.`
});
validationRules.set(target.constructor, rules);
}
function validate(instance: any): string[] {
const classRules = validationRules.get(instance.constructor) || [];
const errors: string[] = [];
for (const rule of classRules) {
if (!rule.validate(instance[rule.property])) {
errors.push(rule.message);
}
}
return errors;
}
class UserProfile {
@Required
firstName: string;
@Required
lastName: string;
age?: number;
constructor(firstName: string, lastName: string, age?: number) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
}
const user1 = new UserProfile("John", "Doe", 30);
console.log("युजर 1 प्रमाणीकरण त्रुटी:", validate(user1)); // []
const user2 = new UserProfile("", "Smith");
console.log("युजर 2 प्रमाणीकरण त्रुटी:", validate(user2)); // ["firstName आवश्यक आहे."]
const user3 = new UserProfile("Alice", "");
console.log("युजर 3 प्रमाणीकरण त्रुटी:", validate(user3)); // ["lastName आवश्यक आहे."]
Required
डेकोरेटर फक्त प्रमाणीकरण नियम एका केंद्रीय validationRules
मॅपमध्ये नोंदवतो. एक वेगळे validate
फंक्शन नंतर या मेटाडेटाचा वापर करून रनटाइममध्ये इन्स्टन्स तपासते. हा पॅटर्न प्रमाणीकरण लॉजिकला डेटाच्या व्याख्येपासून वेगळे करतो, ज्यामुळे ते पुन्हा वापरण्यायोग्य आणि स्वच्छ बनते.
५. पॅरामीटर डेकोरेटर्स
पॅरामीटर डेकोरेटर्स क्लास कन्स्ट्रक्टर किंवा मेथडमधील पॅरामीटर्सना लागू केले जातात. त्यांना तीन आर्गुमेंट्स मिळतात: लक्ष्य ऑब्जेक्ट (स्टॅटिक सदस्यांसाठी, कन्स्ट्रक्टर फंक्शन; इन्स्टन्स सदस्यांसाठी, क्लासचा प्रोटोटाइप), मेथडचे नाव (किंवा कन्स्ट्रक्टर पॅरामीटर्ससाठी undefined
), आणि फंक्शनच्या पॅरामीटर सूचीमधील पॅरामीटरचा क्रमिक निर्देशांक.
सिग्नेचर:
function ParameterDecorator(target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) { ... }
परत मिळणारे मूल्य:
पॅरामीटर डेकोरेटर्स कोणतेही मूल्य परत करू शकत नाहीत. प्रॉपर्टी डेकोरेटर्सप्रमाणेच, त्यांची प्राथमिक भूमिका पॅरामीटरबद्दल मेटाडेटा जोडणे आहे.
उपयोगाची प्रकरणे:
- डिपेंडेंसी इंजेक्शनसाठी पॅरामीटर प्रकारांची नोंदणी करणे (उदा., अँँग्युलरमधील
@Inject()
). - विशिष्ट पॅरामीटर्सवर प्रमाणीकरण किंवा रूपांतर लागू करणे.
- वेब फ्रेमवर्कमधील API विनंती पॅरामीटर्सबद्दल मेटाडेटा काढणे.
पॅरामीटर डेकोरेटर उदाहरण: विनंती डेटा इंजेक्ट करणे
चला सिम्युलेट करूया की एक वेब फ्रेमवर्क मेथड पॅरामीटरमध्ये विशिष्ट डेटा इंजेक्ट करण्यासाठी पॅरामीटर डेकोरेटर्सचा कसा वापर करू शकते, जसे की विनंतीमधून युजर आयडी.
interface ParameterMetadata {
index: number;
key: string | symbol;
resolver: (request: any) => any;
}
const parameterResolvers: Map<Function, Map<string | symbol, ParameterMetadata[]>> = new Map();
function RequestParam(paramName: string) {
return function (target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) {
const targetKey = propertyKey || "constructor";
let methodResolvers = parameterResolvers.get(target.constructor);
if (!methodResolvers) {
methodResolvers = new Map();
parameterResolvers.set(target.constructor, methodResolvers);
}
const paramMetadata = methodResolvers.get(targetKey) || [];
paramMetadata.push({
index: parameterIndex,
key: targetKey,
resolver: (request: any) => request[paramName]
});
methodResolvers.set(targetKey, paramMetadata);
};
}
// निराकरण केलेल्या पॅरामीटर्ससह मेथडला कॉल करण्यासाठी एक काल्पनिक फ्रेमवर्क फंक्शन
function executeWithParams(instance: any, methodName: string, request: any) {
const classResolvers = parameterResolvers.get(instance.constructor);
if (!classResolvers) {
return (instance[methodName] as Function).apply(instance, []);
}
const methodParamMetadata = classResolvers.get(methodName);
if (!methodParamMetadata) {
return (instance[methodName] as Function).apply(instance, []);
}
const args: any[] = Array(methodParamMetadata.length);
for (const meta of methodParamMetadata) {
args[meta.index] = meta.resolver(request);
}
return (instance[methodName] as Function).apply(instance, args);
}
class UserController {
getUser(@RequestParam("id") userId: string, @RequestParam("token") authToken?: string) {
console.log(`युजर आयडीसह आणत आहे: ${userId}, टोकन: ${authToken || "N/A"}`);
return { id: userId, name: "Jane Doe" };
}
deleteUser(@RequestParam("id") userId: string) {
console.log(`युजर आयडीसह हटवत आहे: ${userId}`);
return { status: "deleted", id: userId };
}
}
const userController = new UserController();
// येणाऱ्या विनंतीचे अनुकरण करा
const mockRequest = {
id: "user123",
token: "abc-123",
someOtherProp: "xyz"
};
console.log("\n--- getUser कार्यान्वित करत आहे ---");
executeWithParams(userController, "getUser", mockRequest);
console.log("\n--- deleteUser कार्यान्वित करत आहे ---");
executeWithParams(userController, "deleteUser", { id: "user456" });
हे उदाहरण दाखवते की पॅरामीटर डेकोरेटर्स आवश्यक मेथड पॅरामीटर्सबद्दल माहिती कशी गोळा करू शकतात. एक फ्रेमवर्क नंतर या गोळा केलेल्या मेटाडेटाचा वापर करून मेथड कॉल केल्यावर योग्य मूल्ये स्वयंचलितपणे निराकरण आणि इंजेक्ट करू शकते, ज्यामुळे कंट्रोलर किंवा सर्व्हिस लॉजिक लक्षणीयरीत्या सोपे होते.
डेकोरेटर रचना आणि कार्यान्वयन क्रम
डेकोरेटर्स विविध संयोजनांमध्ये लागू केले जाऊ शकतात आणि त्यांचे कार्यान्वयन क्रम समजून घेणे वर्तनचा अंदाज घेण्यासाठी आणि अनपेक्षित समस्या टाळण्यासाठी महत्त्वाचे आहे.
एकाच लक्ष्यावर अनेक डेकोरेटर्स
जेव्हा एकाच डिक्लरेशनवर (उदा., क्लास, मेथड किंवा प्रॉपर्टी) अनेक डेकोरेटर्स लागू केले जातात, तेव्हा ते एका विशिष्ट क्रमाने कार्यान्वित होतात: त्यांच्या मूल्यांकनासाठी खालून वर, किंवा उजवीकडून डावीकडे. तथापि, त्यांचे परिणाम उलट क्रमाने लागू केले जातात.
@DecoratorA
@DecoratorB
class MyClass {
// ...
}
येथे, DecoratorB
चे मूल्यांकन प्रथम केले जाईल, नंतर DecoratorA
चे. जर ते क्लासमध्ये बदल करत असतील (उदा., नवीन कन्स्ट्रक्टर परत करून), तर DecoratorA
मधील बदल DecoratorB
च्या बदलावर गुंडाळला जाईल किंवा लागू होईल.
उदाहरण: मेथड डेकोरेटर्सची साखळी
दोन मेथड डेकोरेटर्सचा विचार करा: LogCall
आणि Authorization
.
function LogCall(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`[लॉग] ${String(propertyKey)} ला आर्ग्युमेंट्ससह कॉल करत आहे:`, args);
const result = originalMethod.apply(this, args);
console.log(`[लॉग] मेथड ${String(propertyKey)} ने परत केले:`, result);
return result;
};
return descriptor;
}
function Authorization(roles: string[]) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const currentUserRoles = ["admin"]; // वर्तमान युजरच्या भूमिका आणण्याचे अनुकरण करा
const authorized = roles.some(role => currentUserRoles.includes(role));
if (!authorized) {
console.warn(`[AUTH] ${String(propertyKey)} साठी प्रवेश नाकारला. आवश्यक भूमिका: ${roles.join(", ")}`);
throw new Error("अनधिकृत प्रवेश");
}
console.log(`[AUTH] ${String(propertyKey)} साठी प्रवेश मंजूर`);
return originalMethod.apply(this, args);
};
return descriptor;
};
}
class SecureService {
@LogCall
@Authorization(["admin"])
deleteSensitiveData(id: string) {
console.log(`संवेदनशील डेटा हटवत आहे आयडीसाठी: ${id}`);
return `डेटा आयडी ${id} हटवला.`;
}
@Authorization(["user"])
@LogCall // येथे क्रम बदलला आहे
fetchPublicData(query: string) {
console.log(`सार्वजनिक डेटा शोधत आहे क्वेरीसह: ${query}`);
return `क्वेरीसाठी सार्वजनिक डेटा: ${query}`;
}
}
const service = new SecureService();
try {
console.log("\n--- deleteSensitiveData कॉल करत आहे (ॲडमिन युजर) ---");
service.deleteSensitiveData("record123");
} catch (error: any) {
console.error(error.message);
}
try {
console.log("\n--- fetchPublicData कॉल करत आहे (नॉन-ॲडमिन युजर) ---");
// fetchPublicData ॲक्सेस करण्यासाठी 'user' भूमिका आवश्यक असलेल्या नॉन-ॲडमिन युजरचे अनुकरण करा
const mockUserRoles = ["guest"]; // हे प्रमाणीकरण अयशस्वी होईल
// याला डायनॅमिक करण्यासाठी, तुम्हाला वर्तमान युजर भूमिकांसाठी DI सिस्टीम किंवा स्टॅटिक कॉन्टेक्स्टची आवश्यकता असेल.
// साधेपणासाठी, आम्ही असे गृहीत धरतो की Authorization डेकोरेटरला वर्तमान युजर कॉन्टेक्स्टमध्ये प्रवेश आहे.
// डेमो उद्देशांसाठी Authorization डेकोरेटर नेहमी 'admin' गृहीत धरण्यासाठी समायोजित करूया,
// जेणेकरून पहिला कॉल यशस्वी होईल आणि दुसरा अयशस्वी होऊन वेगवेगळे मार्ग दाखवेल.
// fetchPublicData यशस्वी होण्यासाठी युजर भूमिकेसह पुन्हा चालवा.
// कल्पना करा की Authorization मधील currentUserRoles असे होईल: ['user']
// या उदाहरणासाठी, ते सोपे ठेवूया आणि क्रमाचा प्रभाव दाखवूया.
service.fetchPublicData("search term"); // हे Auth -> Log कार्यान्वित करेल
} catch (error: any) {
console.error(error.message);
}
/* deleteSensitiveData साठी अपेक्षित आउटपुट:
[AUTH] deleteSensitiveData साठी प्रवेश मंजूर
[लॉग] deleteSensitiveData ला आर्ग्युमेंट्ससह कॉल करत आहे: [ 'record123' ]
संवेदनशील डेटा हटवत आहे आयडीसाठी: record123
[लॉग] मेथड deleteSensitiveData ने परत केले: डेटा आयडी record123 हटवला.
*/
/* fetchPublicData साठी अपेक्षित आउटपुट (जर युजरला 'user' भूमिका असेल तर):
[लॉग] fetchPublicData ला आर्ग्युमेंट्ससह कॉल करत आहे: [ 'search term' ]
[AUTH] fetchPublicData साठी प्रवेश मंजूर
सार्वजनिक डेटा शोधत आहे क्वेरीसह: search term
[लॉग] मेथड fetchPublicData ने परत केले: क्वेरीसाठी सार्वजनिक डेटा: search term
*/
क्रम लक्षात घ्या: deleteSensitiveData
साठी, Authorization
(खाली) प्रथम चालते, नंतर LogCall
(वर) त्याच्याभोवती गुंडाळते. Authorization
चे आंतरिक लॉजिक प्रथम कार्यान्वित होते. fetchPublicData
साठी, LogCall
(खाली) प्रथम चालते, नंतर Authorization
(वर) त्याच्याभोवती गुंडाळते. याचा अर्थ LogCall
चा पैलू Authorization
च्या पैलूच्या बाहेर असेल. हा फरक लॉगिंग किंवा त्रुटी हाताळणीसारख्या क्रॉस-कटिंग चिंतांसाठी महत्त्वाचा आहे, जिथे कार्यान्वयनाचा क्रम वर्तनावर लक्षणीय परिणाम करू शकतो.
वेगवेगळ्या लक्ष्यांसाठी कार्यान्वयन क्रम
जेव्हा एका क्लास, त्याचे सदस्य आणि पॅरामीटर्स सर्वांवर डेकोरेटर्स असतात, तेव्हा कार्यान्वयन क्रम सु-परिभाषित असतो:
- पॅरामीटर डेकोरेटर्स प्रथम लागू केले जातात, प्रत्येक पॅरामीटरसाठी, शेवटच्या पॅरामीटरपासून पहिल्यापर्यंत.
- नंतर, प्रत्येक सदस्यासाठी मेथड, ॲक्सेसर, किंवा प्रॉपर्टी डेकोरेटर्स लागू केले जातात.
- शेवटी, क्लास डेकोरेटर्स क्लासवरच लागू केले जातात.
प्रत्येक श्रेणीमध्ये, एकाच लक्ष्यावर अनेक डेकोरेटर्स खालून वर (किंवा उजवीकडून डावीकडे) लागू केले जातात.
उदाहरण: पूर्ण कार्यान्वयन क्रम
function log(message: string) {
return function (target: any, propertyKey: string | symbol | undefined, descriptorOrIndex?: PropertyDescriptor | number) {
if (typeof descriptorOrIndex === 'number') {
console.log(`पॅराम डेकोरेटर: ${String(propertyKey || "constructor")} च्या पॅरामीटर #${descriptorOrIndex} वर ${message}`);
} else if (typeof propertyKey === 'string' || typeof propertyKey === 'symbol') {
if (descriptorOrIndex && 'value' in descriptorOrIndex && typeof descriptorOrIndex.value === 'function') {
console.log(`मेथड/ॲक्सेसर डेकोरेटर: ${String(propertyKey)} वर ${message}`);
} else {
console.log(`प्रॉपर्टी डेकोरेटर: ${String(propertyKey)} वर ${message}`);
}
} else {
console.log(`क्लास डेकोरेटर: ${target.name} वर ${message}`);
}
return descriptorOrIndex; // मेथड/ॲक्सेसरसाठी डिस्क्रिप्टर परत करा, इतरांसाठी undefined
};
}
@log("क्लास लेवल D")
@log("क्लास लेवल C")
class MyDecoratedClass {
@log("स्टॅटिक प्रॉपर्टी A")
static staticProp: string = "";
@log("इन्स्टन्स प्रॉपर्टी B")
instanceProp: number = 0;
@log("मेथड D")
@log("मेथड C")
myMethod(
@log("पॅरामीटर Z") paramZ: string,
@log("पॅरामीटर Y") paramY: number
) {
console.log("मेथड myMethod कार्यान्वित झाली.");
}
@log("गेटर/सेटर F")
get myAccessor() {
return "";
}
set myAccessor(value: string) {
//...
}
constructor() {
console.log("कन्स्ट्रक्टर कार्यान्वित झाला.");
}
}
new MyDecoratedClass();
// मेथड डेकोरेटर ट्रिगर करण्यासाठी मेथड कॉल करा
new MyDecoratedClass().myMethod("hello", 123);
/* अंदाजित आउटपुट क्रम (विशिष्ट टाइपस्क्रिप्ट आवृत्ती आणि संकलनावर अवलंबून अंदाजे):
पॅराम डेकोरेटर: myMethod च्या पॅरामीटर #1 वर पॅरामीटर Y
पॅराम डेकोरेटर: myMethod च्या पॅरामीटर #0 वर पॅरामीटर Z
प्रॉपर्टी डेकोरेटर: staticProp वर स्टॅटिक प्रॉपर्टी A
प्रॉपर्टी डेकोरेटर: instanceProp वर इन्स्टन्स प्रॉपर्टी B
मेथड/ॲक्सेसर डेकोरेटर: myAccessor वर गेटर/सेटर F
मेथड/ॲक्सेसर डेकोरेटर: myMethod वर मेथड C
मेथड/ॲक्सेसर डेकोरेटर: myMethod वर मेथड D
क्लास डेकोरेटर: MyDecoratedClass वर क्लास लेवल C
क्लास डेकोरेटर: MyDecoratedClass वर क्लास लेवल D
कन्स्ट्रक्टर कार्यान्वित झाला.
मेथड myMethod कार्यान्वित झाली.
*/
कन्सोल लॉगची अचूक वेळ कन्स्ट्रक्टर किंवा मेथड केव्हा कॉल केली जाते यावर किंचित बदलू शकते, परंतु डेकोरेटर फंक्शन्स स्वतः ज्या क्रमाने कार्यान्वित होतात (आणि त्यामुळे त्यांचे साइड इफेक्ट्स किंवा परत केलेले मूल्य लागू होतात) ते वरील नियमांचे पालन करतात.
डेकोरेटर्ससह व्यावहारिक अनुप्रयोग आणि डिझाइन पॅटर्न्स
डेकोरेटर्स, विशेषतः reflect-metadata
पॉलीफिलच्या संयोगाने, मेटाडेटा-चालित प्रोग्रामिंगचे एक नवीन क्षेत्र उघडतात. हे शक्तिशाली डिझाइन पॅटर्न्सना अनुमती देते जे बॉयलरप्लेट आणि क्रॉस-कटिंग चिंता दूर करतात.
१. डिपेंडेंसी इंजेक्शन (DI)
डेकोरेटर्सचा सर्वात प्रमुख उपयोग डिपेंडेंसी इंजेक्शन फ्रेमवर्कमध्ये होतो (जसे की अँँग्युलरचे @Injectable()
, @Component()
, इत्यादी, किंवा NestJS चा DI चा विस्तृत वापर). डेकोरेटर्स तुम्हाला थेट कन्स्ट्रक्टर किंवा प्रॉपर्टीजवर अवलंबित्व घोषित करण्याची परवानगी देतात, ज्यामुळे फ्रेमवर्कला योग्य सर्व्हिसेस स्वयंचलितपणे इन्स्टँशिएट आणि प्रदान करता येते.
उदाहरण: सरलीकृत सर्व्हिस इंजेक्शन
import "reflect-metadata"; // emitDecoratorMetadata साठी आवश्यक
const INJECTABLE_METADATA_KEY = Symbol("injectable");
const INJECT_METADATA_KEY = Symbol("inject");
function Injectable() {
return function (target: Function) {
Reflect.defineMetadata(INJECTABLE_METADATA_KEY, true, target);
};
}
function Inject(token: any) {
return function (target: Object, propertyKey: string | symbol, parameterIndex: number) {
const existingInjections: any[] = Reflect.getOwnMetadata(INJECT_METADATA_KEY, target, propertyKey) || [];
existingInjections[parameterIndex] = token;
Reflect.defineMetadata(INJECT_METADATA_KEY, existingInjections, target, propertyKey);
};
}
class Container {
private static instances = new Map<any, any>();
static resolve<T>(target: { new (...args: any[]): T }): T {
if (Container.instances.has(target)) {
return Container.instances.get(target);
}
const isInjectable = Reflect.getMetadata(INJECTABLE_METADATA_KEY, target);
if (!isInjectable) {
throw new Error(`क्लास ${target.name} @Injectable म्हणून चिन्हांकित नाही.`);
}
// कन्स्ट्रक्टर पॅरामीटर्सचे प्रकार मिळवा (emitDecoratorMetadata आवश्यक)
const paramTypes: any[] = Reflect.getMetadata("design:paramtypes", target) || [];
const explicitInjections: any[] = Reflect.getMetadata(INJECT_METADATA_KEY, target) || [];
const dependencies = paramTypes.map((paramType, index) => {
// प्रदान केल्यास स्पष्ट @Inject टोकन वापरा, अन्यथा प्रकार अनुमानित करा
const token = explicitInjections[index] || paramType;
if (token === undefined) {
throw new Error(`${target.name} साठी निर्देशांक ${index} वरील पॅरामीटरचे निराकरण करू शकत नाही. ते एक गोलाकार अवलंबित्व किंवा स्पष्ट @Inject शिवाय आदिम प्रकार असू शकते.`);
}
return Container.resolve(token);
});
const instance = new target(...dependencies);
Container.instances.set(target, instance);
return instance;
}
}
// सर्व्हिसेस परिभाषित करा
@Injectable()
class DatabaseService {
connect() {
console.log("डेटाबेसशी कनेक्ट करत आहे...");
return "DB कनेक्शन";
}
}
@Injectable()
class AuthService {
private db: DatabaseService;
constructor(db: DatabaseService) {
this.db = db;
}
login() {
console.log(`AuthService: ${this.db.connect()} वापरून प्रमाणीकरण करत आहे`);
return "युजर लॉग इन झाला";
}
}
@Injectable()
class UserService {
private authService: AuthService;
private dbService: DatabaseService; // कस्टम डेकोरेटर किंवा फ्रेमवर्क वैशिष्ट्य वापरून प्रॉपर्टीद्वारे इंजेक्शनचे उदाहरण
constructor(@Inject(AuthService) authService: AuthService,
@Inject(DatabaseService) dbService: DatabaseService) {
this.authService = authService;
this.dbService = dbService;
}
getUserProfile() {
this.authService.login();
this.dbService.connect();
console.log("UserService: युजर प्रोफाइल आणत आहे...");
return { id: 1, name: "Global User" };
}
}
// मुख्य सर्व्हिसचे निराकरण करा
console.log("--- UserService चे निराकरण करत आहे ---");
const userService = Container.resolve(UserService);
console.log(userService.getUserProfile());
console.log("\n--- AuthService चे निराकरण करत आहे (कॅश केलेले असावे) ---");
const authService = Container.resolve(AuthService);
authService.login();
हे विस्तृत उदाहरण दाखवते की @Injectable
आणि @Inject
डेकोरेटर्स, reflect-metadata
सह एकत्रितपणे, एका कस्टम Container
ला अवलंबित्व स्वयंचलितपणे निराकरण आणि प्रदान करण्याची परवानगी कसे देतात. टाइपस्क्रिप्टद्वारे स्वयंचलितपणे उत्सर्जित होणारा design:paramtypes
मेटाडेटा (जेव्हा emitDecoratorMetadata
सत्य असते) येथे अत्यंत महत्त्वाचा आहे.
२. आस्पेक्ट-ओरिएंटेड प्रोग्रामिंग (AOP)
AOP क्रॉस-कटिंग चिंता (उदा., लॉगिंग, सुरक्षा, व्यवहार) मॉड्युलराइझ करण्यावर लक्ष केंद्रित करते जे अनेक क्लासेस आणि मॉड्यूल्समध्ये पसरलेले असतात. डेकोरेटर्स टाइपस्क्रिप्टमध्ये AOP संकल्पना अंमलात आणण्यासाठी एक उत्कृष्ट जुळणारे आहेत.
उदाहरण: मेथड डेकोरेटरसह लॉगिंग
LogCall
डेकोरेटरला पुन्हा भेट देताना, ते AOP चे एक परिपूर्ण उदाहरण आहे. ते कोणत्याही मेथडमध्ये मेथडच्या मूळ कोडमध्ये बदल न करता लॉगिंग वर्तन जोडते. हे "काय करायचे" (बिझनेस लॉजिक) ला "ते कसे करायचे" (लॉगिंग, कार्यप्रदर्शन देखरेख, इत्यादी) पासून वेगळे करते.
function LogMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`[लॉग AOP] मेथडमध्ये प्रवेश करत आहे: ${String(propertyKey)} आर्ग्ससह:`, args);
try {
const result = originalMethod.apply(this, args);
console.log(`[लॉग AOP] मेथडमधून बाहेर पडत आहे: ${String(propertyKey)} परिणामासह:`, result);
return result;
} catch (error: any) {
console.error(`[लॉग AOP] मेथड ${String(propertyKey)} मध्ये त्रुटी:`, error.message);
throw error;
}
};
return descriptor;
}
class PaymentProcessor {
@LogMethod
processPayment(amount: number, currency: string) {
if (amount <= 0) {
throw new Error("पेमेंटची रक्कम सकारात्मक असणे आवश्यक आहे.");
}
console.log(`${amount} ${currency} चे पेमेंट प्रक्रिया करत आहे...`);
return `पेमेंटची ${amount} ${currency} यशस्वीरित्या प्रक्रिया झाली.`;
}
@LogMethod
refundPayment(transactionId: string) {
console.log(`व्यवहार आयडीसाठी पेमेंट परत करत आहे: ${transactionId}...`);
return `${transactionId} साठी परतावा सुरू केला.`;
}
}
const processor = new PaymentProcessor();
processor.processPayment(100, "USD");
try {
processor.processPayment(-50, "EUR");
} catch (error: any) {
console.error("पकडलेली त्रुटी:", error.message);
}
हा दृष्टिकोन PaymentProcessor
क्लासला फक्त पेमेंट लॉजिकवर केंद्रित ठेवतो, तर LogMethod
डेकोरेटर लॉगिंगच्या क्रॉस-कटिंग चिंतेची हाताळणी करतो.
३. प्रमाणीकरण आणि रूपांतर
डेकोरेटर्स थेट प्रॉपर्टीजवर प्रमाणीकरण नियम परिभाषित करण्यासाठी किंवा सिरियलायझेशन/डिसिरियलायझेशन दरम्यान डेटाचे रूपांतर करण्यासाठी अत्यंत उपयुक्त आहेत.
उदाहरण: प्रॉपर्टी डेकोरेटर्ससह डेटा प्रमाणीकरण
पूर्वीच्या @Required
उदाहरणाने हे आधीच दाखवले आहे. येथे संख्यात्मक श्रेणी प्रमाणीकरणासह आणखी एक उदाहरण आहे.
interface FieldValidationRule {
property: string | symbol;
validator: (value: any) => boolean;
message: string;
}
const fieldValidationRules = new Map<Function, FieldValidationRule[]>();
function addValidationRule(target: Object, propertyKey: string | symbol, validator: (value: any) => boolean, message: string) {
const rules = fieldValidationRules.get(target.constructor) || [];
rules.push({ property: propertyKey, validator, message });
fieldValidationRules.set(target.constructor, rules);
}
function IsPositive(target: Object, propertyKey: string | symbol) {
addValidationRule(target, propertyKey, (value: number) => value > 0, `${String(propertyKey)} एक सकारात्मक संख्या असणे आवश्यक आहे.`);
}
function MaxLength(maxLength: number) {
return function (target: Object, propertyKey: string | symbol) {
addValidationRule(target, propertyKey, (value: string) => value.length <= maxLength, `${String(propertyKey)} जास्तीत जास्त ${maxLength} अक्षरे लांब असणे आवश्यक आहे.`);
};
}
class Product {
@MaxLength(50)
name: string;
@IsPositive
price: number;
constructor(name: string, price: number) {
this.name = name;
this.price = price;
}
static validate(instance: any): string[] {
const errors: string[] = [];
const rules = fieldValidationRules.get(instance.constructor) || [];
for (const rule of rules) {
if (!rule.validator(instance[rule.property])) {
errors.push(rule.message);
}
}
return errors;
}
}
const product1 = new Product("Laptop", 1200);
console.log("उत्पादन 1 त्रुटी:", Product.validate(product1)); // []
const product2 = new Product("Very long product name that exceeds fifty characters limit for testing purpose", 50);
console.log("उत्पादन 2 त्रुटी:", Product.validate(product2)); // ["name जास्तीत जास्त 50 अक्षरे लांब असणे आवश्यक आहे."]
const product3 = new Product("Book", -10);
console.log("उत्पादन 3 त्रुटी:", Product.validate(product3)); // ["price एक सकारात्मक संख्या असणे आवश्यक आहे."]
ही रचना तुम्हाला तुमच्या मॉडेल प्रॉपर्टीजवर प्रमाणीकरण नियम घोषणात्मकरित्या परिभाषित करण्याची परवानगी देते, ज्यामुळे तुमचे डेटा मॉडेल्स त्यांच्या मर्यादांच्या बाबतीत स्वयं-वर्णनात्मक बनतात.
सर्वोत्तम पद्धती आणि विचार
डेकोरेटर्स शक्तिशाली असले तरी, त्यांचा वापर विवेकपूर्णपणे केला पाहिजे. त्यांचा गैरवापर केल्यास कोड डीबग करणे किंवा समजणे कठीण होऊ शकते.
डेकोरेटर्स केव्हा वापरावे (आणि केव्हा नाही)
- यासाठी वापरा:
- क्रॉस-कटिंग चिंता: लॉगिंग, कॅशिंग, ऑथोरायझेशन, व्यवहार व्यवस्थापन.
- मेटाडेटा घोषणा: ORMs साठी स्कीमा परिभाषित करणे, प्रमाणीकरण नियम, DI कॉन्फिगरेशन.
- फ्रेमवर्क एकत्रीकरण: मेटाडेटाचा लाभ घेणारे फ्रेमवर्क तयार करताना किंवा वापरताना.
- बॉयलरप्लेट कमी करणे: पुनरावृत्ती होणारे कोड पॅटर्न्स दूर करणे.
- यासाठी टाळा:
- साधे फंक्शन कॉल्स: जर एक साधे फंक्शन कॉल स्पष्टपणे समान परिणाम साध्य करू शकत असेल, तर त्याला प्राधान्य द्या.
- बिझनेस लॉजिक: डेकोरेटर्सने मुख्य बिझनेस लॉजिक परिभाषित करण्याऐवजी ते वाढवले पाहिजे.
- अति-गुंतागुंत: जर डेकोरेटर वापरल्याने कोड कमी वाचनीय किंवा तपासण्यास कठीण होत असेल, तर पुनर्विचार करा.
कार्यक्षमता परिणाम
डेकोरेटर्स संकलन-वेळी (किंवा जावास्क्रिप्ट रनटाइममध्ये व्याख्या-वेळी जर ट्रान्सपाईल केले असेल तर) कार्यान्वित होतात. परिवर्तन किंवा मेटाडेटा संकलन क्लास/मेथड परिभाषित केल्यावर होते, प्रत्येक कॉलवर नाही. त्यामुळे, डेकोरेटर्स *लागू* करण्याचा रनटाइम कार्यक्षमतेवर परिणाम किमान असतो. तथापि, तुमच्या डेकोरेटर्समधील *लॉजिक*चा कार्यक्षमतेवर परिणाम होऊ शकतो, विशेषतः जर ते प्रत्येक मेथड कॉलवर महागडी ऑपरेशन्स करत असतील (उदा., मेथड डेकोरेटरमध्ये जटिल गणना).
व्यवस्थापनक्षमता आणि वाचनीयता
डेकोरेटर्स, योग्यरित्या वापरल्यास, मुख्य लॉजिकमधून बॉयलरप्लेट कोड काढून वाचनीयता लक्षणीयरीत्या सुधारू शकतात. तथापि, जर ते जटिल, लपलेले परिवर्तन करत असतील, तर डीबगिंग आव्हानात्मक होऊ शकते. तुमचे डेकोरेटर्स चांगले दस्तऐवजीकरण केलेले आहेत आणि त्यांचे वर्तन अंदाजित आहे याची खात्री करा.
प्रायोगिक स्थिती आणि डेकोरेटर्सचे भविष्य
हे पुन्हा सांगणे महत्त्वाचे आहे की टाइपस्क्रिप्ट डेकोरेटर्स स्टेज 3 TC39 प्रस्तावावर आधारित आहेत. याचा अर्थ स्पेसिफिकेशन मोठ्या प्रमाणात स्थिर आहे परंतु अधिकृत ECMAScript मानकाचा भाग होण्यापूर्वी त्यात किरकोळ बदल होऊ शकतात. अँँग्युलर सारख्या फ्रेमवर्कने त्यांना स्वीकारले आहे, त्यांच्या अंतिम मानकीकरणावर विश्वास ठेवून. यात काही प्रमाणात धोका आहे, जरी त्यांच्या व्यापक अवलंबामुळे, महत्त्वपूर्ण ब्रेकिंग बदल होण्याची शक्यता कमी आहे.
TC39 प्रस्ताव विकसित झाला आहे. टाइपस्क्रिप्टची सध्याची अंमलबजावणी प्रस्तावाच्या जुन्या आवृत्तीवर आधारित आहे. "लेगसी डेकोरेटर्स" विरुद्ध "स्टँडर्ड डेकोरेटर्स" असा फरक आहे. जेव्हा अधिकृत मानक येईल, तेव्हा टाइपस्क्रिप्ट बहुधा त्याची अंमलबजावणी अद्यतनित करेल. बहुतेक डेव्हलपर्स जे फ्रेमवर्क वापरतात, त्यांच्यासाठी हे संक्रमण फ्रेमवर्कद्वारेच व्यवस्थापित केले जाईल. लायब्ररी लेखकांसाठी, लेगसी आणि भविष्यातील मानक डेकोरेटर्समधील सूक्ष्म फरक समजून घेणे आवश्यक होऊ शकते.
emitDecoratorMetadata
कंपाइलर पर्याय
हा पर्याय, जेव्हा tsconfig.json
मध्ये true
वर सेट केला जातो, तेव्हा टाइपस्क्रिप्ट कंपाइलरला संकलित जावास्क्रिप्टमध्ये काही डिझाइन-टाइम प्रकार मेटाडेटा उत्सर्जित करण्याचे निर्देश देतो. या मेटाडेटामध्ये कन्स्ट्रक्टर पॅरामीटर्सचा प्रकार (design:paramtypes
), मेथड्सचा रिटर्न प्रकार (design:returntype
), आणि प्रॉपर्टीजचा प्रकार (design:type
) समाविष्ट आहे.
हा उत्सर्जित मेटाडेटा मानक जावास्क्रिप्ट रनटाइमचा भाग नाही. तो सामान्यतः reflect-metadata
पॉलीफिलद्वारे वापरला जातो, जो नंतर त्याला Reflect.getMetadata()
फंक्शन्सद्वारे प्रवेशयोग्य बनवतो. डिपेंडेंसी इंजेक्शनसारख्या प्रगत पॅटर्न्ससाठी हे अत्यंत महत्त्वाचे आहे, जिथे कंटेनरला स्पष्ट कॉन्फिगरेशनशिवाय क्लासला आवश्यक असलेल्या अवलंबित्वांचे प्रकार माहित असणे आवश्यक आहे.
डेकोरेटर्ससह प्रगत पॅटर्न्स
डेकोरेटर्स एकत्र करून आणि विस्तारित करून आणखी अत्याधुनिक पॅटर्न्स तयार केले जाऊ शकतात.
१. डेकोरेटर्सना डेकोरेट करणे (उच्च-क्रम डेकोरेटर्स)
तुम्ही असे डेकोरेटर्स तयार करू शकता जे इतर डेकोरेटर्समध्ये बदल करतात किंवा त्यांची रचना करतात. हे कमी सामान्य आहे परंतु डेकोरेटर्सचे कार्यात्मक स्वरूप दर्शवते.
// एक डेकोरेटर जो सुनिश्चित करतो की एक मेथड लॉग केली जाईल आणि ॲडमिन भूमिकांची देखील आवश्यकता असेल
function AdminAndLoggedMethod() {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
// प्रथम Authorization लागू करा (आतील)
Authorization(["admin"])(target, propertyKey, descriptor);
// नंतर LogCall लागू करा (बाहेरील)
LogCall(target, propertyKey, descriptor);
return descriptor; // सुधारित डिस्क्रिप्टर परत करा
};
}
class AdminPanel {
@AdminAndLoggedMethod()
deleteUserAccount(userId: string) {
console.log(`युजर खाते हटवत आहे: ${userId}`);
return `युजर ${userId} हटवला.`;
}
}
const adminPanel = new AdminPanel();
adminPanel.deleteUserAccount("user007");
/* अपेक्षित आउटपुट (ॲडमिन भूमिका गृहीत धरून):
[AUTH] deleteUserAccount साठी प्रवेश मंजूर
[लॉग] deleteUserAccount ला आर्ग्ससह कॉल करत आहे: [ 'user007' ]
युजर खाते हटवत आहे: user007
[लॉग] मेथड deleteUserAccount ने परत केले: युजर user007 हटवला.
*/
येथे, AdminAndLoggedMethod
ही एक फॅक्टरी आहे जी एक डेकोरेटर परत करते आणि त्या डेकोरेटरमध्ये, ते दोन इतर डेकोरेटर्स लागू करते. हा पॅटर्न जटिल डेकोरेटर रचनांना सामावून घेऊ शकतो.
२. मिक्सिन्ससाठी डेकोरेटर्स वापरणे
टाइपस्क्रिप्ट मिक्सिन्स अंमलात आणण्यासाठी इतर मार्ग ऑफर करत असले तरी, डेकोरेटर्सचा वापर क्लासेसमध्ये क्षमतांना घोषणात्मकरित्या इंजेक्ट करण्यासाठी केला जाऊ शकतो.
function ApplyMixins(constructors: Function[]) {
return function (derivedConstructor: Function) {
constructors.forEach(baseConstructor => {
Object.getOwnPropertyNames(baseConstructor.prototype).forEach(name => {
Object.defineProperty(
derivedConstructor.prototype,
name,
Object.getOwnPropertyDescriptor(baseConstructor.prototype, name) || Object.create(null)
);
});
});
};
}
class Disposable {
isDisposed: boolean = false;
dispose() {
this.isDisposed = true;
console.log("ऑब्जेक्ट डिस्पोज केले.");
}
}
class Loggable {
log(message: string) {
console.log(`[Loggable] ${message}`);
}
}
@ApplyMixins([Disposable, Loggable])
class MyResource implements Disposable, Loggable {
// या प्रॉपर्टीज/मेथड्स डेकोरेटरद्वारे इंजेक्ट केल्या जातात
isDisposed!: boolean;
dispose!: () => void;
log!: (message: string) => void;
constructor(public name: string) {
this.log(`संसाधन ${this.name} तयार झाले.`);
}
cleanUp() {
this.dispose();
this.log(`संसाधन ${this.name} साफ झाले.`);
}
}
const resource = new MyResource("NetworkConnection");
console.log(`डिस्पोज झाले आहे: ${resource.isDisposed}`);
resource.cleanUp();
console.log(`डिस्पोज झाले आहे: ${resource.isDisposed}`);
हा @ApplyMixins
डेकोरेटर बेस कन्स्ट्रक्टर्समधून मेथड्स आणि प्रॉपर्टीज डायनॅमिकरित्या डिराइव्ह्ड क्लासच्या प्रोटोटाइपमध्ये कॉपी करतो, प्रभावीपणे कार्यक्षमता "मिक्स इन" करतो.
निष्कर्ष: आधुनिक टाइपस्क्रिप्ट डेव्हलपमेंटला सक्षम करणे
टाइपस्क्रिप्ट डेकोरेटर्स एक शक्तिशाली आणि अभिव्यक्त वैशिष्ट्य आहे जे मेटाडेटा-चालित आणि आस्पेक्ट-ओरिएंटेड प्रोग्रामिंगचे एक नवीन प्रतिमान सक्षम करते. ते डेव्हलपर्सना क्लासेस, मेथड्स, प्रॉपर्टीज, ॲक्सेसर्स आणि पॅरामीटर्समध्ये त्यांच्या मूळ लॉजिकमध्ये बदल न करता घोषणात्मक वर्तन वाढवण्यास, सुधारित करण्यास आणि जोडण्यास अनुमती देतात. या चिंतांच्या पृथक्करणामुळे स्वच्छ, अधिक व्यवस्थापित करण्यायोग्य आणि अत्यंत पुन्हा वापरण्यायोग्य कोड तयार होतो.
डिपेंडेंसी इंजेक्शन सोपे करण्यापासून आणि मजबूत प्रमाणीकरण प्रणाली अंमलात आणण्यापासून ते लॉगिंग आणि कार्यप्रदर्शन देखरेखीसारख्या क्रॉस-कटिंग चिंता जोडण्यापर्यंत, डेकोरेटर्स अनेक सामान्य विकास आव्हानांवर एक सुंदर उपाय प्रदान करतात. त्यांची प्रायोगिक स्थिती जागरुकतेची हमी देत असली तरी, प्रमुख फ्रेमवर्कमधील त्यांचा व्यापक अवलंब त्यांचे व्यावहारिक मूल्य आणि भविष्यातील प्रासंगिकता दर्शवतो.
टाइपस्क्रिप्ट डेकोरेटर्सवर प्रभुत्व मिळवून, तुम्ही तुमच्या शस्त्रागारात एक महत्त्वपूर्ण साधन मिळवता, जे तुम्हाला अधिक मजबूत, स्केलेबल आणि बुद्धिमान ॲप्लिकेशन्स तयार करण्यास सक्षम करते. त्यांना जबाबदारीने स्वीकारा, त्यांचे यांत्रिकी समजून घ्या आणि तुमच्या टाइपस्क्रिप्ट प्रकल्पांमध्ये घोषणात्मक शक्तीची एक नवीन पातळी अनलॉक करा.