مشاور و مدرس برنامه نویسی در حوزه دات نت - https://github.com/mjebrahimi
اهمیت Side-effect Free و Idempotency در کدنویسی
برنامه نویسی Functional Programming (به اختصار FP) در تعریف، یک Paradigm (پارادایم) برنامه نویسی هست که در اون برنامه ها به کمک ترکیب توابع ساخته میشن. (چه تعریف گنگ و غیر ملموسی!)
از اونجایی که "پارادایم" کلمه غیر ملموسی هست و اگر اولین بارتون باشه که اون رو میشنوین احتمال زیاد مفهومش رو متوجه نمیشین اینطور تعریفش میکنم: پارادایم برنامه نویسی یعنی یک روش یا رویکرد برنامه نویسی.
این رویکرد مفاهیم مختلفی رو داخل خودش داره که اینجا نمیخوام واردشون بشم. چیزی که اینجا میخوام روش تاکید کنیم 3 مفهوم زیر هست:
1- Side-effect Free
2- Idempotent
3- Pure Functions
مفهوم Side-effect Free
این مفهوم به این اشاره میکنه که یه تابع (متد) نباید "تاثیر جانبی" روی بقیه قسمت های برنامه داشته باشه. خب این یعنی چی؟ کی ما تاثیر جانبی رو بقیه میذاریم؟
وقتی متد ما یک shared state تغییر رو بده. shared state به معنی متغیر/مقدار ایی هست که بین چندین تابع یا قسمت مختلف برنامه مشترک هست.
پس در واقع وقتی یک تابع، متغیری یا مقداری را که بین چندین تابع یا قسمت های مختلف برنامه مشترک هست رو "تغییر" بده یعنی side-effect اتفاق افتاده
در کنار این مفهوم، مفهوم دیگری نیز وجود داره به نام Avoiding Shared State که تاکید میکنه از ایجاد و استفاده از shared state ها خودداری کنیم.
وجود shared state خواسته یا ناخواسته باعث بروز side-effect میشه. پس خودداری از اون باعث میشه به side-effect نیز برنخوریم یا کمتر بر بخوریم
چه مزایایی داره؟
- هرچه کد شما side-effect کمتری داشته باشه، احتمال وقوع باگ کمتر میشه.
- از طرفی چون تابع ما قرار نیست یک share-state رو تغییر بده، احتمال قوع مشکلات همزمانی هم کاهش پیدا میکنه و نیز با اطمینان بیشتری میتونین توابع تون رو به صورت parallel اجرا کنین.
- همچنین به ایزوله بودن متد های شما کمک میکنه که این موضوع مخصوصا توی تست نویسی بسیار حائز اهمیت هست
نکته:
هر برنامه "باید" side-effect داشته باشه. در نهایت شما میخواین دیتا رو داخل دیتابیس یا فایل یا... (که یک shared-state) محسوب میشه ذخیره کنین. پس نمیشه side-effect رو کاملا حذف کرد.
ولی نکته اصلی اینجاست که باید side-effect ها رو شناسایی کنین، اونها رو محدود کنین و از پراکنده شدنشون توی کد ها جلوگیری کنین.
مفهوم Idempotent
این مفهوم به این امر اشاره میکنه که زمانی توابع ما Idempotent هستند که اگر اونها رو هرچند بار هم با مقادیر ورودی ثابت و مشابه فراخوانی کنیم همیشه نتیجه یا خروجی یکسان و ثابتی داشته باشه
مثلا تابعی که یک مقدار Random رو برمیگردونه Idempotent نیست چرا که هربار نتیجه اش متفاوته؛ همینطور تابعی که DateTime.Now رو برمیگردونه.
مثال کاربردیش توی دنیای واقعی، متدی میشه که وظیفه حذف یک رکورد رو داره. به این صورت که (مثلا از ORM EF استفاده میکنه و) ابتدا رکورد رو با id مورد نظر واکشی میکنه سپس اون رو به متد Remove میده و SaveChanges فراخوانی میشه
خب بار اولی که این متد رو با id برابر با 100 فراخوانی کنیم اون سطح حذف میشه ولی بار دومی که اون رو "با همین id" فراخوانی میکنیم به exception بر میخوره چرا که دیگه اون رکورد وجود نداره که بخواد حذفش کنه (موقع find، مقدار اون رکورد null هست و متد Remove با ورودی null خطا میده)
ولی همین متد رو اگر به این صورت پیاده سازی کنیم که قبل از حذف کردن، چک کنه که اگر این رکورد وجود نداره کاری انجام نده، متد ما Idempotent میشه چرا که هر چندبار فراخوانی اون، یک نتیجه رو داره و اون هم اطمینان از حذف اون رکورد هست
نکته:
مفهوم Idempotent بودن توی معماری Event-Driven و الگوی Pub/Sub هم اهمیت بسیار بالایی داره. از اونجایی که یک Event یا Message ممکنه به هر دلیلی "بیش از یکبار" توسط Subscriber ها پردازش بشه. باید اطمینان داشته باشیم که نتیجه یکسانی داره و عملکرد سیستم رو تحت تاثیر نمیگذاره (مثلا فرض کنید برای یک سفارش، دو تا فاکتور برای مشتری ثبت بشه!)
نمیخوام خیلی وارد روش های Idempotency بشم فقط اینکه معمولا 2 روش وجود داره:
1- طوری پیاده سازی کنیم که چند دفعه اجرا شدنش توی عملکردش تاثیری نداشته باشه
2- یک flag ایی رو به ازای هر Message داشته باشیم که وقتی اون Message پردازش میشه اون رو true کنیم و دفعات بعدی با چک کردن این flag و متوجه شدن از اینکه قبلا پردازش شده، دیگه پردازشش نکنیم (در این حالت ممکنه استفاده از تکنیک های synchronization مانند locking لازم باشه)
چه مزایایی داره؟
- باعث Reliability (قابل اعتماد) تر شدن توابع مون میشه چون چندین بار اجرا، نتایج یکسانی میده
- تست نویسی رو راحت تر میکنه چون با پارامتر های یکسان، هر بار خروجی متفاوتی نمیده
مفهوم Pure Functions
این مفهوم تعریف خیلی ساده ای داره، تابعی Pure Function (خالص) هست که هم Side-effect Free باشه هم Idempotent. در غیر این صورت میشه Impure Function (تابع ناخالص)
مزیت هاشم که واضحه، مشخصا مزایای جفتشون رو داره.
جمع بندی
مفاهیم Functional Programming الزاما نیاز به استفاده از زبان های FP (مثل FSharp یا Scala یا Haskell) ندارن. خیلی از اون مفاهیم به راحتی داخل زبان های دیگه و رویکرد Object Oriented Programming نیز قابل استفاده و بسیار مفید هستند و باعث Maintainability و Reliability میشن (نگهداری بهتر کد ها و قابل اعتماد تر بودنشون)
توصیه میکنم که اون مفاهیم رو یاد بگیرید و بسته به نیازتون توی کدنویسی های OOP تون هم ازش استفاده کنید
مقاله زیر که این مفاهیم رو به خوبی بررسی کرده و مطالعه اون خالی از لطف نیست
مقالات بیشتر کانال تلگرام دات نت زوم
مطلبی دیگر از این انتشارات
Razor Class Library چیست؟
مطلبی دیگر از این انتشارات
بررسی نکات کلیدی نظرسنجی Stackoverflow 2019 (قسمت سوم)
مطلبی دیگر از این انتشارات
آموزش gRPC در ASP.NET Core - قسمت دوم