ماهیت تکامل واسطها در جاوا (1)

این مقاله، توسط مایکل کولینگ به رشتۀ تحریر درآمده و در اولین شماره Java Magazine سال 2018 منتشر شده است، نویسنده، سیر تاریخی توسعه و تکامل واسطها را با دقت نظر بررسی می کند. بخش اول از این نوشتار سه قسمتی که مورد نیاز آموزش حرفه ای جاوا می باشد را در این جا مطالعه کنید.
دربارۀ نویسنده
مایکل کولینگ، Java Champion و پروفسور در دانشگاه کنت انگلستان است. او دو کتاب مرجع و چندین مقاله در موضوع شیگرایی و آموزش در مهندسی کامپیوتر منتشر کرده است. او توسعهدهندۀ راهبر BlueJ و Greenfoot، دو محیط برنامهنویسی آموزشی است. او همچنین مدرس ممتاز ACM نیز هست.
متن مقاله
در این سری از مقالات که تحت عنوان «New to Java» منتشر میشود، در تلاشم موضوعاتی را انتخاب کنم که میتواند فهم عمیقتری از ساختارهای زبان را ایجاد کند. عموما برنامهنویسان تازهکار مفاهیم را در حدی که بتوانند با آنها کار کنند یاد گرفتهاند. اما در بسیاری از مواقع فاقد فهم عمیقی هستند که باعث نوشتن کدهای بهتر میشود. با استفاده از این اصول مبنایی، برنامهنویس میفهمد هر ساختار، در چه شرایطی باید به کار گرفته شود. واسطهای جاوا یکی از این ساختارها هستند.
در این نوشته، پیشفرض من این است که خواننده فهمی ابتدایی از مفهوم وراثت و همچنین دو کلید واژۀ extends و implements که به این مفهوم مرتبطاند دارد. در ادامه شرح داده میشود که چرا جاوا دو نوع از مفهوم وراثت را – که با این دو کلیدواژه مشخص میشوند – پشتیبانی میکند؟ کلاسهای Abstract چطور با این مفهوم مرتبط میشوند؟ و از واسطها به چه منظوری میتوان استفاده کرد؟
طبق معمول، داستان این ویژگیها معمولا با ایدههایی ساده و ظریف آغاز میشود. ایدههای ساده موجب تعریف آنها در طراحی اولین نسخههای جاوا شد. رفته رفته که زبان پیش میرود و میخواهد با مشکلات بغرنج دنیای واقعی برنامهنویسی سرو کار داشته باشد، داستان پیچیدهتر میشود. در نهایت، چالشهای مذکور موجب شد در جاوا 8 متدهای default به واسطها اضافه شود و سادگی تعاریف اولیه به هم ریخت.
پسزمینهای کوتاه از وراثت
فهم مفهوم وراثت کار ساده ایست. یک کلاس میتواند توسعهیافتۀ کلاس دیگر در نظر گرفته شود. در این شرایط کلاس حاضر، زیرکلاس (subclass) و کلاسی که آن را توسعه داده است سوپرکلاس (superclass) نامیده میشود. اشیاء ساختهشده از زیرکلاس هم خواص سوپرکلاس و هم خواص زیرکلاس را دارند. زیرکلاس فیلدها و متدهایی دارد که یا در سوپرکلاس و یا در خود آن تعریف شده است. خب تا این جا همه چیز خوب پیش رفته است.
اما مفهوم وراثت در برنامهنویسی شبیه چاقوی نظامی سوییسیِ همهکاره است که از آن برای اهداف مختلفی استفاده میشود. به عنوان مثال میتوان از آن برای بهکارگیری کدی که قبلا نوشته شده، ساخت زیرنوع و بهکارگیری پویای اشیا (dynamic dispatch)، جداسازی تعریف از پیادهسازی، تعیین و تعریف قرارداد بین بخشهای مختلف سیستم و چندین کار دیگر استفاده کرد.
مقایسۀ وراثت کد با وراثت نوع
«وراثت کد» و «وراثت نوع» دو قابلیتی هستند که در قالب مفهوم کلی وراثت در برنامهنویسی شیگرا عرضه میشود. با وجود اینکه جاوا در حالت استاندارد این دو مفهوم را کاملا با هم آمیخته است، بهتر است آنها را از هم جدا کنیم. در جاوا هر کلاس یک نوع تعریف میکند؛ به محض این که یک کلاس میسازیم میتوانیم به نمونهسازی (Instantiation) از آن بپردازیم.
زمانی که با استفاه از کلید واژۀ extends یک زیرنوع میسازیم، هم نوع و هم کد به ارث میرسد. متدهای به ارث رسیده را میتوان فراخوانی کرد (وراثت کد) و اشیاء ساختهشده از زیرنوع میتوانند جایی که شیای از نوع سوپرکلاس لازم است به کار برده شوند (وراثت نوع).
نگاهی به مثال بیندازیم. اگر Student زیرکلاسی از Person باشد، میگوییم اشیا ساختهشده از نوع Student هستند، اما همزمان از نوع Person هم به شمار میروند. یک Student یک Person هم هست. در اینجا کد و نوع، هر دو به ارث رفتهاند.
هنگام طراحی زبان جاوا، تصمیم گرفته شد وراثت کد و نوع به هم گره بخورند، اما این تنها گزینۀ موجود برای طراحی نبود. زبانهای دیگری هستند که اجازه میدهند کد به ارث برده شود در حالتی که وراثت نوع اتفاق نیفتاده است. به عنوان مثال private inheritance در ++C این گونه است. یا میتوان نوع را به ارث برد، بدون این که کدی به ارث برده شود (به عنوان مثال جاوا چنین وراثتی را پشتیبانی میکند که در مقالههای بعدی شرح خواهیم داد)
وراثت چندگانه
ایدۀ دیگری که در این فضا مطرح میشود وراثت چندگانه است: یک کلاس میتواند بیش از یک سوپرکلاس داشته باشد. اجازه دهید مثالی بزنم، یک دانشجوی PhD ممکن است به عنوان مدرس نیز کار کند. در این شرایط عملکردی مشابه اعضای هیئت علمی دارد. مثلا تدریس میکند، شمارۀ اتاق دارد، حقوق میگیرد و … . در عین حال دانشجو هم هست، انتخاب واحد میکند و در کلاسها شرکت میکند، شمارۀ دانشجویی دارد و … . این شرایط میتواند با وراثت چندگانه مدلسازی شود (Figure 1).
PhDStudent زیرکلاسی از Faculty و Student است. در این حالت، همزمان ویژگیهای دانشجو و هیئت علمی را خواهد داشت. آنچه گفته شد از نظر مفهومی پیچیده نیست اما وقتی یک زبان وراثت چندگانه را پشتیبانی میکند بسیار پیچیده میشود؛ چرا که وراثت چندگانه مشکلات جدیدی را مطرح میکند. سوالات ذیل را در نظر بگیرید. در شرایطی که هر دو سوپرکلاس فیلدهای هم نام داشته باشند چه اتفاقی باید بیفتد؟ در شرایطی که در دو سوپرکلاس دو متد با عناوین یکسان ولی پیادهسازیهای متفاوت وجود داشته باشند تکلیف چیست؟ در این شرایط ساختارهایی در زبان نیاز است که که بتواند ابهامهای منتج از چنین وراثتی را حل کند. رفتهرفته اوضاع پیچیدهتر و بدتر میشود.
وراثت لوزَوی
سناریویی پیچیدهتر با عنوان وراثت لوزوی وجود دارد (Figure 2).در شرایطی که یک کلاس (مثلا PhDStudent) دو سوپرکلاس داشته باشد (Faculty و Student) که این دو، سوپرکلاسی مانند Person داشته باشند، دچار وراثت لوزوی شدهایم.
حال به این سوال دقت کنید: در شرایطی که سوپرکلاس سطح بالاتر (اینجا: Person) فیلدی داشته باشد، کلاس در پایینترین سطح (اینجا: PhDStudent) باید یک کپی از این فیلد داشته باشد یا دو تا؟ در هر صورت این فیلد دو بار به ارث رسیده است، از هر شاخۀ وراثت یک بار.
پاسخ این است: بستگی دارد! اگر فیلد، مثلا شمارۀ شناسایی باشد، معقول است. ممکن است یک دانشجوی PhD دو شماره شناسایی داشته باشد، یکی به عنوان شماره دانشجویی و یکی به عنوان شناسۀ هیئتعلمی. اما اگر فیلد چیزی مانند نام باشد، با این که از هر دو سوپرکلاس به ارث رسیده، باید یک نسخه از آن در کلاس PhDStudent وجود داشته باشد، بدیهی است که یک شخص به عنوان دانشجو یا هیئت علمی دو اسم متفاوت ندارد.
به صورت خلاصه میتوان گفت در وراثت چندگانه اوضاع بسیار پیچیده میشود. زبانهایی که به صورت کامل، از وراثت چندگانه پشتیبانی میکنند باید قوانین و ساختارهایی داشته باشند که بتواند تمام موقعیتهای اینچنینی را مدیریت کند. بی شک فهم و تعامل با چنین قوانینی ساده نخواهد بود.
ادامه دارد…
عناوین اصلی بخش بعدی مقاله:
- گونهای نجاتبخش از وراثت نوع
- واسطها
- مزایای وراثت چندگانه برای انواع
- کلاسهای Abstract
- واسطهای خالی
منبع: Java Magazine Jan-Feb 2018