برنامه نویسی فانکشنال چیست؟

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

برنامه نویسی فانکشنال چیست؟

به زبان ساده، برنامه نویسی فانکشنال یک سبک توسعه نرم‌افزار است که تأکید عمده‌ای بر استفاده از توابع دارد. ممکن است بگویید “من روزانه از توابع استفاده می‌کنم، پس تفاوت چیست؟” خوب، فقط استفاده از توابع نیست که به نتیجه برسد. هدف این است که تجزیه کنترل جریان و عملکرد داده‌ها با توابع به منظور جلوگیری از عوارض جانبی و کاهش جهش حالت‌ها در برنامه شما باشد.

کنترل جریان داده

برای درک معنای کنترل جریان‌های انتزاعی، ابتدا باید مدلی از برنامه نویسی را به نام برنامه نویسی ضروری یا رویه‌ای بررسی کنیم. برنامه نویسی Imperative یک برنامه کامپیوتری را فقط دنباله‌ای از دستورات بالا به پایین می‌داند که برای محاسبه نتیجه، وضعیت سیستم را تغییر می‌دهد.

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

var array = [۰, ۱, ۲, ۳, ۴, ۵, ۶, ۷, ۸, ۹];
for(let i = ۰; i < array.length; i++) {
    array[i]= Math.pow(array[i], ۲);
}
array;
//-> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

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

برنامه نویسی اعلامی ، توصیف برنامه را از ارزیابی جدا می کند. این برنامه بر استفاده از عبارات برای توصیف منطق یک برنامه متمرکز است بدون اینکه لزوماً جریان کنترل یا تغییرات حالت آن مشخص شود.

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

شما فقط باید نگران اعمال درست در هر عنصر و کنترل حلقه در سایر قسمت‌های سیستم باشید. به عنوان مثال، اجازه دادن به ()Array.map که بیشترین کار سنگین را انجام دهد، یک رویکرد کاربردی‌تر محسوب می‌شود.

[۰, ۱, ۲, ۳, ۴, ۵, ۶, ۷, ۸, ۹].map(num => Math.pow(num, ۲));
//-> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

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

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

از عوارض جانبی خودداری کنید

برنامه نویسی فانکشنال بر این فرض استوار است که شما هنگام ساختن برنامه‌های خود از توابع خالص (توابع بدون هیچگونه عوارض جانبی) به عنوان بلوک‌های اصلی استفاده می‌کنید. توابع خالص دارای ویژگی‌های زیر است:

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

هر تابعی که این شرایط را برآورده نکند “نا خالص” است. تابع زیر را در نظر بگیرید:

var counter = ۰;
function increment() {
    return ++counter;
}

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

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

جهش حالت را کاهش دهید

داده غیرقابل تغییر داده‌ای است که پس از ایجاد تغییر نمی‌کند. در جاوااسکریپت، مانند بسیاری از زبان‌های دیگر، انواع ابتدایی (String ، Number و …) ذاتا قابل تغییر نیستند. اما اشیا دیگری مانند آرایه‌ها، تغییرپذیرند.

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

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

برنامه‌های فانکشنال تا حد امکان تغییرناپذیری را هدف قرار می‌دهند.

ES۶ از کلمه کلیدی const برای ایجاد مراجع ثابت استفاده می‌کند. این سوزن را در مسیر درست حرکت می‌دهد زیرا ثابت‌ها را نمی‌توان دوباره تعیین یا دوباره اعلام کرد. در برنامه نویسی فانکشنال، شما می‌توانید از ساختار به عنوان وسیله‌ای برای وارد کردن داده‌های پیکربندی ساده (رشته‌های URL، نام پایگاه داده و …) استفاده کنید. اما این مشکلات قابل تغییر بودن را تا حدی که برنامه نویسی تابعی نیاز دارد حل نمی‌کند. می‌توانید از تعیین مجدد یک متغیر جلوگیری کنید، اما چگونه می‌توان از تغییر وضعیت داخلی یک شی جلوگیری کرد؟ به عنوان مثال، این کد کاملا قابل قبول است:

const student = new Student('Alonzo', 'Church', '۶۶۶-۶۶-۶۶۶۶', 'Princeton');
student.lastname = 'Mourning';

آنچه شما نیاز دارید سیاست سخت‌گیرانه‌تری برای تغییر ناپذیری آن است، Encapsulation استراتژی خوبی برای محافظت در برابر جهش است. برای ساختارهای ساده اشیا متناوب، یک گزینه خوب اتخاذ الگوی Value Object است که به آن الگوی Module نیز گفته می‌شود.

function zipCode(code, location) {
  let _code = code;
  let _location = location || '';
  return {
    code: function () {
      return _code;
    },
    location: function () {
      return _location;
    },
    fromString: function (str) {
      let parts = str.split('-');
      return zipCode(parts[۰], parts[۱]);
    },
    toString: function () {
      return _code + '-' + _location;
    }
  };
}

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

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

نتیجه‌گیری

وقتی به طراحی برنامه خود فکر می‌کنید، سوالات زیر را از خود بپرسید:

  • آیا برای پشتیبانی از قابلیت‌های اضافی، مرتبا کد خود را تغییر می‌دهم؟
  • اگر یک فایل یا تابع را تغییر دهم، دیگری تحت تأثیر قرار می‌گیرد؟
  • آیا تکرار زیادی در کد من وجود دارد؟
  • آیا کد من بدون ساختار است یا پیگیری آن سخت است؟

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

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

برای اینکه از برنامه نویسی فانکشنال بهره‌مند شوید، باید یاد بگیرید که تابعی بیندیشید و در نتیجه، آگاهی تابعی خود را توسعه دهید – غریزه نگاه کردن به مشکلات به عنوان ترکیبی از توابع ساده که در کنار هم یک راه‌حل کامل را ارائه می‌دهند.

در سطح بالا، برنامه نویسی فانکشنال به طور موثر تأثیر متقابل بین تجزیه (شکستن برنامه‌ها به قطعات کوچک) و ترکیب (پیوستن قطعات به یکدیگر) است. همین دوگانگی است که برنامه‌های کاربردی را ماژولار و بسیار موثر می‌کند.

اگر هرگونه سوال، پیشنهاد یا نظری دارید، در بخش زیر با ما در میان بگذارید.