آشنایی با الگوی Module Pattern در جاوا اسکریپت

16 بهمن 1398
آشنایی با الگوی Module pattern در جاوا اسکریپت

با سلام خدمت شما همراهان گرامی روکسو. در این مقاله ی کوتاه قصد داریم با الگوها یا pattern های برنامه نویسی در جاوا اسکریپت آشنا شویم. الگوهای برنامه نویسی راه حل ها یا کدهایی با قابلیت استفاده ی چند باره هستند که برای از بین بردن مشکلات برنامه نویسی استفاده می شوند. تمام زبان های برنامه نویسی الگوهای مختلفی برای خودشان دارند و در واقع نوعی قالب یا template (روشی برای کدنویسی) برای برنامه نویسی هستند و با design (طراحی) های شیء گرا و رویه ای و... کاملا فرق دارند. در این جلسه می خواهیم با Module Pattern و همچنین نسخه ای از آن به نام Revealing Module pattern آشنا شویم. در ابتدای بحث باید تاکید کنم که در نسخه ی ES6 از جاوا اسکریپت قابلیت Module ها اضافه شده است و فعلا مورد بحث ما نیست. بحث امروز ما مربوط به الگوی Module است که در ES5 نوشته شده و به ما اجازه می دهد کدهای خودمان را به قسمت های کوچک تر تقسیم کنیم. در ابتدای کار یک فایل ساده ی index.html داریم:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>JavaScript Sandbox: Patterns</title>
</head>

<body>
  <h1>JavaScript Sandbox: Patterns</h1>

  <script src="app.js"></script>
</body>

</html>

ساختار کلی کار ما در این الگو، بر اساس IIFE ها است (Immediately Invoked Function Expression).اگر با IIFE ها آشنایی ندارید جای نگرانی نیست. آن ها توابعی هستند که به فراخوانی نیازی ندارند و به محض خوانده شدن کد اجرا می شوند:

(function() {
  // Declare private vars and functions

  return {
    // Declare public var and functions
  }
})();

نحوه ی نوشتن یک IIFE را در کد بالا می بینید: یک تابع درون پرانتز – البته بدون قسمت return). در الگوی Module توابع و متغیر های خصوصی را در قسمت اول (با کامنت مشخص کرده ام) قرار می دهیم. منظورم از خصوصی این است که از بیرون این تابع نمی توان به آن ها دسترسی داشت. سپس در قسمت return توابع و متغیر های عمومی را قرار می دهیم. کل الگوی Module به شکل بالا است.

برای مثال بیایید یک ماژول مربوط به UI را تعریف کنیم تا مسئولیت تغییرات در UI را بر عهده داشته باشد:

// STANDARD MODULE PATTERN
const UICtrl = (function() {
  let text = 'Hello World';

  const changeText = function() {
    const element = document.querySelector('h1');
    element.textContent = text;
  }

  return {
    callChangeText: function() {
      changeText();
      console.log(text);
    }
  }
})();

باید این کد را بررسی کنیم. در قسمت اول (که خصوصی بود) متغیری به نام text دارم و سپس تابع دیگری به نام changeText را دارم که تگ h1 صفحه ی HTML را گرفته و سپس محتوای آن را به محتوای متغیر text تغییر می دهد. همانطور که گفتم این قسمت private است بنابراین برای دسترسی به آن باید حتما آن را در قسمت دوم return کنیم. من برای قسمت return یک خصوصیت تعریف کرده ام که حاوی یک تابع است و آن تابع خودش تابع chageText را صدا می زند. بدین صورت می توانیم به مقادیر خصوصی دسترسی پیدا کنیم.

حالا می توانیم پایین تر از این ماژول و خارج از آن، از متد chageText استفاده کنیم و تگ h1 را تغییر بدهیم:

UICtrl.callChangeText();

با ذخیره کردن و اجرای کد بالا تگ h1 به Hello World تغییر می کند و البته آن را در قسمت کنسول مرورگر نیز نمایش داده می شود چرا که در تعریف تابع چنین کاری کردیم (در قسمت return). حالا اگر بخواهیم مستقیما از متد changeText با خطا روبرو می شویم:

UICtrl.changeText();
console.log(UICtrl.text);

هر دو کد بالا باعث توقف برنامه و ایجاد یک خطای جدید خواهند شد چرا که یک تابع و یک متغیر خصوصی هستند. این الگوی Module است اما حالا نوبت به نسخه ی خاصی از الگوی Module به نام Revealing Module Pattern است. تنها تفاوت آن با خود الگوی Module این است که در شیء برگردانده شده به جای تعریف یک تابع جدید (مثل تابع callChangeText در کد بالا) توابع خصوصی خودمان را به آن شیء برگردانده شده متصل کنیم.

مثال زیر یک مثال خوب از Revealing Module Pattern است:

// REVEALING MODULE PATTERN
const ItemCtrl = (function() {
  let data = [];

  function add(item) {
    data.push(item);
    console.log('Item Added....');
  }

  function get(id) {
    return data.find(item => {
      return item.id === id;
    });
  }

  return {
    add: add,
    get: get
  }
})();

این ماژول قرار است مدیریت آیتم های یک آرایه را بر عهده داشته باشد. همانطور که می بینید تابعی به نام add داریم که آیتم های جدیدی را به آرایه ی data وارد می کند. همچنین تابع دیگری به نام get داریم که id هر آیتم را گرفته و اگر آن را در data پیدا کند، به شما برمی گرداند. این قسمت، بخش private یا خصوصی ما بود و حالا باید قسمت public خود را تکمیل کنیم. اینجاست که تفاوت بین دو نسخه ی الگوی Module را مشاهده می کنیم. همانطور که می بینید به جای تعریف کردن یک تابع جدید، خود توابع private را به خصوصیاتی منسوب کرده ایم و سپس شیء را برگردانده ایم. برای تست کردن این کد می توان گفت:

ItemCtrl.add({id: 1, name: 'John'});
ItemCtrl.add({id: 2, name: 'Mark'});
console.log(ItemCtrl.get(2));

نتیجه ی این کد به راحتی در کنسول مرورگر قابل مشاهده است:

نتیجه ی دستور console.log
نتیجه ی دستور console.log

به دلیل راحت تر بودن کار با Revealing Module Pattern، اکثر توسعه دهندگان استفاده از آن را ترجیح می دهند اما الگوی استاندارد Module (روش اول) به شما اجازه می دهد علاوه بر دسترسی به تابع کار های دیگری نیز انجام دهید (مثلا ما یک دستور console.log را نیز به آن اضافه کرده بودیم که در تابع اصلی وجود نداشت) بنابراین در نهایت استفاده از هر کدام آن ها به نیاز شما بستگی دارد.

نویسنده شوید
دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

hasan hasani
28 مرداد 1400
سلام مهندس - ممنون از مقاله مفیدتون من یه جا تابع Revealing دیدم که پشت کلمه فانشن، علامت نقیض بود- این چه معنایی داره؟ function!

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.