طراحی قراردادی در دنیای نرم‌افزار

May 2020

ویدیویی وجود ندارد

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

 

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

 

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

 

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

 

ایده پشت طراحی قراردادی(design by contract) اینست بجای اینکه کد را یکطرفه بنویسید، هر دو طرف(کاربر، برنامه) توافق کنند که در صورت بروز مشکل، برنامه را متوقف کرده و پیغامی شفاف مبنی بر اینکه چرا نمیتوانند ادامه دهند ارسال کنند.

 

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

 

1. پیش شرط‌ها (postconditions):

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

مخصوصا برای زبانی مثل پایتون که dynamic type است، مجبوریم بررسی کنیم که آیا مقدار ارسالی دقیقا با نیازهای ما همخوانی دارد. در اینجا منظور این نیست که نوع داده را چک کنید بلکه باید بررسی کنید که داده دقیقا آن مقداری که ما انتظار داریم را در خود دارد.

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

در مفهوم طراحی قراردادی روش دوم پیشهاد میشود زیرا امن‌ترین و پرکاربردترین روش از نظر ساختار است.

هر روشی که انتخاب میکنید باید به یاد داشته باشید که فقط یکی را انتخاب کنید نه هر دو که در غیر اینصورت مفهوم DRY (خودتان را تکرار نکنید) را نقض کرده‌اید.

 

2. پس شرط‌ها (postconditions):

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

 

3. اثرات جانبی (side effects):

در صورت تمایل میتوانید اثرات جانبی که هر بخش از کد بر بخشی دیگر دارد را مستند کنید.

 

قراردادهای پایتونی(Pythonic contracts):

طبق چیزی که در PEP-316 گفته شده میتوانید در کلاس‌ها یا فانکشن‌ها در صورت بروز مشکل اقدام به نشان داده یک RuntimeError یا ValueError کنید. البته پیشنهاد دادن یک نوع ارور خاص کار دشواریست زیرا نیازهای هر برنامه متفاوت میباشد. دو نوع ارور اشاره شده از پرکاربردترین ارورهای پایتون است اما اگر با نیازهای شما همخوانی نداشت میتوانید ارور مخصوص به خود را ایجاد کنید. همجنین سعی کنید بخش های مختلف قرارداد را از هم جدا کنید. مثل کدهای مربوط به بخش پیش شرط‌ها در یکجا و کدهای مربوط به پس‌ شرط‌ها در جایی دیگر و هسته اصلی برنامه در بخشی دیگر. میتوانید برای اینکار از فانکشن‌های کوچک استفاده کنید هر چند که استفاده از decoratorها هم پیشنهاد جالبی است.

 

نتیجه

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

ارسال نظر

اگر قراره سوالی بپرسید که داخلش کد هست، بهتره از کدتون عکس بگیرید و به ایمیلی که پایین نوشتم بفرستید