ગુજરાતી

મેટાડેટા પ્રોગ્રામિંગ, એસ્પેક્ટ-ઓરિએન્ટેડ પ્રોગ્રામિંગ અને ઘોષણાત્મક પેટર્ન સાથે કોડને વધારવા માટે ટાઇપસ્ક્રીપ્ટ ડેકોરેટર્સની શક્તિનું અન્વેષણ કરો. વૈશ્વિક ડેવલપર્સ માટે એક વ્યાપક માર્ગદર્શિકા.

ટાઇપસ્ક્રીપ્ટ ડેકોરેટર્સ: મજબૂત એપ્લિકેશન્સ માટે મેટાડેટા પ્રોગ્રામિંગ પેટર્ન્સમાં નિપુણતા મેળવો

આધુનિક સોફ્ટવેર ડેવલપમેન્ટના વિશાળ ક્ષેત્રમાં, સ્વચ્છ, સ્કેલેબલ અને વ્યવસ્થાપિત કોડબેઝ જાળવવું સર્વોપરી છે. ટાઇપસ્ક્રીપ્ટ, તેની શક્તિશાળી ટાઇપ સિસ્ટમ અને અદ્યતન સુવિધાઓ સાથે, ડેવલપર્સને આ પ્રાપ્ત કરવા માટે સાધનો પ્રદાન કરે છે. તેની સૌથી રસપ્રદ અને પરિવર્તનશીલ સુવિધાઓમાં ડેકોરેટર્સ છે. લખતી વખતે હજુ પણ એક પ્રાયોગિક સુવિધા (ECMAScript માટે સ્ટેજ 3 પ્રસ્તાવ) હોવા છતાં, ડેકોરેટર્સનો વ્યાપકપણે Angular અને 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
# 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 (ક્લાસ કન્સ્ટ્રક્ટર) લે છે. આ ડેકોરેટરના વર્તનના ગતિશીલ રૂપરેખાંકનને મંજૂરી આપે છે.

ટાઇપસ્ક્રીપ્ટમાં ડેકોરેટર્સના પ્રકારો

ટાઇપસ્ક્રીપ્ટ પાંચ વિશિષ્ટ પ્રકારના ડેકોરેટર્સને સપોર્ટ કરે છે, દરેક એક વિશિષ્ટ પ્રકારની ઘોષણા માટે લાગુ પડે છે. ડેકોરેટર ફંક્શનની સિગ્નેચર તે જે સંદર્ભમાં લાગુ કરવામાં આવે છે તેના આધારે બદલાય છે.

૧. ક્લાસ ડેકોરેટર્સ

ક્લાસ ડેકોરેટર્સ ક્લાસ ઘોષણાઓ પર લાગુ થાય છે. ડેકોરેટર ફંક્શન તેના એકમાત્ર આર્ગ્યુમેન્ટ તરીકે ક્લાસના કન્સ્ટ્રક્ટરને મેળવે છે. ક્લાસ ડેકોરેટર ક્લાસની વ્યાખ્યાનું નિરીક્ષણ કરી શકે છે, તેમાં ફેરફાર કરી શકે છે, અથવા તેને બદલી પણ શકે છે.

સિગ્નેચર:

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 ડેકોરેટર ક્લાસને સૈદ્ધાંતિક ડિપેન્ડન્સી ઇન્જેક્શન સિસ્ટમ દ્વારા શોધી શકાય તેવું બનાવે છે.

૨. મેથડ ડેકોરેટર્સ

મેથડ ડેકોરેટર્સ મેથડ ઘોષણાઓ પર લાગુ થાય છે. તેઓ ત્રણ આર્ગ્યુમેન્ટ્સ મેળવે છે: ટાર્ગેટ ઓબ્જેક્ટ (સ્ટેટિક સભ્યો માટે, કન્સ્ટ્રક્ટર ફંક્શન; ઇન્સ્ટન્સ સભ્યો માટે, ક્લાસનો પ્રોટોટાઇપ), મેથડનું નામ, અને મેથડનું પ્રોપર્ટી ડિસ્ક્રિપ્ટર.

સિગ્નેચર:

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) નું ક્લાસિક ઉદાહરણ છે.

૩. એક્સેસર ડેકોરેટર્સ

એક્સેસર ડેકોરેટર્સ એક્સેસર (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 ગેટરની ગણતરી માત્ર એક જ વાર ચાલે છે, અનુગામી કૉલ્સ કેશ કરેલ મૂલ્ય પરત કરે છે. આ પેટર્ન પ્રદર્શનને ઑપ્ટિમાઇઝ કરવા માટે ખૂબ જ ઉપયોગી છે જ્યાં પ્રોપર્ટી ઍક્સેસમાં ભારે ગણતરી અથવા બાહ્ય કૉલ્સ શામેલ હોય છે.

૪. પ્રોપર્ટી ડેકોરેટર્સ

પ્રોપર્ટી ડેકોરેટર્સ પ્રોપર્ટી ઘોષણાઓ પર લાગુ થાય છે. તેઓ બે આર્ગ્યુમેન્ટ્સ મેળવે છે: ટાર્ગેટ ઓબ્જેક્ટ (સ્ટેટિક સભ્યો માટે, કન્સ્ટ્રક્ટર ફંક્શન; ઇન્સ્ટન્સ સભ્યો માટે, ક્લાસનો પ્રોટોટાઇપ), અને પ્રોપર્ટીનું નામ.

સિગ્નેચર:

function PropertyDecorator(target: Object, propertyKey: string | symbol) { ... }

પરત મૂલ્ય:

પ્રોપર્ટી ડેકોરેટર્સ કોઈ મૂલ્ય પરત કરી શકતા નથી. તેમનો મુખ્ય ઉપયોગ પ્રોપર્ટી વિશે મેટાડેટાની નોંધણી કરવાનો છે. તેઓ ડેકોરેશન સમયે પ્રોપર્ટીના મૂલ્ય અથવા તેના ડિસ્ક્રિપ્ટરમાં સીધો ફેરફાર કરી શકતા નથી, કારણ કે જ્યારે પ્રોપર્ટી ડેકોરેટર્સ ચલાવવામાં આવે છે ત્યારે પ્રોપર્ટી માટેનું ડિસ્ક્રિપ્ટર હજી સંપૂર્ણ રીતે વ્યાખ્યાયિત થયેલું હોતું નથી.

ઉપયોગના કિસ્સાઓ:

પ્રોપર્ટી ડેકોરેટર ઉદાહરણ: આવશ્યક ફીલ્ડ માન્યતા

ચાલો આપણે પ્રોપર્ટીને "આવશ્યક" તરીકે ચિહ્નિત કરવા અને પછી તેને રનટાઇમ પર માન્ય કરવા માટે એક ડેકોરેટર બનાવીએ.

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 ફંક્શન પછી રનટાઇમ પર ઇન્સ્ટન્સને ચકાસવા માટે આ મેટાડેટાનો ઉપયોગ કરે છે. આ પેટર્ન માન્યતા લોજિકને ડેટા વ્યાખ્યાથી અલગ કરે છે, જે તેને પુનઃઉપયોગી અને સ્વચ્છ બનાવે છે.

૫. પેરામીટર ડેકોરેટર્સ

પેરામીટર ડેકોરેટર્સ ક્લાસ કન્સ્ટ્રક્ટર અથવા મેથડની અંદરના પેરામીટર્સ પર લાગુ થાય છે. તેઓ ત્રણ આર્ગ્યુમેન્ટ્સ મેળવે છે: ટાર્ગેટ ઓબ્જેક્ટ (સ્ટેટિક સભ્યો માટે, કન્સ્ટ્રક્ટર ફંક્શન; ઇન્સ્ટન્સ સભ્યો માટે, ક્લાસનો પ્રોટોટાઇપ), મેથડનું નામ (અથવા કન્સ્ટ્રક્ટર પેરામીટર્સ માટે undefined), અને ફંક્શનની પેરામીટર સૂચિમાં પેરામીટરનો ઓર્ડિનલ ઇન્ડેક્સ.

સિગ્નેચર:

function ParameterDecorator(target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) { ... }

પરત મૂલ્ય:

પેરામીટર ડેકોરેટર્સ કોઈ મૂલ્ય પરત કરી શકતા નથી. પ્રોપર્ટી ડેકોરેટર્સની જેમ, તેમની મુખ્ય ભૂમિકા પેરામીટર વિશે મેટાડેટા ઉમેરવાની છે.

ઉપયોગના કિસ્સાઓ:

પેરામીટર ડેકોરેટર ઉદાહરણ: વિનંતી ડેટા ઇન્જેક્ટ કરવો

ચાલો આપણે અનુકરણ કરીએ કે વેબ ફ્રેમવર્ક કેવી રીતે મેથડ પેરામીટરમાં વિશિષ્ટ ડેટા ઇન્જેક્ટ કરવા માટે પેરામીટર ડેકોરેટર્સનો ઉપયોગ કરી શકે છે, જેમ કે વિનંતીમાંથી વપરાશકર્તા ID.

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 એસ્પેક્ટની બહાર હશે. આ તફાવત લોગિંગ અથવા એરર હેન્ડલિંગ જેવી ક્રોસ-કટિંગ ચિંતાઓ માટે નિર્ણાયક છે, જ્યાં એક્ઝેક્યુશનનો ક્રમ વર્તનને નોંધપાત્ર રીતે અસર કરી શકે છે.

વિવિધ ટાર્ગેટ્સ માટે એક્ઝેક્યુશન ઓર્ડર

જ્યારે ક્લાસ, તેના સભ્યો અને પેરામીટર્સ બધામાં ડેકોરેટર્સ હોય, ત્યારે એક્ઝેક્યુશન ઓર્ડર સારી રીતે વ્યાખ્યાયિત છે:

  1. પેરામીટર ડેકોરેટર્સ પ્રથમ લાગુ થાય છે, દરેક પેરામીટર માટે, છેલ્લા પેરામીટરથી પ્રથમ સુધી.
  2. પછી, મેથડ, એક્સેસર, અથવા પ્રોપર્ટી ડેકોરેટર્સ દરેક સભ્ય માટે લાગુ થાય છે.
  3. અંતે, ક્લાસ ડેકોરેટર્સ ક્લાસ પર જ લાગુ થાય છે.

દરેક કેટેગરીમાં, સમાન ટાર્ગેટ પર બહુવિધ ડેકોરેટર્સ નીચેથી ઉપર (અથવા જમણેથી ડાબે) લાગુ થાય છે.

ઉદાહરણ: સંપૂર્ણ એક્ઝેક્યુશન ઓર્ડર

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 પોલીફિલ સાથે, મેટાડેટા-સંચાલિત પ્રોગ્રામિંગના નવા ક્ષેત્રને ખોલે છે. આ શક્તિશાળી ડિઝાઇન પેટર્ન્સ માટે પરવાનગી આપે છે જે બોઇલરપ્લેટ અને ક્રોસ-કટિંગ ચિંતાઓને દૂર કરે છે.

૧. ડિપેન્ડન્સી ઇન્જેક્શન (DI)

ડેકોરેટર્સનો સૌથી અગ્રણી ઉપયોગ ડિપેન્ડન્સી ઇન્જેક્શન ફ્રેમવર્કમાં છે (જેમ કે Angular નું @Injectable(), @Component(), વગેરે, અથવા NestJS નો DI નો વ્યાપક ઉપયોગ). ડેકોરેટર્સ તમને કન્સ્ટ્રક્ટર્સ અથવા પ્રોપર્ટીઝ પર સીધા જ ડિપેન્ડન્સી જાહેર કરવાની મંજૂરી આપે છે, જે ફ્રેમવર્કને આપમેળે યોગ્ય સેવાઓ ઇન્સ્ટેન્શિયેટ અને પ્રદાન કરવા સક્ષમ બનાવે છે.

ઉદાહરણ: સરળીકૃત સર્વિસ ઇન્જેક્શન

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 હોય) અહીં નિર્ણાયક છે.

૨. એસ્પેક્ટ-ઓરિએન્ટેડ પ્રોગ્રામિંગ (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 ડેકોરેટર લોગિંગની ક્રોસ-કટિંગ ચિંતાને સંભાળે છે.

૩. માન્યતા અને રૂપાંતરણ

ડેકોરેટર્સ પ્રોપર્ટીઝ પર સીધા માન્યતા નિયમો વ્યાખ્યાયિત કરવા અથવા સિરિયલાઇઝેશન/ડિસિરિયલાઇઝેશન દરમિયાન ડેટાને રૂપાંતરિત કરવા માટે અત્યંત ઉપયોગી છે.

ઉદાહરણ: પ્રોપર્ટી ડેકોરેટર્સ સાથે ડેટા માન્યતા

અગાઉનું @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 TC39 પ્રસ્તાવ પર આધારિત છે. આનો અર્થ એ છે કે સ્પષ્ટીકરણ મોટાભાગે સ્થિર છે પરંતુ સત્તાવાર ECMAScript ધોરણનો ભાગ બનતા પહેલા હજુ પણ નાના ફેરફારો થઈ શકે છે. Angular જેવા ફ્રેમવર્ક્સે તેમને અપનાવ્યા છે, તેમના અંતિમ માનકીકરણ પર દાવ લગાવ્યો છે. આ ચોક્કસ સ્તરનું જોખમ સૂચવે છે, જોકે તેમના વ્યાપક દત્તકને જોતાં, નોંધપાત્ર બ્રેકિંગ ફેરફારોની સંભાવના ઓછી છે.

TC39 પ્રસ્તાવ વિકસિત થયો છે. ટાઇપસ્ક્રીપ્ટનું વર્તમાન અમલીકરણ પ્રસ્તાવના જૂના સંસ્કરણ પર આધારિત છે. ત્યાં "લેગસી ડેકોરેટર્સ" વિ. "સ્ટાન્ડર્ડ ડેકોરેટર્સ" નો તફાવત છે. જ્યારે સત્તાવાર ધોરણ આવશે, ત્યારે ટાઇપસ્ક્રીપ્ટ સંભવતઃ તેના અમલીકરણને અપડેટ કરશે. મોટાભાગના ડેવલપર્સ જે ફ્રેમવર્કનો ઉપયોગ કરે છે, તેમના માટે આ સંક્રમણ ફ્રેમવર્ક દ્વારા જ સંચાલિત કરવામાં આવશે. લાઇબ્રેરી લેખકો માટે, લેગસી અને ભવિષ્યના સ્ટાન્ડર્ડ ડેકોરેટર્સ વચ્ચેના સૂક્ષ્મ તફાવતોને સમજવું જરૂરી બની શકે છે.

emitDecoratorMetadata કમ્પાઇલર વિકલ્પ

આ વિકલ્પ, જ્યારે tsconfig.json માં true પર સેટ હોય, ત્યારે ટાઇપસ્ક્રીપ્ટ કમ્પાઇલરને અમુક ડિઝાઇન-ટાઇમ પ્રકાર મેટાડેટાને સંકલિત જાવાસ્ક્રીપ્ટમાં ઉત્સર્જિત કરવા સૂચના આપે છે. આ મેટાડેટામાં કન્સ્ટ્રક્ટર પેરામીટર્સનો પ્રકાર (design:paramtypes), મેથડ્સનો રિટર્ન પ્રકાર (design:returntype), અને પ્રોપર્ટીઝનો પ્રકાર (design:type) શામેલ છે.

આ ઉત્સર્જિત મેટાડેટા સ્ટાન્ડર્ડ જાવાસ્ક્રીપ્ટ રનટાઇમનો ભાગ નથી. તે સામાન્ય રીતે reflect-metadata પોલીફિલ દ્વારા ઉપયોગમાં લેવાય છે, જે પછી તેને Reflect.getMetadata() ફંક્શન્સ દ્વારા સુલભ બનાવે છે. આ ડિપેન્ડન્સી ઇન્જેક્શન જેવા અદ્યતન પેટર્ન્સ માટે એકદમ નિર્ણાયક છે, જ્યાં કન્ટેનરને સ્પષ્ટ રૂપરેખાંકન વિના ક્લાસને કયા પ્રકારની ડિપેન્ડન્સીની જરૂર છે તે જાણવાની જરૂર છે.

ડેકોરેટર્સ સાથે એડવાન્સ્ડ પેટર્ન્સ

ડેકોરેટર્સને વધુ જટિલ પેટર્ન્સ બનાવવા માટે સંયુક્ત અને વિસ્તૃત કરી શકાય છે.

૧. ડેકોરેટર્સને ડેકોરેટ કરવું (હાયર-ઓર્ડર ડેકોરેટર્સ)

તમે એવા ડેકોરેટર્સ બનાવી શકો છો જે અન્ય ડેકોરેટર્સમાં ફેરફાર કરે છે અથવા કંપોઝ કરે છે. આ ઓછું સામાન્ય છે પરંતુ ડેકોરેટર્સની કાર્યાત્મક પ્રકૃતિ દર્શાવે છે.

// 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 એ એક ફેક્ટરી છે જે ડેકોરેટર પરત કરે છે, અને તે ડેકોરેટરની અંદર, તે બે અન્ય ડેકોરેટર્સ લાગુ કરે છે. આ પેટર્ન જટિલ ડેકોરેટર કમ્પોઝિશનને સમાવી શકે છે.

૨. મિક્સિન્સ માટે ડેકોરેટર્સનો ઉપયોગ

જ્યારે ટાઇપસ્ક્રીપ્ટ મિક્સિન્સ લાગુ કરવાની અન્ય રીતો પ્રદાન કરે છે, ડેકોરેટર્સનો ઉપયોગ ઘોષણાત્મક રીતે ક્લાસમાં ક્ષમતાઓ ઇન્જેક્ટ કરવા માટે થઈ શકે છે.

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 ડેકોરેટર ગતિશીલ રીતે બેઝ કન્સ્ટ્રક્ટર્સમાંથી મેથડ્સ અને પ્રોપર્ટીઝને ડેરિવેડ ક્લાસના પ્રોટોટાઇપ પર કૉપિ કરે છે, અસરકારક રીતે કાર્યક્ષમતાઓને "મિક્સ ઇન" કરે છે.

નિષ્કર્ષ: આધુનિક ટાઇપસ્ક્રીપ્ટ ડેવલપમેન્ટને સશક્ત બનાવવું

ટાઇપસ્ક્રીપ્ટ ડેકોરેટર્સ એક શક્તિશાળી અને અભિવ્યક્ત સુવિધા છે જે મેટાડેટા-સંચાલિત અને એસ્પેક્ટ-ઓરિએન્ટેડ પ્રોગ્રામિંગના નવા દાખલાને સક્ષમ કરે છે. તેઓ ડેવલપર્સને તેમના મુખ્ય લોજિકમાં ફેરફાર કર્યા વિના ક્લાસ, મેથડ્સ, પ્રોપર્ટીઝ, એક્સેસર્સ અને પેરામીટર્સમાં ઘોષણાત્મક વર્તન ઉમેરવા, સંશોધિત કરવા અને વધારવાની મંજૂરી આપે છે. આ ચિંતાઓના વિભાજનથી સ્વચ્છ, વધુ જાળવણીક્ષમ અને અત્યંત પુનઃઉપયોગી કોડ તરફ દોરી જાય છે.

ડિપેન્ડન્સી ઇન્જેક્શનને સરળ બનાવવાથી અને મજબૂત માન્યતા પ્રણાલીઓ લાગુ કરવાથી લઈને લોગિંગ અને પર્ફોર્મન્સ મોનિટરિંગ જેવી ક્રોસ-કટિંગ ચિંતાઓ ઉમેરવા સુધી, ડેકોરેટર્સ ઘણી સામાન્ય વિકાસ પડકારો માટે એક ભવ્ય ઉકેલ પૂરો પાડે છે. જ્યારે તેમની પ્રાયોગિક સ્થિતિ જાગૃતિની ખાતરી આપે છે, ત્યારે મુખ્ય ફ્રેમવર્કમાં તેમનો વ્યાપક દત્તક તેમના વ્યવહારુ મૂલ્ય અને ભવિષ્યની સુસંગતતા દર્શાવે છે.

ટાઇપસ્ક્રીપ્ટ ડેકોરેટર્સમાં નિપુણતા મેળવીને, તમે તમારા શસ્ત્રાગારમાં એક મહત્વપૂર્ણ સાધન મેળવો છો, જે તમને વધુ મજબૂત, સ્કેલેબલ અને બુદ્ધિશાળી એપ્લિકેશન્સ બનાવવામાં સક્ષમ બનાવે છે. તેમને જવાબદારીપૂર્વક અપનાવો, તેમની મિકેનિક્સને સમજો, અને તમારા ટાઇપસ્ક્રીપ્ટ પ્રોજેક્ટ્સમાં ઘોષણાત્મક શક્તિના નવા સ્તરને અનલૉક કરો.