ایدهٔ اصلی
باربارا لیسکوف در توسعهٔ مفاهیم انتزاع داده و زبان CLU نقش مهمی داشت. تمرکز این مسیر بر آن بود که کاربران یک جزء بتوانند با قرارداد آن کار کنند، بدون نیاز به دانستن همهٔ جزئیات داخلی.
کاربرد در طراحی
برای هر ماژول بپرسید چه چیزی را تضمین میکند و چه چیزی باید پنهان بماند. این پرسش ساده به کاهش وابستگی نامناسب و طراحی آزمونهایی که رفتار واقعی را میسنجند کمک میکند.
انتزاع داده؛ پنهانکردنِ درست جزئیات
در طراحی نرمافزار، پنهانکردن جزئیات به معنی حذف آنها نیست. هدف این است که مصرفکنندهٔ یک جزء برای استفادهٔ صحیح، مجبور نباشد ساختار داخلی آن را بداند. مثلاً کاربر یک صف باید بداند چگونه داده اضافه یا دریافت کند؛ لازم نیست بداند صف با آرایه ساخته شده یا فهرست پیوندی. قرارداد عمومی، مرز میان این دو نگاه است.
اگر برنامههای دیگر مستقیماً به ساختار داخلی صف وابسته شوند، تغییر پیادهسازی میتواند آنها را خراب کند. انتزاع مناسب اجازه میدهد روش ذخیرهسازی عوض شود، به شرط آنکه رفتار وعدهدادهشده حفظ شود. این همان جایی است که طراحی رابط، آزمون و مستندات به هم میرسند.
جایگزینی فقط همنامبودن متدها نیست
دو پیادهسازی ممکن است نام متدهای یکسانی داشته باشند ولی رفتارشان قابل جایگزینی نباشد. اگر یکی ترتیب دادهها را حفظ کند و دیگری بدون توضیح آن را تغییر دهد، مصرفکننده با نتیجهای متفاوت روبهرو میشود. برای بررسی جایگزینی باید پیششرطها، نتیجهٔ عملیات و رفتار در خطا را نیز در نظر گرفت.
فرض کن یک سرویس ذخیرهسازی وعده میدهد پس از پاسخ موفق، داده در خواندن بعدی قابل دریافت باشد. پیادهسازی جدیدی که بدون اعلام، این تضمین را ضعیف کند، صرفاً با داشتن همان نام API سازگار نشده است. قرارداد رفتاری باید به اندازهٔ شکل پیامها جدی گرفته شود.
برداشت آموزشی IROSG
یک رابط کوچک برای ذخیره و دریافت یادداشت تعریف کن و آن را یکبار با حافظه و یکبار با فایل پیادهسازی کن. مجموعهٔ آزمون مشترکی بنویس که برای هر دو اجرا شود. اگر آزمونها تنها نام متدها را میسنجند، هنوز قرارداد کافی تعریف نشده است؛ ورودی نامعتبر، دادهٔ گمشده و نتیجهٔ تکرار عملیات را هم بررسی کن.
مطالعهٔ کار لیسکوف از این زاویه، یادگیری یک اصطلاح مشهور را به تمرین طراحی تبدیل میکند. هدف نهایی، کاهش وابستگی نامناسب و ساخت اجزایی است که بتوان رفتارشان را فهمید، آزمود و با اطمینان بیشتری تغییر داد. روایت تاریخی و نقش زبان CLU در منابع پایین صفحه آمده است.
یک نوع داده را از نمایش داخلی آن جدا کنیم
برای فهم انتزاع داده، یک پشتهٔ ساده در نظر بگیرید. مصرفکننده میخواهد مقدار اضافه کند، آخرین مقدار را بردارد و بداند ساختار خالی است یا نه. لزومی ندارد بداند داده داخل آرایه نگه داشته میشود یا زنجیرهای از گرهها. این جداسازی زمانی مفید است که قرارداد رفتار دقیق باشد. مثال حاضر تمرین آموزشی مستقل است و پیادهسازی CLU یا بازنشر کار تاریخی لیسکوف نیست.
قرارداد را با پرسشهای کوچک شروع کنید. برداشتن از پشتهٔ خالی چه نتیجهای دارد؟ آیا مقدار تهی مجاز است؟ آیا عملیات، نسخهٔ قبلی ساختار را تغییر میدهد؟ پاسخ این سؤالها باید برای مصرفکننده قابل اتکا باشد. پنهانکردن داده بدون مشخصکردن رفتار، انتزاع خوب نمیسازد؛ فقط اطلاعات لازم برای استفاده را حذف میکند. هدف، پنهانکردن جزئیات غیرضروری و آشکارکردن تعهدهای ضروری است.
تمرین تعویض پیادهسازی بدون تغییر مصرفکننده
دو پیادهسازی کوچک از همین قرارداد بسازید و مجموعهای از آزمونهای مشترک روی هر دو اجرا کنید. آزمونها باید از عملیات عمومی استفاده کنند، نه از ساختار داخلی. سپس نمایش داده را در یکی تغییر دهید. اگر مصرفکننده بدون دلیل شکسته شد، احتمالاً به چیزی وابسته بوده که نباید جزئی از قرارداد میبود. این آزمایش تفاوت میان آزمون رفتار و آزمون شکل داخلی را ملموس میکند.
در دور دوم، یکی از پیادهسازیها را طوری تغییر دهید که ورودی بیشتری را رد کند. اکنون بررسی کنید آیا هنوز میتوان آن را در جای پیادهسازی قبلی قرار داد. شباهت نام متدها کافی نیست؛ انتظار مصرفکننده از ورودی و نتیجه هم مهم است. با یک مثال نقض کوچک میتوان ناسازگاری رفتاری را نشان داد، بدون آنکه بحث فقط به ساختار کلاسها یا اصطلاحات زبان محدود شود.
قرارداد رفتاری در API سازمانی
فرض کنید خدمتی زمان پاسخ را بهصورت عدد برمیگرداند. اگر یک پیادهسازی میلیثانیه و دیگری ثانیه گزارش کند، نوع ظاهری هر دو عدد است اما رفتار سازگار نیست. همچنین حذف خطا از پاسخ و برگرداندن صفر ممکن است معنای نتیجه را تغییر دهد. در طراحی API باید واحد، حالت نبود داده و خطا صریح باشند. این مثال آموزشی نشان میدهد انتزاع فقط موضوع درس زبان برنامهنویسی نیست؛ در مرز دو تیم هم حضور دارد.
یک جدول قرارداد تهیه کنید که برای هر عملیات، پیششرط، اثر و نتیجه را توضیح دهد. سپس از مصرفکننده بخواهید بر اساس همان جدول چند آزمون بنویسد. اختلاف برداشتها را پیش از انتشار پیدا کنید. قرارداد خوب قرار نیست همهٔ جزئیات فنی را توضیح دهد؛ باید آن بخش از رفتار را روشن کند که دیگران حق دارند به آن تکیه کنند. این تمایز، تغییر داخلی را کمهزینهتر میکند.
انتزاع و توزیعشدگی
وقتی یک عملیات از شبکه عبور میکند، شکست و تأخیر وارد مسئله میشوند. ممکن است درخواست انجام شده باشد اما پاسخ به مصرفکننده نرسد. پنهانکردن کامل این تفاوت زیر نام یک تابع، لزوماً طراحی مناسبی نیست. باید روشن باشد چه چیزی تضمین میشود، چه چیزی ممکن است تکرار شود و در حالت نامشخص چه رفتاری انتظار میرود. این بخش یک تحلیل عمومی معماری است و نتیجهٔ یک آزمایش خاص را ادعا نمیکند.
برای تمرین، یک شمارندهٔ محلی و یک شمارندهٔ فرضیِ دوردست را مقایسه کنید. آیا هر دو میتوانند دقیقاً همان قرارداد خطا و زمان پاسخ را داشته باشند؟ کدام فرض باید به مصرفکننده اعلام شود؟ این پرسشها کمک میکنند بهجای پنهانکردن همهٔ پیچیدگی، مرز مناسب انتزاع را انتخاب کنیم. هر چیزی که پنهان میشود باید واقعاً برای استفادهکننده غیرضروری باشد، نه فقط برای نویسنده ناخوشایند.
معیار یادگیری از این معرفی
پس از مطالعهٔ زندگی و آثار لیسکوف، یک ماژول از پروژهٔ خود انتخاب کنید و توضیح آن را بدون اشاره به ساختمان داخلی بنویسید. اگر نتوانستید، شاید قرارداد هنوز به اندازهٔ کافی مشخص نیست. سپس فهرست وابستگیهای مصرفکنندگان را بررسی کنید: کدام وابستگی لازم است و کدام تصادفی؟ این تمرین میتواند پیش از بازطراحی بزرگ، چند مرز مهم را روشن کند.
برای ادعاهای تاریخی به منابع مراجعه کنید و برداشت آموزشی را جدا نگه دارید. هدف، استفاده از یک نام مشهور برای تأیید سلیقهٔ طراحی خودمان نیست. باید بتوانیم با مثال، آزمون و استدلال نشان دهیم چرا یک قرارداد برای مسئلهٔ مشخص مناسبتر است. تاریخ علوم رایانه در این صورت به نقطهٔ شروع پرسش تبدیل میشود، نه جایگزینی برای بررسی مستقل تصمیمهای فنی امروز.