మెటాడేటా ప్రోగ్రామింగ్, ఆస్పెక్ట్-ఓరియెంటెడ్ ప్రోగ్రామింగ్, మరియు డిక్లరేటివ్ ప్యాట్రన్లతో కోడ్ను మెరుగుపరచడానికి టైప్స్క్రిప్ట్ డెకరేటర్ల శక్తిని అన్వేషించండి. ప్రపంచవ్యాప్త డెవలపర్ల కోసం ఒక సమగ్ర గైడ్.
టైప్స్క్రిప్ట్ డెకరేటర్లు: దృఢమైన అప్లికేషన్ల కోసం మెటాడేటా ప్రోగ్రామింగ్ ప్యాట్రన్లను నేర్చుకోవడం
ఆధునిక సాఫ్ట్వేర్ డెవలప్మెంట్ యొక్క విస్తారమైన రంగంలో, శుభ్రమైన, స్కేలబుల్, మరియు నిర్వహించదగిన కోడ్బేస్లను నిర్వహించడం చాలా ముఖ్యం. టైప్స్క్రిప్ట్, దాని శక్తివంతమైన టైప్ సిస్టమ్ మరియు అధునాతన ఫీచర్లతో, డెవలపర్లకు దీన్ని సాధించడానికి సాధనాలను అందిస్తుంది. దాని అత్యంత ఆసక్తికరమైన మరియు పరివర్తనాత్మక ఫీచర్లలో డెకరేటర్లు ఒకటి. రాసే సమయంలో ఇది ఇంకా ప్రయోగాత్మక ఫీచర్ (ECMAScript కోసం స్టేజ్ 3 ప్రతిపాదన) అయినప్పటికీ, డెకరేటర్లు ఆంగ్యులర్ మరియు టైప్ఓఆర్ఎమ్ వంటి ఫ్రేమ్వర్క్లలో విస్తృతంగా ఉపయోగించబడుతున్నాయి, ఇది మనం డిజైన్ ప్యాట్రన్లు, మెటాడేటా ప్రోగ్రామింగ్, మరియు ఆస్పెక్ట్-ఓరియెంటెడ్ ప్రోగ్రామింగ్ (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
# or
yarn add reflect-metadata
మరియు దానిని మీ అప్లికేషన్ యొక్క ఎంట్రీ పాయింట్ (ఉదా., main.ts
లేదా app.ts
) పైభాగంలో ఇంపోర్ట్ చేయండి:
import "reflect-metadata";
// మీ అప్లికేషన్ కోడ్ అనుసరిస్తుంది
డెకరేటర్ ఫ్యాక్టరీలు: మీ వేలికొనలకు అనుకూలీకరణ
ఒక ప్రాథమిక డెకరేటర్ ఒక ఫంక్షన్ అయినప్పటికీ, దాని ప్రవర్తనను కాన్ఫిగర్ చేయడానికి తరచుగా మీరు డెకరేటర్కు ఆర్గ్యుమెంట్స్ పాస్ చేయవలసి ఉంటుంది. ఇది ఒక డెకరేటర్ ఫ్యాక్టరీని ఉపయోగించి సాధించబడుతుంది. డెకరేటర్ ఫ్యాక్టరీ అనేది అసలు డెకరేటర్ ఫంక్షన్ను తిరిగి ఇచ్చే ఫంక్షన్. మీరు డెకరేటర్ ఫ్యాక్టరీని వర్తింపజేసినప్పుడు, మీరు దాని ఆర్గ్యుమెంట్స్తో దానిని కాల్ చేస్తారు, మరియు అది అప్పుడు టైప్స్క్రిప్ట్ మీ కోడ్కు వర్తించే డెకరేటర్ ఫంక్షన్ను తిరిగి ఇస్తుంది.
ఒక సాధారణ డెకరేటర్ ఫ్యాక్టరీ ఉదాహరణను సృష్టించడం
వివిధ ప్రిఫిక్స్లతో సందేశాలను లాగ్ చేయగల ఒక Logger
డెకరేటర్ కోసం ఒక ఫ్యాక్టరీని సృష్టిద్దాం.
function Logger(prefix: string) {
return function (target: Function) {
console.log(`[${prefix}] Class ${target.name} has been defined.`);
};
}
@Logger("APP_INIT")
class ApplicationBootstrap {
constructor() {
console.log("Application is starting...");
}
}
const app = new ApplicationBootstrap();
// అవుట్పుట్:
// [APP_INIT] Class ApplicationBootstrap has been defined.
// Application is starting...
ఈ ఉదాహరణలో, Logger("APP_INIT")
డెకరేటర్ ఫ్యాక్టరీ కాల్. ఇది అసలు డెకరేటర్ ఫంక్షన్ను తిరిగి ఇస్తుంది, ఇది target: Function
(క్లాస్ కన్స్ట్రక్టర్)ని దాని ఆర్గ్యుమెంట్గా తీసుకుంటుంది. ఇది డెకరేటర్ యొక్క ప్రవర్తన యొక్క డైనమిక్ కాన్ఫిగరేషన్ను అనుమతిస్తుంది.
టైప్స్క్రిప్ట్లో డెకరేటర్ల రకాలు
టైప్స్క్రిప్ట్ ఐదు విభిన్న రకాల డెకరేటర్లకు మద్దతు ఇస్తుంది, ప్రతి ఒక్కటి ఒక నిర్దిష్ట రకమైన డిక్లరేషన్కు వర్తిస్తుంది. డెకరేటర్ ఫంక్షన్ యొక్క సిగ్నేచర్ అది వర్తించే సందర్భాన్ని బట్టి మారుతుంది.
1. క్లాస్ డెకరేటర్లు
క్లాస్ డెకరేటర్లు క్లాస్ డిక్లరేషన్లకు వర్తింపజేయబడతాయి. డెకరేటర్ ఫంక్షన్ క్లాస్ యొక్క కన్స్ట్రక్టర్ను దాని ఏకైక ఆర్గ్యుమెంట్గా అందుకుంటుంది. ఒక క్లాస్ డెకరేటర్ క్లాస్ నిర్వచనాన్ని పరిశీలించగలదు, సవరించగలదు, లేదా భర్తీ చేయగలదు.
సిగ్నేచర్:
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(`Registered service: ${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("--- Services Registered ---");
console.log(Array.from(InjectableServiceRegistry.keys()));
const userServiceConstructor = InjectableServiceRegistry.get("UserService");
if (userServiceConstructor) {
const userServiceInstance = new userServiceConstructor();
console.log("Users:", userServiceInstance.getUsers());
// console.log("User Service Created At:", userServiceInstance.createdAt); // తిరిగి ఇవ్వబడిన క్లాస్ ఉపయోగించబడితే
}
ఈ ఉదాహరణ క్లాస్ డెకరేటర్ ఒక క్లాస్ను ఎలా నమోదు చేయగలదో మరియు దాని కన్స్ట్రక్టర్ను కూడా ఎలా సవరించగలదో చూపిస్తుంది. Injectable
డెకరేటర్ క్లాస్ను ఒక సైద్ధాంతిక డిపెండెన్సీ ఇంజెక్షన్ సిస్టమ్ ద్వారా కనుగొనగలిగేలా చేస్తుంది.
2. మెథడ్ డెకరేటర్లు
మెథడ్ డెకరేటర్లు మెథడ్ డిక్లరేషన్లకు వర్తింపజేయబడతాయి. అవి మూడు ఆర్గ్యుమెంట్లను అందుకుంటాయి: టార్గెట్ ఆబ్జెక్ట్ (స్టాటిక్ సభ్యుల కోసం, కన్స్ట్రక్టర్ ఫంక్షన్; ఇన్స్టాన్స్ సభ్యుల కోసం, క్లాస్ యొక్క ప్రోటోటైప్), మెథడ్ పేరు, మరియు మెథడ్ యొక్క ప్రాపర్టీ డిస్క్రిప్టర్.
సిగ్నేచర్:
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(`Method "${propertyKey}" executed in ${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(`Data for ID: ${id}`);
}, 500);
});
}
}
const processor = new DataProcessor();
processor.processData([1, 2, 3]);
processor.fetchRemoteData("abc").then(result => console.log(result));
MeasurePerformance
డెకరేటర్ అసలు మెథడ్ను టైమింగ్ లాజిక్తో చుట్టి, మెథడ్లోని బిజినెస్ లాజిక్ను గందరగోళపరచకుండా అమలు వ్యవధిని ప్రింట్ చేస్తుంది. ఇది ఆస్పెక్ట్-ఓరియెంటెడ్ ప్రోగ్రామింగ్ (AOP) యొక్క ఒక క్లాసిక్ ఉదాహరణ.
3. యాక్సెసర్ డెకరేటర్లు
యాక్సెసర్ డెకరేటర్లు యాక్సెసర్ (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(`[Cache Miss] Computing value for ${String(propertyKey)}`);
this[cacheKey] = originalGetter.apply(this);
} else {
console.log(`[Cache Hit] Using cached value for ${String(propertyKey)}`);
}
return this[cacheKey];
};
}
return descriptor;
}
class ReportGenerator {
private data: number[];
constructor(data: number[]) {
this.data = data;
}
// ఒక ఖరీదైన గణనను అనుకరిస్తుంది
@CachedGetter
get expensiveSummary(): number {
console.log("Performing expensive summary calculation...");
return this.data.reduce((sum, current) => sum + current, 0) / this.data.length;
}
}
const generator = new ReportGenerator([10, 20, 30, 40, 50]);
console.log("First access:", generator.expensiveSummary);
console.log("Second access:", generator.expensiveSummary);
console.log("Third access:", generator.expensiveSummary);
ఈ డెకరేటర్ expensiveSummary
గెట్టర్ యొక్క గణన ఒకసారి మాత్రమే నడుస్తుందని నిర్ధారిస్తుంది, తదుపరి కాల్స్ కాష్ చేయబడిన విలువను తిరిగి ఇస్తాయి. ఈ ప్యాట్రన్ ప్రాపర్టీ యాక్సెస్ భారీ గణన లేదా బాహ్య కాల్స్ను కలిగి ఉన్న చోట పనితీరును ఆప్టిమైజ్ చేయడానికి చాలా ఉపయోగకరంగా ఉంటుంది.
4. ప్రాపర్టీ డెకరేటర్లు
ప్రాపర్టీ డెకరేటర్లు ప్రాపర్టీ డిక్లరేషన్లకు వర్తింపజేయబడతాయి. అవి రెండు ఆర్గ్యుమెంట్లను అందుకుంటాయి: టార్గెట్ ఆబ్జెక్ట్ (స్టాటిక్ సభ్యుల కోసం, కన్స్ట్రక్టర్ ఫంక్షన్; ఇన్స్టాన్స్ సభ్యుల కోసం, క్లాస్ యొక్క ప్రోటోటైప్), మరియు ప్రాపర్టీ పేరు.
సిగ్నేచర్:
function PropertyDecorator(target: Object, propertyKey: string | symbol) { ... }
తిరిగి ఇచ్చే విలువ:
ప్రాపర్టీ డెకరేటర్లు ఏ విలువను తిరిగి ఇవ్వలేవు. వాటి ప్రాథమిక ఉపయోగం ప్రాపర్టీ గురించి మెటాడేటాను నమోదు చేయడం. అవి ప్రాపర్టీ యొక్క విలువను లేదా దాని డిస్క్రిప్టర్ను డెకరేషన్ సమయంలో నేరుగా మార్చలేవు, ఎందుకంటే ప్రాపర్టీ డెకరేటర్లు నడుస్తున్నప్పుడు ప్రాపర్టీ కోసం డిస్క్రిప్టర్ ఇంకా పూర్తిగా నిర్వచించబడలేదు.
ఉపయోగ సందర్భాలు:
- సీరియలైజేషన్/డీసీరియలైజేషన్ కోసం ప్రాపర్టీలను నమోదు చేయడం.
- ప్రాపర్టీలకు ధృవీకరణ నియమాలను వర్తింపజేయడం.
- ప్రాపర్టీల కోసం డిఫాల్ట్ విలువలు లేదా కాన్ఫిగరేషన్లను సెట్ చేయడం.
- ORM (ఆబ్జెక్ట్-రిలేషనల్ మ్యాపింగ్) కాలమ్ మ్యాపింగ్ (ఉదా., టైప్ఓఆర్ఎమ్లో
@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)} is required.`
});
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("User 1 validation errors:", validate(user1)); // []
const user2 = new UserProfile("", "Smith");
console.log("User 2 validation errors:", validate(user2)); // ["firstName is required."]
const user3 = new UserProfile("Alice", "");
console.log("User 3 validation errors:", validate(user3)); // ["lastName is required."]
Required
డెకరేటర్ కేవలం ఒక కేంద్ర validationRules
మ్యాప్తో ధృవీకరణ నియమాన్ని నమోదు చేస్తుంది. ఒక ప్రత్యేక validate
ఫంక్షన్ ఈ మెటాడేటాను ఉపయోగించి రన్టైమ్లో ఇన్స్టాన్స్ను తనిఖీ చేస్తుంది. ఈ ప్యాట్రన్ ధృవీకరణ లాజిక్ను డేటా నిర్వచనం నుండి వేరు చేస్తుంది, ఇది పునర్వినియోగం మరియు శుభ్రంగా ఉంటుంది.
5. పారామీటర్ డెకరేటర్లు
పారామీటర్ డెకరేటర్లు క్లాస్ కన్స్ట్రక్టర్ లేదా మెథడ్లోని పారామీటర్లకు వర్తింపజేయబడతాయి. అవి మూడు ఆర్గ్యుమెంట్లను అందుకుంటాయి: టార్గెట్ ఆబ్జెక్ట్ (స్టాటిక్ సభ్యుల కోసం, కన్స్ట్రక్టర్ ఫంక్షన్; ఇన్స్టాన్స్ సభ్యుల కోసం, క్లాస్ యొక్క ప్రోటోటైప్), మెథడ్ పేరు (లేదా కన్స్ట్రక్టర్ పారామీటర్ల కోసం 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(`Fetching user with ID: ${userId}, Token: ${authToken || "N/A"}`);
return { id: userId, name: "Jane Doe" };
}
deleteUser(@RequestParam("id") userId: string) {
console.log(`Deleting user with ID: ${userId}`);
return { status: "deleted", id: userId };
}
}
const userController = new UserController();
// ఒక ఇన్కమింగ్ అభ్యర్థనను అనుకరించండి
const mockRequest = {
id: "user123",
token: "abc-123",
someOtherProp: "xyz"
};
console.log("\n--- Executing getUser ---");
executeWithParams(userController, "getUser", mockRequest);
console.log("\n--- Executing 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(`[LOG] Calling ${String(propertyKey)} with args:`, args);
const result = originalMethod.apply(this, args);
console.log(`[LOG] Method ${String(propertyKey)} returned:`, 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] Access denied for ${String(propertyKey)}. Required roles: ${roles.join(", ")}`);
throw new Error("Unauthorized access");
}
console.log(`[AUTH] Access granted for ${String(propertyKey)}`);
return originalMethod.apply(this, args);
};
return descriptor;
};
}
class SecureService {
@LogCall
@Authorization(["admin"])
deleteSensitiveData(id: string) {
console.log(`Deleting sensitive data for ID: ${id}`);
return `Data ID ${id} deleted.`;
}
@Authorization(["user"])
@LogCall // ఇక్కడ క్రమం మార్చబడింది
fetchPublicData(query: string) {
console.log(`Fetching public data with query: ${query}`);
return `Public data for query: ${query}`;
}
}
const service = new SecureService();
try {
console.log("\n--- Calling deleteSensitiveData (Admin User) ---");
service.deleteSensitiveData("record123");
} catch (error: any) {
console.error(error.message);
}
try {
console.log("\n--- Calling fetchPublicData (Non-Admin User) ---");
// 'user' పాత్ర అవసరమైన fetchPublicData ను యాక్సెస్ చేయడానికి ప్రయత్నిస్తున్న ఒక నాన్-అడ్మిన్ యూజర్ను అనుకరించండి
const mockUserRoles = ["guest"]; // ఇది ఆథరైజేషన్లో విఫలమవుతుంది
// దీనిని డైనమిక్గా చేయడానికి, మీకు ప్రస్తుత యూజర్ పాత్రల కోసం DI సిస్టమ్ లేదా స్టాటిక్ కాంటెక్స్ట్ అవసరం.
// సరళత కోసం, ఆథరైజేషన్ డెకరేటర్ ప్రస్తుత యూజర్ కాంటెక్స్ట్కు యాక్సెస్ ఉందని మేము భావిస్తాము.
// డెమో ప్రయోజనాల కోసం ఆథరైజేషన్ డెకరేటర్ను ఎల్లప్పుడూ 'admin' అని భావించేలా సర్దుబాటు చేద్దాం,
// తద్వారా మొదటి కాల్ విజయవంతమవుతుంది మరియు రెండవది విఫలమై వివిధ మార్గాలను చూపుతుంది.
// fetchPublicData విజయవంతం కావడానికి యూజర్ పాత్రతో తిరిగి అమలు చేయండి.
// ఆథరైజేషన్లో currentUserRoles ఇలా మారాయని ఊహించుకోండి: ['user']
// ఈ ఉదాహరణ కోసం, దీనిని సరళంగా ఉంచి, క్రమం ప్రభావాన్ని చూపుదాం.
service.fetchPublicData("search term"); // ఇది Auth -> Log ను అమలు చేస్తుంది
} catch (error: any) {
console.error(error.message);
}
/* deleteSensitiveData కోసం ఊహించిన అవుట్పుట్:
[AUTH] Access granted for deleteSensitiveData
[LOG] Calling deleteSensitiveData with args: [ 'record123' ]
Deleting sensitive data for ID: record123
[LOG] Method deleteSensitiveData returned: Data ID record123 deleted.
*/
/* fetchPublicData కోసం ఊహించిన అవుట్పుట్ (యూజర్కు 'user' పాత్ర ఉంటే):
[LOG] Calling fetchPublicData with args: [ 'search term' ]
[AUTH] Access granted for fetchPublicData
Fetching public data with query: search term
[LOG] Method fetchPublicData returned: Public data for query: 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(`Param Decorator: ${message} on parameter #${descriptorOrIndex} of ${String(propertyKey || "constructor")}`);
} else if (typeof propertyKey === 'string' || typeof propertyKey === 'symbol') {
if (descriptorOrIndex && 'value' in descriptorOrIndex && typeof descriptorOrIndex.value === 'function') {
console.log(`Method/Accessor Decorator: ${message} on ${String(propertyKey)}`);
} else {
console.log(`Property Decorator: ${message} on ${String(propertyKey)}`);
}
} else {
console.log(`Class Decorator: ${message} on ${target.name}`);
}
return descriptorOrIndex; // మెథడ్/యాక్సెసర్ కోసం డిస్క్రిప్టర్ తిరిగి ఇవ్వండి, ఇతరులకు undefined
};
}
@log("Class Level D")
@log("Class Level C")
class MyDecoratedClass {
@log("Static Property A")
static staticProp: string = "";
@log("Instance Property B")
instanceProp: number = 0;
@log("Method D")
@log("Method C")
myMethod(
@log("Parameter Z") paramZ: string,
@log("Parameter Y") paramY: number
) {
console.log("Method myMethod executed.");
}
@log("Getter/Setter F")
get myAccessor() {
return "";
}
set myAccessor(value: string) {
//...
}
constructor() {
console.log("Constructor executed.");
}
}
new MyDecoratedClass();
// మెథడ్ డెకరేటర్ను ట్రిగ్గర్ చేయడానికి మెథడ్ను కాల్ చేయండి
new MyDecoratedClass().myMethod("hello", 123);
/* ఊహించిన అవుట్పుట్ క్రమం (సుమారుగా, నిర్దిష్ట టైప్స్క్రిప్ట్ వెర్షన్ మరియు కంపైలేషన్పై ఆధారపడి):
Param Decorator: Parameter Y on parameter #1 of myMethod
Param Decorator: Parameter Z on parameter #0 of myMethod
Property Decorator: Static Property A on staticProp
Property Decorator: Instance Property B on instanceProp
Method/Accessor Decorator: Getter/Setter F on myAccessor
Method/Accessor Decorator: Method C on myMethod
Method/Accessor Decorator: Method D on myMethod
Class Decorator: Class Level C on MyDecoratedClass
Class Decorator: Class Level D on MyDecoratedClass
Constructor executed.
Method myMethod executed.
*/
ఒక కన్స్ట్రక్టర్ లేదా మెథడ్ ఎప్పుడు ఇన్వోక్ చేయబడుతుందనే దానిపై ఆధారపడి ఖచ్చితమైన కన్సోల్ లాగ్ టైమింగ్ కొద్దిగా మారవచ్చు, కానీ డెకరేటర్ ఫంక్షన్లు అమలు చేయబడే క్రమం (మరియు అందువల్ల వాటి సైడ్ ఎఫెక్ట్స్ లేదా తిరిగి ఇవ్వబడిన విలువలు వర్తింపజేయబడతాయి) పై నియమాలను అనుసరిస్తుంది.
డెకరేటర్లతో ఆచరణాత్మక అప్లికేషన్లు మరియు డిజైన్ ప్యాట్రన్లు
డెకరేటర్లు, ముఖ్యంగా reflect-metadata
పాలిఫిల్తో కలిపి, మెటాడేటా-ఆధారిత ప్రోగ్రామింగ్ యొక్క కొత్త రంగాన్ని తెరుస్తాయి. ఇది బాయిలర్ప్లేట్ మరియు క్రాస్-కటింగ్ ఆందోళనలను సంగ్రహించే శక్తివంతమైన డిజైన్ ప్యాట్రన్లను అనుమతిస్తుంది.
1. డిపెండెన్సీ ఇంజెక్షన్ (DI)
డెకరేటర్ల యొక్క అత్యంత ప్రముఖ ఉపయోగాలలో ఒకటి డిపెండెన్సీ ఇంజెక్షన్ ఫ్రేమ్వర్క్లలో (ఆంగ్యులర్ యొక్క @Injectable()
, @Component()
, మొదలైనవి, లేదా నెస్ట్జేఎస్ యొక్క విస్తృతమైన 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(`Class ${target.name} is not marked as @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(`Cannot resolve parameter at index ${index} for ${target.name}. It might be a circular dependency or primitive type without explicit @Inject.`);
}
return Container.resolve(token);
});
const instance = new target(...dependencies);
Container.instances.set(target, instance);
return instance;
}
}
// సర్వీస్లను నిర్వచించండి
@Injectable()
class DatabaseService {
connect() {
console.log("Connecting to database...");
return "DB Connection";
}
}
@Injectable()
class AuthService {
private db: DatabaseService;
constructor(db: DatabaseService) {
this.db = db;
}
login() {
console.log(`AuthService: Authenticating using ${this.db.connect()}`);
return "User logged in";
}
}
@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: Fetching user profile...");
return { id: 1, name: "Global User" };
}
}
// ప్రధాన సర్వీస్ను రిసాల్వ్ చేయండి
console.log("--- Resolving UserService ---");
const userService = Container.resolve(UserService);
console.log(userService.getUserProfile());
console.log("\n--- Resolving AuthService (should be cached) ---");
const authService = Container.resolve(AuthService);
authService.login();
ఈ విస్తృతమైన ఉదాహరణ @Injectable
మరియు @Inject
డెకరేటర్లు, reflect-metadata
తో కలిపి, ఒక కస్టమ్ Container
ఆటోమేటిక్గా డిపెండెన్సీలను ఎలా రిసాల్వ్ చేసి, అందించగలదో చూపిస్తుంది. టైప్స్క్రిప్ట్ (emitDecoratorMetadata
నిజమైనప్పుడు) ఆటోమేటిక్గా విడుదల చేసే design:paramtypes
మెటాడేటా ఇక్కడ చాలా ముఖ్యమైనది.
2. ఆస్పెక్ట్-ఓరియెంటెడ్ ప్రోగ్రామింగ్ (AOP)
AOP క్రాస్-కటింగ్ ఆందోళనలను (ఉదా., లాగింగ్, భద్రత, లావాదేవీలు) మాడ్యులరైజ్ చేయడంపై దృష్టి పెడుతుంది, ఇవి బహుళ క్లాస్లు మరియు మాడ్యూల్స్ను దాటిపోతాయి. టైప్స్క్రిప్ట్లో AOP భావనలను అమలు చేయడానికి డెకరేటర్లు ఒక అద్భుతమైన సరిపోలిక.
ఉదాహరణ: మెథడ్ డెకరేటర్తో లాగింగ్
LogCall
డెకరేటర్ను తిరిగి సందర్శిస్తే, ఇది AOP యొక్క ఒక ఖచ్చితమైన ఉదాహరణ. ఇది మెథడ్ యొక్క అసలు కోడ్ను సవరించకుండా ఏ మెథడ్కైనా లాగింగ్ ప్రవర్తనను జోడిస్తుంది. ఇది "ఏమి చేయాలి" (బిజినెస్ లాజిక్) నుండి "ఎలా చేయాలి" (లాగింగ్, పనితీరు పర్యవేక్షణ, మొదలైనవి) వేరు చేస్తుంది.
function LogMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`[LOG AOP] Entering method: ${String(propertyKey)} with args:`, args);
try {
const result = originalMethod.apply(this, args);
console.log(`[LOG AOP] Exiting method: ${String(propertyKey)} with result:`, result);
return result;
} catch (error: any) {
console.error(`[LOG AOP] Error in method ${String(propertyKey)}:`, error.message);
throw error;
}
};
return descriptor;
}
class PaymentProcessor {
@LogMethod
processPayment(amount: number, currency: string) {
if (amount <= 0) {
throw new Error("Payment amount must be positive.");
}
console.log(`Processing payment of ${amount} ${currency}...`);
return `Payment of ${amount} ${currency} processed successfully.`;
}
@LogMethod
refundPayment(transactionId: string) {
console.log(`Refunding payment for transaction ID: ${transactionId}...`);
return `Refund initiated for ${transactionId}.`;
}
}
const processor = new PaymentProcessor();
processor.processPayment(100, "USD");
try {
processor.processPayment(-50, "EUR");
} catch (error: any) {
console.error("Caught error:", error.message);
}
ఈ విధానం PaymentProcessor
క్లాస్ను కేవలం చెల్లింపు లాజిక్పై దృష్టి పెట్టేలా చేస్తుంది, అయితే LogMethod
డెకరేటర్ లాగింగ్ యొక్క క్రాస్-కటింగ్ ఆందోళనను నిర్వహిస్తుంది.
3. ధృవీకరణ మరియు పరివర్తన
డెకరేటర్లు ప్రాపర్టీలపై నేరుగా ధృవీకరణ నియమాలను నిర్వచించడానికి లేదా సీరియలైజేషన్/డీసీరియలైజేషన్ సమయంలో డేటాను మార్చడానికి చాలా ఉపయోగకరంగా ఉంటాయి.
ఉదాహరణ: ప్రాపర్టీ డెకరేటర్లతో డేటా ధృవీకరణ
మునుపటి @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)} must be a positive number.`);
}
function MaxLength(maxLength: number) {
return function (target: Object, propertyKey: string | symbol) {
addValidationRule(target, propertyKey, (value: string) => value.length <= maxLength, `${String(propertyKey)} must be at most ${maxLength} characters long.`);
};
}
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("Product 1 errors:", Product.validate(product1)); // []
const product2 = new Product("Very long product name that exceeds fifty characters limit for testing purpose", 50);
console.log("Product 2 errors:", Product.validate(product2)); // ["name must be at most 50 characters long."]
const product3 = new Product("Book", -10);
console.log("Product 3 errors:", Product.validate(product3)); // ["price must be a positive number."]
ఈ సెటప్ మీ మోడల్ ప్రాపర్టీలపై డిక్లరేటివ్గా ధృవీకరణ నియమాలను నిర్వచించడానికి మిమ్మల్ని అనుమతిస్తుంది, ఇది మీ డేటా మోడల్స్ను వాటి పరిమితుల పరంగా స్వీయ-వివరణాత్మకంగా చేస్తుంది.
ఉత్తమ పద్ధతులు మరియు పరిగణనలు
డెకరేటర్లు శక్తివంతమైనవి అయినప్పటికీ, వాటిని వివేకంతో ఉపయోగించాలి. వాటిని దుర్వినియోగం చేయడం వలన డీబగ్ చేయడానికి లేదా అర్థం చేసుకోవడానికి కష్టంగా ఉండే కోడ్కు దారితీయవచ్చు.
డెకరేటర్లను ఎప్పుడు ఉపయోగించాలి (మరియు ఎప్పుడు ఉపయోగించకూడదు)
- వీటి కోసం ఉపయోగించండి:
- క్రాస్-కటింగ్ ఆందోళనలు: లాగింగ్, కాషింగ్, ఆథరైజేషన్, లావాదేవీల నిర్వహణ.
- మెటాడేటా డిక్లరేషన్: ఓఆర్ఎంల కోసం స్కీమాను నిర్వచించడం, ధృవీకరణ నియమాలు, డిఐ కాన్ఫిగరేషన్.
- ఫ్రేమ్వర్క్ ఇంటిగ్రేషన్: మెటాడేటాను ఉపయోగించుకునే ఫ్రేమ్వర్క్లను నిర్మించేటప్పుడు లేదా ఉపయోగించేటప్పుడు.
- బాయిలర్ప్లేట్ను తగ్గించడం: పునరావృత కోడ్ ప్యాట్రన్లను సంగ్రహించడం.
- వీటి కోసం నివారించండి:
- సాధారణ ఫంక్షన్ కాల్స్: ఒక సాధారణ ఫంక్షన్ కాల్ అదే ఫలితాన్ని స్పష్టంగా సాధించగలిగితే, దానిని ఇష్టపడండి.
- బిజినెస్ లాజిక్: డెకరేటర్లు ప్రధాన బిజినెస్ లాజిక్ను నిర్వచించకూడదు, కానీ పెంచాలి.
- అతి-సంక్లిష్టత: ఒక డెకరేటర్ను ఉపయోగించడం వలన కోడ్ తక్కువ చదవగలిగేలా లేదా పరీక్షించడానికి కష్టంగా ఉంటే, పునరాలోచించండి.
పనితీరు చిక్కులు
డెకరేటర్లు కంపైల్-టైమ్లో (లేదా ట్రాన్స్పైల్ చేయబడితే జావాస్క్రిప్ట్ రన్టైమ్లో డెఫినిషన్-టైమ్లో) అమలు చేయబడతాయి. క్లాస్/మెథడ్ నిర్వచించబడినప్పుడు పరివర్తన లేదా మెటాడేటా సేకరణ జరుగుతుంది, ప్రతి కాల్పై కాదు. అందువల్ల, డెకరేటర్లను *వర్తింపజేయడం* యొక్క రన్టైమ్ పనితీరు ప్రభావం చాలా తక్కువ. అయితే, మీ డెకరేటర్ల *లోపల లాజిక్* పనితీరుపై ప్రభావం చూపుతుంది, ముఖ్యంగా అవి ప్రతి మెథడ్ కాల్పై ఖరీదైన ఆపరేషన్లను నిర్వహిస్తే (ఉదా., మెథడ్ డెకరేటర్లో సంక్లిష్ట గణనలు).
నిర్వహణ మరియు చదవగలిగే సామర్థ్యం
డెకరేటర్లు, సరిగ్గా ఉపయోగించినప్పుడు, ప్రధాన లాజిక్ నుండి బాయిలర్ప్లేట్ కోడ్ను తరలించడం ద్వారా చదవగలిగే సామర్థ్యాన్ని గణనీయంగా మెరుగుపరుస్తాయి. అయితే, అవి సంక్లిష్ట, దాచిన పరివర్తనలను నిర్వహిస్తే, డీబగ్గింగ్ సవాలుగా మారవచ్చు. మీ డెకరేటర్లు బాగా డాక్యుమెంట్ చేయబడి, వాటి ప్రవర్తన అంచనా వేయదగినదిగా ఉండేలా చూసుకోండి.
ప్రయోగాత్మక స్థితి మరియు డెకరేటర్ల భవిష్యత్తు
టైప్స్క్రిప్ట్ డెకరేటర్లు స్టేజ్ 3 టిసి39 ప్రతిపాదనపై ఆధారపడి ఉన్నాయని పునరుద్ఘాటించడం ముఖ్యం. దీని అర్థం స్పెసిఫికేషన్ చాలా వరకు స్థిరంగా ఉంది కానీ అధికారిక ECMAScript ప్రమాణంలో భాగంగా మారడానికి ముందు ఇప్పటికీ చిన్న మార్పులకు లోనవుతుంది. ఆంగ్యులర్ వంటి ఫ్రేమ్వర్క్లు వాటిని స్వీకరించాయి, వాటి తుది ప్రామాణీకరణపై పందెం కాస్తున్నాయి. ఇది ఒక నిర్దిష్ట స్థాయి ప్రమాదాన్ని సూచిస్తుంది, అయితే వాటి విస్తృతమైన స్వీకరణను బట్టి, గణనీయమైన బ్రేకింగ్ మార్పులు అసంభవం.
టిసి39 ప్రతిపాదన అభివృద్ధి చెందింది. టైప్స్క్రిప్ట్ యొక్క ప్రస్తుత అమలు ప్రతిపాదన యొక్క పాత వెర్షన్పై ఆధారపడి ఉంది. "లెగసీ డెకరేటర్లు" వర్సెస్ "ప్రామాణిక డెకరేటర్లు" అనే వ్యత్యాసం ఉంది. అధికారిక ప్రమాణం వచ్చినప్పుడు, టైప్స్క్రిప్ట్ దాని అమలును అప్డేట్ చేసే అవకాశం ఉంది. ఫ్రేమ్వర్క్లను ఉపయోగించే చాలా మంది డెవలపర్ల కోసం, ఈ పరివర్తన ఫ్రేమ్వర్క్ ద్వారానే నిర్వహించబడుతుంది. లైబ్రరీ రచయితల కోసం, లెగసీ మరియు భవిష్యత్ ప్రామాణిక డెకరేటర్ల మధ్య సూక్ష్మ వ్యత్యాసాలను అర్థం చేసుకోవడం అవసరం కావచ్చు.
emitDecoratorMetadata
కంపైలర్ ఆప్షన్
ఈ ఆప్షన్, tsconfig.json
లో true
కు సెట్ చేయబడినప్పుడు, టైప్స్క్రిప్ట్ కంపైలర్కు కొన్ని డిజైన్-టైమ్ టైప్ మెటాడేటాను కంపైల్ చేయబడిన జావాస్క్రిప్ట్లోకి విడుదల చేయమని నిర్దేశిస్తుంది. ఈ మెటాడేటాలో కన్స్ట్రక్టర్ పారామీటర్ల రకం (design:paramtypes
), మెథడ్ల రిటర్న్ రకం (design:returntype
), మరియు ప్రాపర్టీల రకం (design:type
) ఉంటాయి.
ఈ విడుదల చేయబడిన మెటాడేటా ప్రామాణిక జావాస్క్రిప్ట్ రన్టైమ్లో భాగం కాదు. ఇది సాధారణంగా reflect-metadata
పాలిఫిల్ ద్వారా వినియోగించబడుతుంది, ఇది అప్పుడు Reflect.getMetadata()
ఫంక్షన్ల ద్వారా అందుబాటులో ఉంటుంది. డిపెండెన్సీ ఇంజెక్షన్ వంటి అధునాతన ప్యాట్రన్ల కోసం ఇది ఖచ్చితంగా చాలా ముఖ్యం, ఇక్కడ ఒక కంటైనర్ స్పష్టమైన కాన్ఫిగరేషన్ లేకుండా ఒక క్లాస్కు అవసరమైన డిపెండెన్సీల రకాలను తెలుసుకోవాలి.
డెకరేటర్లతో అధునాతన ప్యాట్రన్లు
డెకరేటర్లను కలిపి మరియు విస్తరించి మరింత అధునాతన ప్యాట్రన్లను నిర్మించవచ్చు.
1. డెకరేటర్లను డెకరేట్ చేయడం (హయ్యర్-ఆర్డర్ డెకరేటర్లు)
మీరు ఇతర డెకరేటర్లను సవరించే లేదా కంపోజ్ చేసే డెకరేటర్లను సృష్టించవచ్చు. ఇది తక్కువ సాధారణం కానీ డెకరేటర్ల యొక్క ఫంక్షనల్ స్వభావాన్ని ప్రదర్శిస్తుంది.
// ఒక మెథడ్ లాగ్ చేయబడిందని మరియు అడ్మిన్ పాత్రలు కూడా అవసరమని నిర్ధారించే ఒక డెకరేటర్
function AdminAndLoggedMethod() {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
// మొదట ఆథరైజేషన్ వర్తించండి (లోపలిది)
Authorization(["admin"])(target, propertyKey, descriptor);
// ఆపై LogCall వర్తించండి (బయటిది)
LogCall(target, propertyKey, descriptor);
return descriptor; // సవరించిన డిస్క్రిప్టర్ను తిరిగి ఇవ్వండి
};
}
class AdminPanel {
@AdminAndLoggedMethod()
deleteUserAccount(userId: string) {
console.log(`Deleting user account: ${userId}`);
return `User ${userId} deleted.`;
}
}
const adminPanel = new AdminPanel();
adminPanel.deleteUserAccount("user007");
/* ఊహించిన అవుట్పుట్ (అడ్మిన్ పాత్ర ఉందని భావించి):
[AUTH] Access granted for deleteUserAccount
[LOG] Calling deleteUserAccount with args: [ 'user007' ]
Deleting user account: user007
[LOG] Method deleteUserAccount returned: User user007 deleted.
*/
ఇక్కడ, AdminAndLoggedMethod
ఒక డెకరేటర్ను తిరిగి ఇచ్చే ఒక ఫ్యాక్టరీ, మరియు ఆ డెకరేటర్ లోపల, ఇది రెండు ఇతర డెకరేటర్లను వర్తింపజేస్తుంది. ఈ ప్యాట్రన్ సంక్లిష్ట డెకరేటర్ కూర్పులను సంగ్రహించగలదు.
2. మిక్సిన్ల కోసం డెకరేటర్లను ఉపయోగించడం
టైప్స్క్రిప్ట్ మిక్సిన్లను అమలు చేయడానికి ఇతర మార్గాలను అందిస్తున్నప్పటికీ, డెకరేటర్లను క్లాస్లలోకి సామర్థ్యాలను డిక్లరేటివ్ పద్ధతిలో ఇంజెక్ట్ చేయడానికి ఉపయోగించవచ్చు.
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("Object disposed.");
}
}
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(`Resource ${this.name} created.`);
}
cleanUp() {
this.dispose();
this.log(`Resource ${this.name} cleaned up.`);
}
}
const resource = new MyResource("NetworkConnection");
console.log(`Is disposed: ${resource.isDisposed}`);
resource.cleanUp();
console.log(`Is disposed: ${resource.isDisposed}`);
ఈ @ApplyMixins
డెకరేటర్ డైనమిక్గా బేస్ కన్స్ట్రక్టర్ల నుండి మెథడ్స్ మరియు ప్రాపర్టీలను డెరైవ్డ్ క్లాస్ యొక్క ప్రోటోటైప్కు కాపీ చేస్తుంది, ఇది కార్యాచరణలను సమర్థవంతంగా "మిక్స్ ఇన్" చేస్తుంది.
ముగింపు: ఆధునిక టైప్స్క్రిప్ట్ డెవలప్మెంట్ను శక్తివంతం చేయడం
టైప్స్క్రిప్ట్ డెకరేటర్లు ఒక శక్తివంతమైన మరియు వ్యక్తీకరణాత్మక ఫీచర్, ఇది మెటాడేటా-ఆధారిత మరియు ఆస్పెక్ట్-ఓరియెంటెడ్ ప్రోగ్రామింగ్ యొక్క కొత్త నమూనాను అనుమతిస్తుంది. అవి డెవలపర్లకు క్లాస్లు, మెథడ్స్, ప్రాపర్టీలు, యాక్సెసర్లు మరియు పారామీటర్లను వాటి ప్రధాన లాజిక్ను మార్చకుండా మెరుగుపరచడానికి, సవరించడానికి, మరియు డిక్లరేటివ్ ప్రవర్తనలను జోడించడానికి అనుమతిస్తాయి. ఈ ఆందోళనల విభజన శుభ్రమైన, మరింత నిర్వహించదగిన, మరియు అధికంగా పునర్వినియోగించగల కోడ్కు దారితీస్తుంది.
డిపెండెన్సీ ఇంజెక్షన్ను సులభతరం చేయడం మరియు దృఢమైన ధృవీకరణ వ్యవస్థలను అమలు చేయడం నుండి లాగింగ్ మరియు పనితీరు పర్యవేక్షణ వంటి క్రాస్-కటింగ్ ఆందోళనలను జోడించడం వరకు, డెకరేటర్లు అనేక సాధారణ అభివృద్ధి సవాళ్లకు ఒక సొగసైన పరిష్కారాన్ని అందిస్తాయి. వాటి ప్రయోగాత్మక స్థితి అవగాహనను కోరుతున్నప్పటికీ, ప్రధాన ఫ్రేమ్వర్క్లలో వాటి విస్తృతమైన స్వీకరణ వాటి ఆచరణాత్మక విలువను మరియు భవిష్యత్ ప్రాముఖ్యతను సూచిస్తుంది.
టైప్స్క్రిప్ట్ డెకరేటర్లను నేర్చుకోవడం ద్వారా, మీరు మీ ఆయుధాగారంలో ఒక ముఖ్యమైన సాధనాన్ని పొందుతారు, ఇది మిమ్మల్ని మరింత దృఢమైన, స్కేలబుల్, మరియు తెలివైన అప్లికేషన్లను నిర్మించడానికి వీలు కల్పిస్తుంది. వాటిని బాధ్యతాయుతంగా స్వీకరించండి, వాటి మెకానిక్స్ను అర్థం చేసుకోండి, మరియు మీ టైప్స్క్రిప్ట్ ప్రాజెక్ట్లలో కొత్త స్థాయి డిక్లరేటివ్ శక్తిని అన్లాక్ చేయండి.