فارسی

دکوراتورهای تایپ‌اسکریپت را کاوش کنید: یک ویژگی قدرتمند فرابرنامه‌نویسی برای بهبود ساختار، قابلیت استفاده مجدد و نگهداری کد. با مثال‌های عملی، نحوه استفاده مؤثر از آنها را بیاموزید.

دکوراتورهای تایپ‌اسکریپت: آزادسازی قدرت فرابرنامه‌نویسی

دکوراتورهای تایپ‌اسکریپت روشی قدرتمند و زیبا برای بهبود کد شما با قابلیت‌های فرابرنامه‌نویسی فراهم می‌کنند. آنها مکانیزمی برای تغییر و توسعه کلاس‌ها، متدها، خصوصیات و پارامترها در زمان طراحی ارائه می‌دهند که به شما اجازه می‌دهد رفتار و انوتیشن‌ها را بدون تغییر منطق اصلی کد خود تزریق کنید. این پست وبلاگ به پیچیدگی‌های دکوراتورهای تایپ‌اسکریپت می‌پردازد و راهنمای جامعی برای توسعه‌دهندگان در تمام سطوح ارائه می‌دهد. ما بررسی خواهیم کرد که دکوراتورها چه هستند، چگونه کار می‌کنند، انواع مختلف موجود، مثال‌های عملی و بهترین شیوه‌ها برای استفاده مؤثر از آنها. چه تازه‌کار در تایپ‌اسکریپت باشید و چه یک توسعه‌دهنده با تجربه، این راهنما شما را به دانشی مجهز می‌کند تا از دکوراتورها برای کدی تمیزتر، قابل نگهداری‌تر و گویاتر استفاده کنید.

دکوراتورهای تایپ‌اسکریپت چه هستند؟

در هسته خود، دکوراتورهای تایپ‌اسکریپت نوعی فرابرنامه‌نویسی هستند. آنها اساساً توابعی هستند که یک یا چند آرگومان (معمولاً چیزی که دکوریت می‌شود، مانند کلاس، متد، خصوصیت یا پارامتر) را می‌گیرند و می‌توانند آن را تغییر دهند یا عملکرد جدیدی به آن اضافه کنند. آنها را مانند انوتیشن‌ها یا ویژگی‌هایی در نظر بگیرید که به کد خود متصل می‌کنید. این انوتیشن‌ها می‌توانند برای ارائه فراداده در مورد کد یا تغییر رفتار آن استفاده شوند.

دکوراتورها با استفاده از نماد `@` و به دنبال آن یک فراخوانی تابع (مثلاً `@decoratorName()`) تعریف می‌شوند. سپس تابع دکوراتور در مرحله زمان طراحی برنامه شما اجرا خواهد شد.

دکوراتورها از ویژگی‌های مشابه در زبان‌هایی مانند جاوا، سی‌شارپ و پایتون الهام گرفته‌اند. آنها روشی برای جداسازی دغدغه‌ها (separation of concerns) و ترویج قابلیت استفاده مجدد کد با تمیز نگه داشتن منطق اصلی و متمرکز کردن فراداده یا جنبه‌های اصلاحی در یک مکان اختصاصی ارائه می‌دهند.

دکوراتورها چگونه کار می‌کنند

کامپایلر تایپ‌اسکریپت دکوراتورها را به توابعی تبدیل می‌کند که در زمان طراحی فراخوانی می‌شوند. آرگومان‌های دقیقی که به تابع دکوراتور ارسال می‌شوند به نوع دکوراتور مورد استفاده (کلاس، متد، خصوصیت یا پارامتر) بستگی دارد. بیایید انواع مختلف دکوراتورها و آرگومان‌های مربوط به آنها را بررسی کنیم:

درک این امضاهای آرگومان برای نوشتن دکوراتورهای مؤثر بسیار مهم است.

انواع دکوراتورها

تایپ‌اسکریپت از چندین نوع دکوراتور پشتیبانی می‌کند که هر کدام هدف خاصی را دنبال می‌کنند:

مثال‌های عملی

بیایید چند مثال عملی را بررسی کنیم تا نحوه استفاده از دکوراتورها در تایپ‌اسکریپت را نشان دهیم.

مثال دکوراتور کلاس: افزودن مهر زمانی

تصور کنید می‌خواهید یک مهر زمانی به هر نمونه از یک کلاس اضافه کنید. برای این کار می‌توانید از یک دکوراتور کلاس استفاده کنید:


function addTimestamp<T extends { new(...args: any[]): {} }>(constructor: T) {
  return class extends constructor {
    timestamp = Date.now();
  };
}

@addTimestamp
class MyClass {
  constructor() {
    console.log('MyClass created');
  }
}

const instance = new MyClass();
console.log(instance.timestamp); // Output: a timestamp

در این مثال، دکوراتور `addTimestamp` یک خصوصیت `timestamp` به نمونه کلاس اضافه می‌کند. این کار اطلاعات مفیدی برای دیباگ کردن یا ردگیری فراهم می‌کند بدون اینکه تعریف اصلی کلاس را مستقیماً تغییر دهد.

مثال دکوراتور متد: لاگ کردن فراخوانی متدها

می‌توانید از یک دکوراتور متد برای لاگ کردن فراخوانی متدها و آرگومان‌های آنها استفاده کنید:


function logMethod(target: any, key: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`[LOG] Method ${key} called with arguments:`, args);
    const result = originalMethod.apply(this, args);
    console.log(`[LOG] Method ${key} returned:`, result);
    return result;
  };

  return descriptor;
}

class Greeter {
  @logMethod
  greet(message: string): string {
    return `Hello, ${message}!`;
  }
}

const greeter = new Greeter();
greeter.greet('World');
// Output:
// [LOG] Method greet called with arguments: [ 'World' ]
// [LOG] Method greet returned: Hello, World!

این مثال هر بار که متد `greet` فراخوانی می‌شود، به همراه آرگومان‌ها و مقدار بازگشتی آن را لاگ می‌کند. این برای دیباگ کردن و نظارت در برنامه‌های پیچیده‌تر بسیار مفید است.

مثال دکوراتور خصوصیت: افزودن اعتبارسنجی

در اینجا مثالی از یک دکوراتور خصوصیت است که اعتبارسنجی اولیه را اضافه می‌کند:


function validate(target: any, key: string) {
  let value: any;

  const getter = function () {
    return value;
  };

  const setter = function (newValue: any) {
    if (typeof newValue !== 'number') {
      console.warn(`[WARN] Invalid property value: ${key}. Expected a number.`);
      return;
    }
    value = newValue;
  };

  Object.defineProperty(target, key, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true,
  });
}

class Person {
  @validate
  age: number; //  <- Property with validation
}

const person = new Person();
person.age = 'abc'; // Logs a warning
person.age = 30;   // Sets the value
console.log(person.age); // Output: 30

در این دکوراتور `validate`، ما بررسی می‌کنیم که آیا مقدار اختصاص داده شده یک عدد است یا خیر. اگر نباشد، یک هشدار لاگ می‌کنیم. این یک مثال ساده است اما نشان می‌دهد چگونه می‌توان از دکوراتورها برای اعمال یکپارچگی داده‌ها استفاده کرد.

مثال دکوراتور پارامتر: تزریق وابستگی (ساده‌شده)

در حالی که فریم‌ورک‌های تزریق وابستگی کامل اغلب از مکانیزم‌های پیچیده‌تری استفاده می‌کنند، دکوراتورها همچنین می‌توانند برای علامت‌گذاری پارامترها برای تزریق استفاده شوند. این مثال یک تصویر ساده‌شده است:


// This is a simplification and doesn't handle actual injection.  Real DI is more complex.
function Inject(service: any) {
  return function (target: any, propertyKey: string | symbol, parameterIndex: number) {
    // Store the service somewhere (e.g., in a static property or a map)
    if (!target.injectedServices) {
      target.injectedServices = {};
    }
    target.injectedServices[parameterIndex] = service;
  };
}

class MyService {
  doSomething() { /* ... */ }
}

class MyComponent {
  constructor(@Inject(MyService) private myService: MyService) {
    // In a real system, the DI container would resolve 'myService' here.
    console.log('MyComponent constructed with:', myService.constructor.name); //Example
  }
}

const component = new MyComponent(new MyService());  // Injecting the service (simplified).

دکوراتور `Inject` یک پارامتر را به عنوان نیازمند یک سرویس علامت‌گذاری می‌کند. این مثال نشان می‌دهد که چگونه یک دکوراتور می‌تواند پارامترهای نیازمند تزریق وابستگی را شناسایی کند (اما یک فریم‌ورک واقعی نیاز به مدیریت حل و فصل سرویس‌ها دارد).

مزایای استفاده از دکوراتورها

بهترین شیوه‌ها برای استفاده از دکوراتورها

مفاهیم پیشرفته

فکتوری‌های دکوراتور (Decorator Factories)

فکتوری‌های دکوراتور توابعی هستند که توابع دکوراتور را برمی‌گردانند. این به شما امکان می‌دهد آرگومان‌هایی را به دکوراتورهای خود ارسال کنید و آنها را انعطاف‌پذیرتر و قابل تنظیم‌تر کنید. به عنوان مثال، می‌توانید یک فکتوری دکوراتور اعتبارسنجی ایجاد کنید که به شما امکان می‌دهد قوانین اعتبارسنجی را مشخص کنید:


function validate(minLength: number) {
  return function (target: any, key: string) {
    let value: string;

    const getter = function () {
      return value;
    };

    const setter = function (newValue: string) {
      if (typeof newValue !== 'string') {
        console.warn(`[WARN] Invalid property value: ${key}. Expected a string.`);
        return;
      }
      if (newValue.length < minLength) {
        console.warn(`[WARN] ${key} must be at least ${minLength} characters long.`);
        return;
      }
      value = newValue;
    };

    Object.defineProperty(target, key, {
      get: getter,
      set: setter,
      enumerable: true,
      configurable: true,
    });
  };
}

class Person {
  @validate(3) // Validate with minimum length of 3
  name: string;
}

const person = new Person();
person.name = 'Jo';
console.log(person.name); // Logs a warning, sets value.
person.name = 'John';
console.log(person.name); // Output: John

فکتوری‌های دکوراتور، دکوراتورها را بسیار سازگارتر می‌کنند.

ترکیب دکوراتورها

شما می‌توانید چندین دکوراتور را روی یک عنصر اعمال کنید. ترتیبی که آنها اعمال می‌شوند گاهی اوقات می‌تواند مهم باشد. ترتیب از پایین به بالا (همانطور که نوشته شده) است. برای مثال:


function first() {
  console.log('first(): factory evaluated');
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log('first(): called');
  }
}

function second() {
  console.log('second(): factory evaluated');
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log('second(): called');
  }
}

class ExampleClass {
  @first()
  @second()
  method() {}
}

// Output:
// second(): factory evaluated
// first(): factory evaluated
// second(): called
// first(): called

توجه داشته باشید که توابع فکتوری به ترتیبی که ظاهر می‌شوند ارزیابی می‌شوند، اما توابع دکوراتور به ترتیب معکوس فراخوانی می‌شوند. اگر دکوراتورهای شما به یکدیگر وابسته هستند، این ترتیب را درک کنید.

دکوراتورها و بازتاب فراداده (Metadata Reflection)

دکوراتورها می‌توانند با بازتاب فراداده (به عنوان مثال، با استفاده از کتابخانه‌هایی مانند `reflect-metadata`) دست به دست هم دهند تا رفتار پویاتری به دست آورند. این به شما امکان می‌دهد، به عنوان مثال، اطلاعات مربوط به عناصر دکوریت شده را در زمان اجرا ذخیره و بازیابی کنید. این به ویژه در فریم‌ورک‌ها و سیستم‌های تزریق وابستگی مفید است. دکوراتورها می‌توانند کلاس‌ها یا متدها را با فراداده انوتیت کنند و سپس از بازتاب برای کشف و استفاده از آن فراداده استفاده شود.

دکوراتورها در فریم‌ورک‌ها و کتابخانه‌های محبوب

دکوراتورها به بخش‌های جدایی‌ناپذیر بسیاری از فریم‌ورک‌ها و کتابخانه‌های مدرن جاوااسکریپت تبدیل شده‌اند. دانستن کاربرد آنها به شما کمک می‌کند تا معماری فریم‌ورک و نحوه ساده‌سازی وظایف مختلف را درک کنید.

این فریم‌ورک‌ها و کتابخانه‌ها نشان می‌دهند که چگونه دکوراتورها سازماندهی کد را بهبود می‌بخشند، وظایف مشترک را ساده می‌کنند و قابلیت نگهداری را در برنامه‌های دنیای واقعی ترویج می‌دهند.

چالش‌ها و ملاحظات

نتیجه‌گیری

دکوراتورهای تایپ‌اسکریپت یک ویژگی قدرتمند فرابرنامه‌نویسی هستند که می‌توانند به طور قابل توجهی ساختار، قابلیت استفاده مجدد و قابلیت نگهداری کد شما را بهبود بخشند. با درک انواع مختلف دکوراتورها، نحوه کار آنها و بهترین شیوه‌ها برای استفاده از آنها، می‌توانید از آنها برای ایجاد برنامه‌های تمیزتر، گویاتر و کارآمدتر استفاده کنید. چه در حال ساخت یک برنامه ساده باشید و چه یک سیستم پیچیده در سطح سازمانی، دکوراتورها ابزار ارزشمندی برای بهبود گردش کار توسعه شما فراهم می‌کنند. پذیرش دکوراتورها امکان بهبود قابل توجهی در کیفیت کد را فراهم می‌کند. با درک اینکه چگونه دکوراتورها در فریم‌ورک‌های محبوبی مانند انگولار و نست‌جی‌اس ادغام می‌شوند، توسعه‌دهندگان می‌توانند از پتانسیل کامل آنها برای ساخت برنامه‌های مقیاس‌پذیر، قابل نگهداری و قوی بهره‌مند شوند. نکته کلیدی درک هدف آنها و نحوه اعمال آنها در زمینه‌های مناسب است، تا اطمینان حاصل شود که مزایا بر هرگونه اشکال احتمالی برتری دارد.

با پیاده‌سازی مؤثر دکوراتورها، می‌توانید کد خود را با ساختار، قابلیت نگهداری و کارایی بیشتر بهبود بخشید. این راهنما یک نمای کلی جامع از نحوه استفاده از دکوراتورهای تایپ‌اسکریپت ارائه می‌دهد. با این دانش، شما برای ایجاد کدهای تایپ‌اسکریپت بهتر و قابل نگهداری‌تر توانمند شده‌اید. پیش بروید و دکوریت کنید!