ಮೆಟಾಡೇಟಾ ಪ್ರೋಗ್ರಾಮಿಂಗ್, ಆಸ್ಪೆಕ್ಟ್-ಓರಿಯೆಂಟೆಡ್ ಪ್ರೋಗ್ರಾಮಿಂಗ್, ಮತ್ತು ಘೋಷಣಾತ್ಮಕ ಪ್ಯಾಟರ್ನ್ಗಳೊಂದಿಗೆ ಕೋಡ್ ವರ್ಧಿಸಲು ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ ಡೆಕೋರೇಟರ್ಗಳ ಶಕ್ತಿಯನ್ನು ಅನ್ವೇಷಿಸಿ. ಜಾಗತಿಕ ಡೆವಲಪರ್ಗಳಿಗಾಗಿ ಒಂದು ಸಮಗ್ರ ಮಾರ್ಗದರ್ಶಿ.
ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ ಡೆಕೋರೇಟರ್ಗಳು: ದೃಢವಾದ ಅಪ್ಲಿಕೇಶನ್ಗಳಿಗಾಗಿ ಮೆಟಾಡೇಟಾ ಪ್ರೋಗ್ರಾಮಿಂಗ್ ಪ್ಯಾಟರ್ನ್ಗಳಲ್ಲಿ ಪರಿಣತಿ
ಆಧುನಿಕ ಸಾಫ್ಟ್ವೇರ್ ಅಭಿವೃದ್ಧಿಯ ವಿಶಾಲ ಭೂದೃಶ್ಯದಲ್ಲಿ, ಸ್ವಚ್ಛ, ಸ್ಕೇಲೆಬಲ್, ಮತ್ತು ನಿರ್ವಹಿಸಬಲ್ಲ ಕೋಡ್ಬೇಸ್ಗಳನ್ನು ನಿರ್ವಹಿಸುವುದು ಅತ್ಯಗತ್ಯ. ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್, ತನ್ನ ಶಕ್ತಿಯುತ ಟೈಪ್ ಸಿಸ್ಟಮ್ ಮತ್ತು ಸುಧಾರಿತ ವೈಶಿಷ್ಟ್ಯಗಳೊಂದಿಗೆ, ಇದನ್ನು ಸಾಧಿಸಲು ಡೆವಲಪರ್ಗಳಿಗೆ ಸಾಧನಗಳನ್ನು ಒದಗಿಸುತ್ತದೆ. ಅದರ ಅತ್ಯಂತ ಕುತೂಹಲಕಾರಿ ಮತ್ತು ಪರಿವರ್ತನಾತ್ಮಕ ವೈಶಿಷ್ಟ್ಯಗಳಲ್ಲಿ ಡೆಕೋರೇಟರ್ಗಳು ಕೂಡ ಒಂದು. ಈ ಬರವಣಿಗೆಯ ಸಮಯದಲ್ಲಿ ಇದು ಇನ್ನೂ ಪ್ರಾಯೋಗಿಕ ವೈಶಿಷ್ಟ್ಯವಾಗಿದ್ದರೂ (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";
// Your application code follows
ಡೆಕೋರೇಟರ್ ಫ್ಯಾಕ್ಟರಿಗಳು: ನಿಮ್ಮ ಬೆರಳ ತುದಿಯಲ್ಲಿ ಕಸ್ಟಮೈಸೇಶನ್
ಒಂದು ಮೂಲಭೂತ ಡೆಕೋರೇಟರ್ ಒಂದು ಫಂಕ್ಷನ್ ಆಗಿದ್ದರೂ, ಅದರ ನಡವಳಿಕೆಯನ್ನು ಕಾನ್ಫಿಗರ್ ಮಾಡಲು ನೀವು ಡೆಕೋರೇಟರ್ಗೆ ಆರ್ಗ್ಯುಮೆಂಟ್ಗಳನ್ನು ರವಾನಿಸಬೇಕಾಗುತ್ತದೆ. ಇದನ್ನು ಡೆಕೋರೇಟರ್ ಫ್ಯಾಕ್ಟರಿ ಬಳಸಿ ಸಾಧಿಸಲಾಗುತ್ತದೆ. ಡೆಕೋರೇಟರ್ ಫ್ಯಾಕ್ಟರಿ ಎನ್ನುವುದು ನಿಜವಾದ ಡೆಕೋರೇಟರ್ ಫಂಕ್ಷನ್ ಅನ್ನು ಹಿಂತಿರುಗಿಸುವ ಒಂದು ಫಂಕ್ಷನ್ ಆಗಿದೆ. ನೀವು ಡೆಕೋರೇಟರ್ ಫ್ಯಾಕ್ಟರಿಯನ್ನು ಅನ್ವಯಿಸಿದಾಗ, ನೀವು ಅದನ್ನು ಅದರ ಆರ್ಗ್ಯುಮೆಂಟ್ಗಳೊಂದಿಗೆ ಕರೆಯುತ್ತೀರಿ, ಮತ್ತು ನಂತರ ಅದು ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ ನಿಮ್ಮ ಕೋಡ್ಗೆ ಅನ್ವಯಿಸುವ ಡೆಕೋರೇಟರ್ ಫಂಕ್ಷನ್ ಅನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ.
ಸರಳ ಡೆಕೋರೇಟರ್ ಫ್ಯಾಕ್ಟರಿ ಉದಾಹರಣೆಯನ್ನು ರಚಿಸುವುದು
ವಿವಿಧ ಪೂರ್ವಪ್ರತ್ಯಯಗಳೊಂದಿಗೆ ಸಂದೇಶಗಳನ್ನು ಲಾಗ್ ಮಾಡಬಲ್ಲ 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();
// Output:
// [APP_INIT] Class ApplicationBootstrap has been defined.
// Application is starting...
ಈ ಉದಾಹರಣೆಯಲ್ಲಿ, Logger("APP_INIT")
ಡೆಕೋರೇಟರ್ ಫ್ಯಾಕ್ಟರಿ ಕರೆಯಾಗಿದೆ. ಇದು ನಿಜವಾದ ಡೆಕೋರೇಟರ್ ಫಂಕ್ಷನ್ ಅನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ, ಅದು target: Function
(ಕ್ಲಾಸ್ ಕನ್ಸ್ಟ್ರಕ್ಟರ್) ಅನ್ನು ಅದರ ಆರ್ಗ್ಯುಮೆಂಟ್ ಆಗಿ ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ. ಇದು ಡೆಕೋರೇಟರ್ನ ನಡವಳಿಕೆಯ ಡೈನಾಮಿಕ್ ಕಾನ್ಫಿಗರೇಶನ್ಗೆ ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ.
ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ನಲ್ಲಿ ಡೆಕೋರೇಟರ್ಗಳ ವಿಧಗಳು
ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ ಐದು ವಿಭಿನ್ನ ರೀತಿಯ ಡೆಕೋರೇಟರ್ಗಳನ್ನು ಬೆಂಬಲಿಸುತ್ತದೆ, ಪ್ರತಿಯೊಂದೂ ಒಂದು ನಿರ್ದಿಷ್ಟ ರೀತಿಯ ಘೋಷಣೆಗೆ ಅನ್ವಯಿಸುತ್ತದೆ. ಡೆಕೋರೇಟರ್ ಫಂಕ್ಷನ್ನ ಸಿಗ್ನೇಚರ್ ಅದನ್ನು ಅನ್ವಯಿಸುವ ಸಂದರ್ಭವನ್ನು ಆಧರಿಸಿ ಬದಲಾಗುತ್ತದೆ.
1. ಕ್ಲಾಸ್ ಡೆಕೋರೇಟರ್ಗಳು
ಕ್ಲಾಸ್ ಡೆಕೋರೇಟರ್ಗಳನ್ನು ಕ್ಲಾಸ್ ಘೋಷಣೆಗಳಿಗೆ ಅನ್ವಯಿಸಲಾಗುತ್ತದೆ. ಡೆಕೋರೇಟರ್ ಫಂಕ್ಷನ್ ಕ್ಲಾಸ್ನ ಕನ್ಸ್ಟ್ರಕ್ಟರ್ ಅನ್ನು ತನ್ನ ಏಕೈಕ ಆರ್ಗ್ಯುಮೆಂಟ್ ಆಗಿ ಸ್ವೀಕರಿಸುತ್ತದೆ. ಕ್ಲಾಸ್ ಡೆಕೋರೇಟರ್ ಒಂದು ಕ್ಲಾಸ್ ವ್ಯಾಖ್ಯಾನವನ್ನು ಗಮನಿಸಬಹುದು, ಮಾರ್ಪಡಿಸಬಹುದು, ಅಥವಾ ಬದಲಾಯಿಸಬಹುದು.
ಸಿಗ್ನೇಚರ್:
function ClassDecorator(target: Function) { ... }
ಹಿಂತಿರುಗಿಸುವ ಮೌಲ್ಯ:
ಕ್ಲಾಸ್ ಡೆಕೋರೇಟರ್ ಒಂದು ಮೌಲ್ಯವನ್ನು ಹಿಂತಿರುಗಿಸಿದರೆ, ಅದು ಕ್ಲಾಸ್ ಘೋಷಣೆಯನ್ನು ಒದಗಿಸಿದ ಕನ್ಸ್ಟ್ರಕ್ಟರ್ ಫಂಕ್ಷನ್ನೊಂದಿಗೆ ಬದಲಾಯಿಸುತ್ತದೆ. ಇದು ಒಂದು ಶಕ್ತಿಯುತ ವೈಶಿಷ್ಟ್ಯವಾಗಿದ್ದು, ಇದನ್ನು ಮಿಕ್ಸಿನ್ಗಳು ಅಥವಾ ಕ್ಲಾಸ್ ವರ್ಧನೆಗಾಗಿ ಹೆಚ್ಚಾಗಿ ಬಳಸಲಾಗುತ್ತದೆ. ಯಾವುದೇ ಮೌಲ್ಯವನ್ನು ಹಿಂತಿರುಗಿಸದಿದ್ದರೆ, ಮೂಲ ಕ್ಲಾಸ್ ಅನ್ನು ಬಳಸಲಾಗುತ್ತದೆ.
ಬಳಕೆಯ ಪ್ರಕರಣಗಳು:
- ಡಿಪೆಂಡೆನ್ಸಿ ಇಂಜೆಕ್ಷನ್ ಕಂಟೇನರ್ನಲ್ಲಿ ಕ್ಲಾಸ್ಗಳನ್ನು ನೋಂದಾಯಿಸುವುದು.
- ಒಂದು ಕ್ಲಾಸ್ಗೆ ಮಿಕ್ಸಿನ್ಗಳು ಅಥವಾ ಹೆಚ್ಚುವರಿ ಕಾರ್ಯಗಳನ್ನು ಅನ್ವಯಿಸುವುದು.
- ಫ್ರೇಮ್ವರ್ಕ್-ನಿರ್ದಿಷ್ಟ ಸಂರಚನೆಗಳು (ಉದಾಹರಣೆಗೆ, ವೆಬ್ ಫ್ರೇಮ್ವರ್ಕ್ನಲ್ಲಿ ರೂಟಿಂಗ್).
- ಕ್ಲಾಸ್ಗಳಿಗೆ ಲೈಫ್ಸೈಕಲ್ ಹುಕ್ಗಳನ್ನು ಸೇರಿಸುವುದು.
ಕ್ಲಾಸ್ ಡೆಕೋರೇಟರ್ ಉದಾಹರಣೆ: ಸೇವೆಯನ್ನು ಇಂಜೆಕ್ಟ್ ಮಾಡುವುದು
ಒಂದು ಸರಳ ಡಿಪೆಂಡೆನ್ಸಿ ಇಂಜೆಕ್ಷನ್ ಸನ್ನಿವೇಶವನ್ನು ಕಲ್ಪಿಸಿಕೊಳ್ಳಿ, ಅಲ್ಲಿ ನೀವು ಒಂದು ಕ್ಲಾಸ್ ಅನ್ನು "injectable" ಎಂದು ಗುರುತಿಸಲು ಮತ್ತು ಐಚ್ಛಿಕವಾಗಿ ಕಂಟೇನರ್ನಲ್ಲಿ ಅದಕ್ಕೆ ಒಂದು ಹೆಸರನ್ನು ಒದಗಿಸಲು ಬಯಸುತ್ತೀರಿ.
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}`);
// Optionally, you could return a new class here to augment behavior
return class extends constructor {
createdAt = new Date();
// Additional properties or methods for all injected services
};
};
}
@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); // If the returned class is used
}
ಈ ಉದಾಹರಣೆಯು ಕ್ಲಾಸ್ ಡೆಕೋರೇಟರ್ ಹೇಗೆ ಒಂದು ಕ್ಲಾಸ್ ಅನ್ನು ನೋಂದಾಯಿಸಬಹುದು ಮತ್ತು ಅದರ ಕನ್ಸ್ಟ್ರಕ್ಟರ್ ಅನ್ನು ಸಹ ಮಾರ್ಪಡಿಸಬಹುದು ಎಂಬುದನ್ನು ಪ್ರದರ್ಶಿಸುತ್ತದೆ. 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[] {
// Simulate a complex, time-consuming operation
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;
}
// Simulates an expensive computation
@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);
};
}
// A hypothetical framework function to invoke a method with resolved parameters
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();
// Simulate an incoming request
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"]; // Simulate fetching current user roles
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 // Order changed here
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) ---");
// Simulate a non-admin user trying to access fetchPublicData which requires 'user' role
const mockUserRoles = ["guest"]; // This will fail auth
// To make this dynamic, you'd need a DI system or static context for current user roles.
// For simplicity, we assume the Authorization decorator has access to current user context.
// Let's adjust Authorization decorator to always assume 'admin' for demo purposes,
// so the first call succeeds and second fails to show different paths.
// Re-run with user role for fetchPublicData to succeed.
// Imagine currentUserRoles in Authorization becomes: ['user']
// For this example, let's keep it simple and show the order effect.
service.fetchPublicData("search term"); // This will execute Auth -> Log
} catch (error: any) {
console.error(error.message);
}
/* Expected output for 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.
*/
/* Expected output for fetchPublicData (if user has 'user' role):
[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; // Return descriptor for method/accessor, undefined for others
};
}
@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();
// Call method to trigger method decorator
new MyDecoratedClass().myMethod("hello", 123);
/* Predicted Output Order (approximate, depending on specific TypeScript version and compilation):
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. ಡಿಪೆಂಡೆನ್ಸಿ ಇಂಜೆಕ್ಷನ್ (ಡಿಐ)
ಡೆಕೋರೇಟರ್ಗಳ ಅತ್ಯಂತ ಪ್ರಮುಖ ಬಳಕೆಗಳಲ್ಲಿ ಒಂದು ಡಿಪೆಂಡೆನ್ಸಿ ಇಂಜೆಕ್ಷನ್ ಫ್ರೇಮ್ವರ್ಕ್ಗಳಲ್ಲಿದೆ (ಆಂಗುಲರ್ನ @Injectable()
, @Component()
, ಇತ್ಯಾದಿ, ಅಥವಾ ನೆಸ್ಟ್ ಜೆಎಸ್ನ ವ್ಯಾಪಕವಾದ ಡಿಐ ಬಳಕೆ). ಡೆಕೋರೇಟರ್ಗಳು ನಿಮಗೆ ಕನ್ಸ್ಟ್ರಕ್ಟರ್ಗಳು ಅಥವಾ ಪ್ರಾಪರ್ಟಿಗಳಲ್ಲಿ ನೇರವಾಗಿ ಡಿಪೆಂಡೆನ್ಸಿಗಳನ್ನು ಘೋಷಿಸಲು ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ, ಫ್ರೇಮ್ವರ್ಕ್ಗೆ ಸರಿಯಾದ ಸೇವೆಗಳನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಇನ್ಸ್ಟಾನ್ಶಿಯೇಟ್ ಮಾಡಲು ಮತ್ತು ಒದಗಿಸಲು ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ.
ಉದಾಹರಣೆ: ಸರಳೀಕೃತ ಸೇವಾ ಇಂಜೆಕ್ಷನ್
import "reflect-metadata"; // Essential for 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.`);
}
// Get constructor parameters' types (requires emitDecoratorMetadata)
const paramTypes: any[] = Reflect.getMetadata("design:paramtypes", target) || [];
const explicitInjections: any[] = Reflect.getMetadata(INJECT_METADATA_KEY, target) || [];
const dependencies = paramTypes.map((paramType, index) => {
// Use explicit @Inject token if provided, otherwise infer type
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;
}
}
// Define services
@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; // Example of injecting via property using a custom decorator or framework feature
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" };
}
}
// Resolve the main service
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
ಗೆ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಡಿಪೆಂಡೆನ್ಸಿಗಳನ್ನು ಪರಿಹರಿಸಲು ಮತ್ತು ಒದಗಿಸಲು ಹೇಗೆ ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ ಎಂಬುದನ್ನು ಪ್ರದರ್ಶಿಸುತ್ತದೆ. ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ನಿಂದ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಹೊರಸೂಸುವ design:paramtypes
ಮೆಟಾಡೇಟಾ (emitDecoratorMetadata
true ಆಗಿದ್ದಾಗ) ಇಲ್ಲಿ ನಿರ್ಣಾಯಕವಾಗಿದೆ.
2. ಆಸ್ಪೆಕ್ಟ್-ಓರಿಯೆಂಟೆಡ್ ಪ್ರೋಗ್ರಾಮಿಂಗ್ (ಎಒಪಿ)
ಎಒಪಿ ಕ್ರಾಸ್-ಕಟಿಂಗ್ ಕಾಳಜಿಗಳನ್ನು (ಉದಾಹರಣೆಗೆ, ಲಾಗಿಂಗ್, ಭದ್ರತೆ, ವಹಿವಾಟುಗಳು) ಮಾಡ್ಯುಲರೈಸ್ ಮಾಡುವುದರ ಮೇಲೆ ಕೇಂದ್ರೀಕರಿಸುತ್ತದೆ, ಅದು ಬಹು ಕ್ಲಾಸ್ಗಳು ಮತ್ತು ಮಾಡ್ಯೂಲ್ಗಳಾದ್ಯಂತ ಕತ್ತರಿಸುತ್ತದೆ. ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ನಲ್ಲಿ ಎಒಪಿ ಪರಿಕಲ್ಪನೆಗಳನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸಲು ಡೆಕೋರೇಟರ್ಗಳು ಅತ್ಯುತ್ತಮವಾಗಿ ಹೊಂದಿಕೊಳ್ಳುತ್ತವೆ.
ಉದಾಹರಣೆ: ಮೆಥಡ್ ಡೆಕೋರೇಟರ್ನೊಂದಿಗೆ ಲಾಗಿಂಗ್
LogCall
ಡೆಕೋರೇಟರ್ ಅನ್ನು ಪುನಃ ಪರಿಶೀಲಿಸಿದಾಗ, ಇದು ಎಒಪಿಯ ಒಂದು ಪರಿಪೂರ್ಣ ಉದಾಹರಣೆಯಾಗಿದೆ. ಇದು ಯಾವುದೇ ಮೆಥಡ್ನ ಮೂಲ ಕೋಡ್ ಅನ್ನು ಮಾರ್ಪಡಿಸದೆ ಲಾಗಿಂಗ್ ನಡವಳಿಕೆಯನ್ನು ಸೇರಿಸುತ್ತದೆ. ಇದು "ಏನು ಮಾಡಬೇಕು" (ಬಿಸಿನೆಸ್ ಲಾಜಿಕ್) ಅನ್ನು "ಹೇಗೆ ಮಾಡಬೇಕು" (ಲಾಗಿಂಗ್, ಕಾರ್ಯಕ್ಷಮತೆ ಮೇಲ್ವಿಚಾರಣೆ, ಇತ್ಯಾದಿ) ದಿಂದ ಪ್ರತ್ಯೇಕಿಸುತ್ತದೆ.
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."]
ಈ ಸೆಟಪ್ ನಿಮ್ಮ ಮಾದರಿ ಪ್ರಾಪರ್ಟಿಗಳಲ್ಲಿ ವ್ಯಾಲಿಡೇಶನ್ ನಿಯಮಗಳನ್ನು ಘೋಷಣಾತ್ಮಕವಾಗಿ ವ್ಯಾಖ್ಯಾನಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ, ನಿಮ್ಮ ಡೇಟಾ ಮಾದರಿಗಳನ್ನು ಅವುಗಳ ನಿರ್ಬಂಧಗಳ ವಿಷಯದಲ್ಲಿ ಸ್ವಯಂ-ವಿವರಣಾತ್ಮಕವಾಗಿಸುತ್ತದೆ.
ಉತ್ತಮ ಅಭ್ಯಾಸಗಳು ಮತ್ತು ಪರಿಗಣನೆಗಳು
ಡೆಕೋರೇಟರ್ಗಳು ಶಕ್ತಿಯುತವಾಗಿದ್ದರೂ, ಅವುಗಳನ್ನು ವಿವೇಚನೆಯಿಂದ ಬಳಸಬೇಕು. ಅವುಗಳನ್ನು ದುರುಪಯೋಗಪಡಿಸಿಕೊಳ್ಳುವುದು ಡೀಬಗ್ ಮಾಡಲು ಅಥವಾ ಅರ್ಥಮಾಡಿಕೊಳ್ಳಲು ಕಷ್ಟಕರವಾದ ಕೋಡ್ಗೆ ಕಾರಣವಾಗಬಹುದು.
ಡೆಕೋರೇಟರ್ಗಳನ್ನು ಯಾವಾಗ ಬಳಸಬೇಕು (ಮತ್ತು ಯಾವಾಗ ಬಳಸಬಾರದು)
- ಇದಕ್ಕಾಗಿ ಬಳಸಿ:
- ಕ್ರಾಸ್-ಕಟಿಂಗ್ ಕಾಳಜಿಗಳು: ಲಾಗಿಂಗ್, ಕ್ಯಾಶಿಂಗ್, ದೃಢೀಕರಣ, ವಹಿವಾಟು ನಿರ್ವಹಣೆ.
- ಮೆಟಾಡೇಟಾ ಘೋಷಣೆ: ORM ಗಳಿಗಾಗಿ ಸ್ಕೀಮಾವನ್ನು ವ್ಯಾಖ್ಯಾನಿಸುವುದು, ವ್ಯಾಲಿಡೇಶನ್ ನಿಯಮಗಳು, DI ಸಂರಚನೆ.
- ಫ್ರೇಮ್ವರ್ಕ್ ಏಕೀಕರಣ: ಮೆಟಾಡೇಟಾವನ್ನು ಬಳಸಿಕೊಳ್ಳುವ ಫ್ರೇಮ್ವರ್ಕ್ಗಳನ್ನು ನಿರ್ಮಿಸುವಾಗ ಅಥವಾ ಬಳಸುವಾಗ.
- ಬಾಯ್ಲರ್ಪ್ಲೇಟ್ ಕಡಿಮೆ ಮಾಡುವುದು: ಪುನರಾವರ್ತಿತ ಕೋಡ್ ಮಾದರಿಗಳನ್ನು ಅಮೂರ್ತಗೊಳಿಸುವುದು.
- ಇದಕ್ಕಾಗಿ ತಪ್ಪಿಸಿ:
- ಸರಳ ಫಂಕ್ಷನ್ ಕರೆಗಳು: ಒಂದು ಸರಳ ಫಂಕ್ಷನ್ ಕರೆಯು ಅದೇ ಫಲಿತಾಂಶವನ್ನು ಸ್ಪಷ್ಟವಾಗಿ ಸಾಧಿಸಬಹುದಾದರೆ, ಅದನ್ನು ಆದ್ಯತೆ ನೀಡಿ.
- ಬಿಸಿನೆಸ್ ಲಾಜಿಕ್: ಡೆಕೋರೇಟರ್ಗಳು ಕೋರ್ ಬಿಸಿನೆಸ್ ಲಾಜಿಕ್ ಅನ್ನು ವ್ಯಾಖ್ಯಾನಿಸುವುದಕ್ಕಿಂತ ಹೆಚ್ಚಾಗಿ ಹೆಚ್ಚಿಸಬೇಕು.
- ಅತಿಯಾದ ಸಂಕೀರ್ಣತೆ: ಡೆಕೋರೇಟರ್ ಬಳಸುವುದು ಕೋಡ್ ಅನ್ನು ಕಡಿಮೆ ಓದಬಲ್ಲ ಅಥವಾ ಪರೀಕ್ಷಿಸಲು ಕಷ್ಟಕರವಾಗಿಸಿದರೆ, ಪುನರ್ವಿಮರ್ಶಿಸಿ.
ಕಾರ್ಯಕ್ಷಮತೆಯ ಪರಿಣಾಮಗಳು
ಡೆಕೋರೇಟರ್ಗಳು ಕಂಪೈಲ್-ಟೈಮ್ನಲ್ಲಿ (ಅಥವಾ ಟ್ರಾನ್ಸ್ಪೈಲ್ ಮಾಡಿದರೆ ಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ ರನ್ಟೈಮ್ನಲ್ಲಿ ಡೆಫಿನಿಷನ್-ಟೈಮ್ನಲ್ಲಿ) ಕಾರ್ಯಗತಗೊಳ್ಳುತ್ತವೆ. ರೂಪಾಂತರ ಅಥವಾ ಮೆಟಾಡೇಟಾ ಸಂಗ್ರಹಣೆಯು ಕ್ಲಾಸ್/ಮೆಥಡ್ ಅನ್ನು ವ್ಯಾಖ್ಯಾನಿಸಿದಾಗ ಸಂಭವಿಸುತ್ತದೆ, ಪ್ರತಿ ಕರೆಯಲ್ಲಿ ಅಲ್ಲ. ಆದ್ದರಿಂದ, ಡೆಕೋರೇಟರ್ಗಳನ್ನು *ಅನ್ವಯಿಸುವ* ರನ್ಟೈಮ್ ಕಾರ್ಯಕ್ಷಮತೆಯ ಪರಿಣಾಮವು ಕನಿಷ್ಠವಾಗಿದೆ. ಆದಾಗ್ಯೂ, ನಿಮ್ಮ ಡೆಕೋರೇಟರ್ಗಳ *ಒಳಗಿನ ತರ್ಕ* ವು ಕಾರ್ಯಕ್ಷಮತೆಯ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರಬಹುದು, ವಿಶೇಷವಾಗಿ ಅವು ಪ್ರತಿ ಮೆಥಡ್ ಕರೆಯಲ್ಲಿ ದುಬಾರಿ ಕಾರ್ಯಾಚರಣೆಗಳನ್ನು ನಿರ್ವಹಿಸಿದರೆ (ಉದಾಹರಣೆಗೆ, ಮೆಥಡ್ ಡೆಕೋರೇಟರ್ನೊಳಗಿನ ಸಂಕೀರ್ಣ ಲೆಕ್ಕಾಚಾರಗಳು).
ನಿರ್ವಹಣೆ ಮತ್ತು ಓದುವಿಕೆ
ಡೆಕೋರೇಟರ್ಗಳು, ಸರಿಯಾಗಿ ಬಳಸಿದಾಗ, ಬಾಯ್ಲರ್ಪ್ಲೇಟ್ ಕೋಡ್ ಅನ್ನು ಮುಖ್ಯ ತರ್ಕದಿಂದ ಹೊರಗೆ ಸರಿಸುವ ಮೂಲಕ ಓದುವಿಕೆಯನ್ನು ಗಮನಾರ್ಹವಾಗಿ ಸುಧಾರಿಸಬಹುದು. ಆದಾಗ್ಯೂ, ಅವು ಸಂಕೀರ್ಣ, ಗುಪ್ತ ರೂಪಾಂತರಗಳನ್ನು ನಿರ್ವಹಿಸಿದರೆ, ಡೀಬಗ್ ಮಾಡುವುದು ಸವಾಲಾಗಬಹುದು. ನಿಮ್ಮ ಡೆಕೋರೇಟರ್ಗಳು ಉತ್ತಮವಾಗಿ ದಾಖಲಿಸಲ್ಪಟ್ಟಿವೆ ಮತ್ತು ಅವುಗಳ ನಡವಳಿಕೆಯು ಊಹಿಸಬಹುದಾಗಿದೆ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ.
ಪ್ರಾಯೋಗಿಕ ಸ್ಥಿತಿ ಮತ್ತು ಡೆಕೋರೇಟರ್ಗಳ ಭವಿಷ್ಯ
ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ ಡೆಕೋರೇಟರ್ಗಳು ಹಂತ 3 TC39 ಪ್ರಸ್ತಾಪವನ್ನು ಆಧರಿಸಿವೆ ಎಂದು ಪುನರುಚ್ಚರಿಸುವುದು ಮುಖ್ಯ. ಇದರರ್ಥ ನಿರ್ದಿಷ್ಟತೆಯು ಹೆಚ್ಚಾಗಿ ಸ್ಥಿರವಾಗಿದೆ ಆದರೆ ಅಧಿಕೃತ ECMAScript ಮಾನದಂಡದ ಭಾಗವಾಗುವ ಮೊದಲು ಇನ್ನೂ ಸಣ್ಣ ಬದಲಾವಣೆಗಳಿಗೆ ಒಳಗಾಗಬಹುದು. ಆಂಗುಲರ್ನಂತಹ ಫ್ರೇಮ್ವರ್ಕ್ಗಳು ಅವುಗಳನ್ನು ಅಳವಡಿಸಿಕೊಂಡಿವೆ, ಅವುಗಳ ಅಂತಿಮ ಪ್ರಮಾಣೀಕರಣದ ಮೇಲೆ ಬಾಜಿ ಕಟ್ಟಿವೆ. ಇದು ಒಂದು ನಿರ್ದಿಷ್ಟ ಮಟ್ಟದ ಅಪಾಯವನ್ನು ಸೂಚಿಸುತ್ತದೆ, ಆದರೂ ಅವುಗಳ ವ್ಯಾಪಕ ಅಳವಡಿಕೆಯನ್ನು ಗಮನಿಸಿದರೆ, ಗಮನಾರ್ಹ ಬ್ರೇಕಿಂಗ್ ಬದಲಾವಣೆಗಳು ಅಸಂಭವ.
TC39 ಪ್ರಸ್ತಾಪವು ವಿಕಸನಗೊಂಡಿದೆ. ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ನ ಪ್ರಸ್ತುತ ಅನುಷ್ಠಾನವು ಪ್ರಸ್ತಾಪದ ಹಳೆಯ ಆವೃತ್ತಿಯನ್ನು ಆಧರಿಸಿದೆ. "ಲೆಗಸಿ ಡೆಕೋರೇಟರ್ಗಳು" ಮತ್ತು "ಸ್ಟ್ಯಾಂಡರ್ಡ್ ಡೆಕೋರೇಟರ್ಗಳು" ನಡುವೆ ವ್ಯತ್ಯಾಸವಿದೆ. ಅಧಿಕೃತ ಮಾನದಂಡ ಬಂದಾಗ, ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ ತನ್ನ ಅನುಷ್ಠಾನವನ್ನು ನವೀಕರಿಸುವ ಸಾಧ್ಯತೆಯಿದೆ. ಹೆಚ್ಚಿನ ಡೆವಲಪರ್ಗಳಿಗೆ ಫ್ರೇಮ್ವರ್ಕ್ಗಳನ್ನು ಬಳಸುವಾಗ, ಈ ಪರಿವರ್ತನೆಯನ್ನು ಫ್ರೇಮ್ವರ್ಕ್ ಸ್ವತಃ ನಿರ್ವಹಿಸುತ್ತದೆ. ಲೈಬ್ರರಿ ಲೇಖಕರಿಗೆ, ಲೆಗಸಿ ಮತ್ತು ಭವಿಷ್ಯದ ಸ್ಟ್ಯಾಂಡರ್ಡ್ ಡೆಕೋರೇಟರ್ಗಳ ನಡುವಿನ ಸೂಕ್ಷ್ಮ ವ್ಯತ್ಯಾಸಗಳನ್ನು ಅರ್ಥಮಾಡಿಕೊಳ್ಳುವುದು ಅಗತ್ಯವಾಗಬಹುದು.
emitDecoratorMetadata
ಕಂಪೈಲರ್ ಆಯ್ಕೆ
ಈ ಆಯ್ಕೆಯು, tsconfig.json
ನಲ್ಲಿ true
ಗೆ ಹೊಂದಿಸಿದಾಗ, ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ ಕಂಪೈಲರ್ಗೆ ಕೆಲವು ಡಿಸೈನ್-ಟೈಮ್ ಟೈಪ್ ಮೆಟಾಡೇಟಾವನ್ನು ಸಂಕಲಿಸಿದ ಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ಗೆ ಹೊರಸೂಸಲು ಸೂಚಿಸುತ್ತದೆ. ಈ ಮೆಟಾಡೇಟಾವು ಕನ್ಸ್ಟ್ರಕ್ಟರ್ ಪ್ಯಾರಾಮೀಟರ್ಗಳ ಪ್ರಕಾರ (design:paramtypes
), ಮೆಥಡ್ಗಳ ರಿಟರ್ನ್ ಪ್ರಕಾರ (design:returntype
), ಮತ್ತು ಪ್ರಾಪರ್ಟಿಗಳ ಪ್ರಕಾರವನ್ನು (design:type
) ಒಳಗೊಂಡಿರುತ್ತದೆ.
ಈ ಹೊರಸೂಸಿದ ಮೆಟಾಡೇಟಾವು ಸ್ಟ್ಯಾಂಡರ್ಡ್ ಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ ರನ್ಟೈಮ್ನ ಭಾಗವಲ್ಲ. ಇದನ್ನು ಸಾಮಾನ್ಯವಾಗಿ reflect-metadata
ಪಾಲಿಫಿಲ್ ಸೇವಿಸುತ್ತದೆ, ಅದು ನಂತರ ಅದನ್ನು Reflect.getMetadata()
ಫಂಕ್ಷನ್ಗಳ ಮೂಲಕ ಪ್ರವೇಶಿಸುವಂತೆ ಮಾಡುತ್ತದೆ. ಡಿಪೆಂಡೆನ್ಸಿ ಇಂಜೆಕ್ಷನ್ನಂತಹ ಸುಧಾರಿತ ಮಾದರಿಗಳಿಗೆ ಇದು ಸಂಪೂರ್ಣವಾಗಿ ನಿರ್ಣಾಯಕವಾಗಿದೆ, ಅಲ್ಲಿ ಕಂಟೇನರ್ ಸ್ಪಷ್ಟ ಸಂರಚನೆಯಿಲ್ಲದೆ ಕ್ಲಾಸ್ಗೆ ಅಗತ್ಯವಿರುವ ಡಿಪೆಂಡೆನ್ಸಿಗಳ ಪ್ರಕಾರಗಳನ್ನು ತಿಳಿದುಕೊಳ್ಳಬೇಕು.
ಡೆಕೋರೇಟರ್ಗಳೊಂದಿಗೆ ಸುಧಾರಿತ ಮಾದರಿಗಳು
ಡೆಕೋರೇಟರ್ಗಳನ್ನು ಸಂಯೋಜಿಸಿ ಮತ್ತು ವಿಸ್ತರಿಸಿ ಇನ್ನಷ್ಟು ಅತ್ಯಾಧುನಿಕ ಮಾದರಿಗಳನ್ನು ನಿರ್ಮಿಸಬಹುದು.
1. ಡೆಕೋರೇಟರ್ಗಳನ್ನು ಅಲಂಕರಿಸುವುದು (ಹೈಯರ್-ಆರ್ಡರ್ ಡೆಕೋರೇಟರ್ಗಳು)
ಇತರ ಡೆಕೋರೇಟರ್ಗಳನ್ನು ಮಾರ್ಪಡಿಸುವ ಅಥವಾ ಸಂಯೋಜಿಸುವ ಡೆಕೋರೇಟರ್ಗಳನ್ನು ನೀವು ರಚಿಸಬಹುದು. ಇದು ಕಡಿಮೆ ಸಾಮಾನ್ಯವಾಗಿದೆ ಆದರೆ ಡೆಕೋರೇಟರ್ಗಳ ಕ್ರಿಯಾತ್ಮಕ ಸ್ವರೂಪವನ್ನು ಪ್ರದರ್ಶಿಸುತ್ತದೆ.
// A decorator that ensures a method is logged and also requires admin roles
function AdminAndLoggedMethod() {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
// Apply Authorization first (inner)
Authorization(["admin"])(target, propertyKey, descriptor);
// Then apply LogCall (outer)
LogCall(target, propertyKey, descriptor);
return descriptor; // Return the modified descriptor
};
}
class AdminPanel {
@AdminAndLoggedMethod()
deleteUserAccount(userId: string) {
console.log(`Deleting user account: ${userId}`);
return `User ${userId} deleted.`;
}
}
const adminPanel = new AdminPanel();
adminPanel.deleteUserAccount("user007");
/* Expected Output (assuming admin role):
[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 {
// These properties/methods are injected by the decorator
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
ಡೆಕೋರೇಟರ್ ಮೂಲ ಕನ್ಸ್ಟ್ರಕ್ಟರ್ಗಳಿಂದ ಮೆಥಡ್ಗಳು ಮತ್ತು ಪ್ರಾಪರ್ಟಿಗಳನ್ನು ವ್ಯುತ್ಪನ್ನ ಕ್ಲಾಸ್ನ ಪ್ರೊಟೊಟೈಪ್ಗೆ ಡೈನಾಮಿಕ್ ಆಗಿ ನಕಲಿಸುತ್ತದೆ, ಪರಿಣಾಮಕಾರಿಯಾಗಿ ಕಾರ್ಯಗಳನ್ನು "ಮಿಶ್ರಣ" ಮಾಡುತ್ತದೆ.
ತೀರ್ಮಾನ: ಆಧುನಿಕ ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ ಅಭಿವೃದ್ಧಿಯನ್ನು ಸಶಕ್ತಗೊಳಿಸುವುದು
ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ ಡೆಕೋರೇಟರ್ಗಳು ಒಂದು ಶಕ್ತಿಯುತ ಮತ್ತು ಅಭಿವ್ಯಕ್ತಿಶೀಲ ವೈಶಿಷ್ಟ್ಯವಾಗಿದ್ದು, ಇದು ಮೆಟಾಡೇಟಾ-ಚಾಲಿತ ಮತ್ತು ಆಸ್ಪೆಕ್ಟ್-ಓರಿಯೆಂಟೆಡ್ ಪ್ರೋಗ್ರಾಮಿಂಗ್ನ ಹೊಸ ಮಾದರಿಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ. ಅವು ಡೆವಲಪರ್ಗಳಿಗೆ ಕ್ಲಾಸ್ಗಳು, ಮೆಥಡ್ಗಳು, ಪ್ರಾಪರ್ಟಿಗಳು, ಅಕ್ಸೆಸರ್ಗಳು, ಮತ್ತು ಪ್ಯಾರಾಮೀಟರ್ಗಳಿಗೆ ಅವುಗಳ ಕೋರ್ ಲಾಜಿಕ್ ಅನ್ನು ಬದಲಾಯಿಸದೆ ವರ್ಧಿಸಲು, ಮಾರ್ಪಡಿಸಲು, ಮತ್ತು ಘೋಷಣಾತ್ಮಕ ನಡವಳಿಕೆಗಳನ್ನು ಸೇರಿಸಲು ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ. ಈ ಕಾಳಜಿಗಳ ಪ್ರತ್ಯೇಕತೆಯು ಸ್ವಚ್ಛ, ಹೆಚ್ಚು ನಿರ್ವಹಿಸಬಲ್ಲ, ಮತ್ತು ಹೆಚ್ಚು ಮರುಬಳಕೆ ಮಾಡಬಹುದಾದ ಕೋಡ್ಗೆ ಕಾರಣವಾಗುತ್ತದೆ.
ಡಿಪೆಂಡೆನ್ಸಿ ಇಂಜೆಕ್ಷನ್ ಅನ್ನು ಸರಳಗೊಳಿಸುವುದರಿಂದ ಮತ್ತು ದೃಢವಾದ ವ್ಯಾಲಿಡೇಶನ್ ವ್ಯವಸ್ಥೆಗಳನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸುವುದರಿಂದ ಹಿಡಿದು ಲಾಗಿಂಗ್ ಮತ್ತು ಕಾರ್ಯಕ್ಷಮತೆ ಮೇಲ್ವಿಚಾರಣೆಯಂತಹ ಕ್ರಾಸ್-ಕಟಿಂಗ್ ಕಾಳಜಿಗಳನ್ನು ಸೇರಿಸುವವರೆಗೆ, ಡೆಕೋರೇಟರ್ಗಳು ಅನೇಕ ಸಾಮಾನ್ಯ ಅಭಿವೃದ್ಧಿ ಸವಾಲುಗಳಿಗೆ ಒಂದು ಸೊಗಸಾದ ಪರಿಹಾರವನ್ನು ಒದಗಿಸುತ್ತವೆ. ಅವುಗಳ ಪ್ರಾಯೋಗಿಕ ಸ್ಥಿತಿಯು ಜಾಗೃತಿಯನ್ನು ಬಯಸುತ್ತದೆಯಾದರೂ, ಪ್ರಮುಖ ಫ್ರೇಮ್ವರ್ಕ್ಗಳಲ್ಲಿ ಅವುಗಳ ವ್ಯಾಪಕ ಅಳವಡಿಕೆಯು ಅವುಗಳ ಪ್ರಾಯೋಗಿಕ ಮೌಲ್ಯ ಮತ್ತು ಭವಿಷ್ಯದ ಪ್ರಸ್ತುತತೆಯನ್ನು ಸೂಚಿಸುತ್ತದೆ.
ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ ಡೆಕೋರೇಟರ್ಗಳಲ್ಲಿ ಪರಿಣತಿ ಹೊಂದುವ ಮೂಲಕ, ನಿಮ್ಮ ಆರ್ಸೆನಲ್ನಲ್ಲಿ ನೀವು ಒಂದು ಮಹತ್ವದ ಸಾಧನವನ್ನು ಪಡೆಯುತ್ತೀರಿ, ಇದು ನಿಮಗೆ ಹೆಚ್ಚು ದೃಢವಾದ, ಸ್ಕೇಲೆಬಲ್, ಮತ್ತು ಬುದ್ಧಿವಂತ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ನಿರ್ಮಿಸಲು ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ. ಅವುಗಳನ್ನು ಜವಾಬ್ದಾರಿಯುತವಾಗಿ ಅಳವಡಿಸಿಕೊಳ್ಳಿ, ಅವುಗಳ ಕಾರ್ಯವಿಧಾನವನ್ನು ಅರ್ಥಮಾಡಿಕೊಳ್ಳಿ, ಮತ್ತು ನಿಮ್ಮ ಟೈಪ್ಸ್ಕ್ರಿಪ್ಟ್ ಯೋಜನೆಗಳಲ್ಲಿ ಘೋಷಣಾತ್ಮಕ ಶಕ್ತಿಯ ಹೊಸ ಮಟ್ಟವನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಿ.