تست کلاس GradeBook
تابع main (خطوط ۴۳-۶۰) یک شی از کلاس GradeBook ایجاد کرده و از توابع عضو آن استفاده می کند. در خط ۴۶ یک شی GradeBook بنام myGradeBook ایجاد شده است. خطوط ۴۹-۵۰ نام دوره اولیه را با فراخوانی تابع عضو getCourseName به نمایش در می آورند. توجه کنید که اولین خط خروجی نشاندهنده نام دوره نمی باشد، چرا که عضو داده courseName در ابتدای کار تهی است. بطور پیشفرض، مقدار اولیه یک رشته، رشته تهی است، رشتهای که حاوی هیچ کاراکتری نمی باشد. زمانیکه یک رشته تهی به نمایش درآید، چیزی بر روی صفحه نمایش ظاهر نمیشود.
تابع main
خط ۵۳ از کاربر می خواهد که نام دوره را وارد سازد. متغیر محلی nameOfCourse اعلان شده در خط ۴۵، با نام دوره وارد شده توسط کاربر مقداردهی می شود که از فراخوانی تابع getline بدست می آید (خط ۵۴). خط ۵۵ تابع عضو setCourseName را فراخوانی کرده و nameOfCourse را بعنوان آرگومان تابع بکار می گیرد. به هنگام فراخوانی تابع، مقدار آرگومان به پارامتر name (خط ۱۹) از تابع عضو setCourseName کپی می گردد (خطوط ۱۹-۲۲). سپس مقدار پارامتر به عضو داده courseName تخصیص می یابد (خط ۲۱). خط ۵۷ یک خط خالی در خروجی قرار داده، سپس خط ۵۸ تابع عضو displayMessage را برای نمایش پیغام خوشآمدگویی به همراه نام دوره فراخوانی می کند.
مهندسی نرمافزار با توابع set و get
اعضای داده private یک کلاس فقط قادر به دستکاری شدن از طریق توابع عضو آن کلاس هستند (و «دوستان» آن کلاس که در فصل دهم با آنها آشنا خواهید شد). بنابر این سرویسگیرنده یک شی، (یعنی هر کلاس یا تابعی که مبادرت به فراخوانی توابع عضو از خارج شی می کند)، توابع عضو public کلاس را برای دریافت سرویسهای کلاس برای شی های خاصی از کلاس فراخوانی میکند. به همین دلیل است که عبارات موجود در تابع main (برنامه شکل ۵-۳، خطوط ۴۳-۶۰) مبادرت به فراخوانی توابع عضو setCourseName، getCourseName و displayMessage موجود در شی GradeBookpublic کلاسها به سرویسگیرندهها اجازه تخصیص مقادیر به (set) یا دریافت مقادیر از (get) اعضای داده private را می دهند. نیازی نیست که اسامی این توابع عضو حتماً با set یا get شروع شوند، اما این روش نامگذاری مرسوم است. در این مثال، تابع عضوی که مبادرت به تنظیم (تخصیص مقادیر) عضو داده courseName میکند، setCourseName نام دارد و تابع عضوی که مقداری از عضو داده courseName بدست می آورد، تابع getCourseName نامیده می شود. توجه کنید که گاهاً توابع set، بنام mutators شناخته می شوند (چرا که مبادرت به تغییر یا اصلاح مقادیر می کنند)، و توابع get بنام accessors نامیده می شوند (چرا که به مقادیر دسترسی پیدا می کنند). میکند. غالباً توابع عضو
بخاطر دارید که اعلان اعضای داده با تصریحکننده دسترسی private موجب پنهانسازی داده میشود. تدارک دیدن توابع set و get به سرویسگیرندههای کلاس اجازه دسترسی به دادههای پنهان شده را میدهند، اما بصورت غیرمستقیم. سرویسگیرنده از مبادرت خود به اصلاح یا بدست آوردن داده یک شی اطلاع دارد، اما از اینکه شی چگونه این عملیات را انجام میدهد، اطلاعی ندارد. در برخی از موارد امکان دارد کلاسی به یک روش مبادرت به عرضه داده در محیط داخلی نماید، در حالیکه همان داده را به روش مختلفی در اختیار سرویسگیرندهها قرار میدهد. برای مثال، فرض کنید کلاس Clock زمانی از روز را بصورت یک عضو داده time و از نوع int و private عرضه میکند که تعداد ثانیههای از نیمهشب را ذخیره می سازد. با این همه، زمانیکه سرویسگیرندهای تابع عضو getTime از شی Clock را فراخوانی میکند، زمان برحسب ساعت، دقیقه و ثانیه در یک رشته با فرمت “HH:MM:SS” به وی برگشت داده می شود. به همین ترتیب، فرض کنید کلاس Clock یک تابع set بنام setTime تدارک دیده که یک رشته پارامتری با فرمت “HH:MM:SS” دریافت مینماید. با استفاده از قابلیتهای عرضه شده در فصل هیجدهم، تابع setTime قادر به تبدیل این رشته به تعداد ثانیهها است، که تابع آن را در عضو داده private ذخیره سازد. همچنین تابع set میتواند به بررسی اعتبار مقدار دریافتی پرداخته و اعتبار آن را تایید یا لغو کند (مثلاً “۱۲:۳۰:۴۳” معتبر بوده اما “۴۲:۸۵:۷۰” معتبر نیست). توابع set و get به سرویسگیرنده امکان تعامل با یک شی را فراهم می آورند، اما داده private (خصوصی) شی همچنان بصورت کپسوله (پنهان) و ایمن در خودش نگهداری می شود.
همچنین توابع set و get یک کلاس میتوانند توسط سایر توابع عضو موجود در درون کلاس به منظور دستکاری کردن داده private کلاس بکار گرفته شوند، اگر چه این توابع عضو میتواند بصورت مستقیم به داده private دسترسی پیدا کنند. در برنامه شکل ۵-۳، توابع عضو getCourseName و setCourseName از نوع توابع عضو public هستند، از اینرو در دسترسگیرندههای کلاس و همچنین خود کلاس قرار دارند. تابع عضو displayMessage تابع عضو getCourseName را برای بدست آوردن مقدار عضو داده courseName برای نمایش آن، فراخوانی میکند، با اینکه خود displayMessage میتواند بصورت مستقیم به courseName دسترسی پیدا کند. دسترسی به عضو داده از طریق تابع get آن، سبب ایجاد یک کلاس بهتر و پایدارتر می شود (کلاسی که نگهداری آن آسان بوده و کمتر از کار می افتد). اگر تصمیم به تغییر عضو داده courseName بگیریم، ساختار تعریفکننده displayMessage نیازمند اصلاح نخواهد بود، فقط بدنه توابع get و set که مستقیماً عضو داده را دستکاری می کنند نیاز به تغییر خواهند داشت. برای مثال، فرض کنید که میخواهیم نام دوره را در دو عضو داده عرضه کنیم، courseName (مثلاً “CS101”) و courseTitle (مثلاً “Introduction to C++ Programming”). هنوز هم تابع displayMessage میتواند با یک فراخوانی تابع عضو getCourseName نام کامل دوره را بدست آورده و بعنوان بخشی از پیغام خوشآمدگویی به نمایش در آورد. در اینحالت، تابع getCourseName نیازمند ایجاد و برگشت دادن یک رشته حاویcourseNumber و بدنبال آن courseTitle است. در ادامه تابع عضو displayMessage عنوان کامل دوره “CS101 Introduction to C++ Programming” را به نمایش در خواهد آورد، چرا که از تغییرات صورت گرفته بر روی اعضای داده کلاس به دور مانده است. مزایای فراخوانی تابع set از یک تابع عضو دیگر کلاس به هنگام بحث اعتبارسنجی در بخش ۱۰-۳ بیشتر آشکار خواهد شد.
دیاگرام UML کلاس GradeBook با یک داده عضو و توابع set و get
شکل ۶-۳ حاوی دیاگرام کلاس UML به روز شده برای نسخهای از کلاس GradeBook بکار رفته در برنامه ۵-۳ است. این دیاگرام مبادرت به مدلسازی داده عضو courseName بعنوان یک صفت در بخش میانی کلاس کرده است. UML اعضای داده را در لیستی که در آن نام صفت، یک کولن و نوع صفت قرار گرفته عرضه میکند. نوع UML صفت courseName، نوع String است که متناظر با string در C++ می باشد. عضو داده courseName در C++ حالت private دارد و از اینرو در دیاگرام کلاس با یک علامت منفی (–) در مقابل نام صفت مشخص شده است. علامت منفی در UML معادل با تصریحکننده دسترسی private در C++ است. کلاس GradeBook حاوی سه تابع عضو public است، از اینرو در لیست دیاگرام کلاس این سه عملیات در بخش تحتانی یا سوم جای گرفتهاند. بخاطر دارید که نماد جمع (+) قبل نام هر عملیات نشان می دهد که عملیات در C++ حالت public دارد. عملیات setCourseName دارای یک پارامتر String بنام name است. در UML نوع برگشتی از یک عملیات با قرار دادن یک کولن و نوع برگشتی پس از پرانتزهای نام عملیات مشخص میشود. تابع عضو getCourseName از کلاس GradeBook (شکل ۵-۳) دارای نوع string برگشتی در C++ است، بنابر این دیاگرام کلاس نوع برگشتی String را در UML به نمایش درآورده است. توجه نمائید که عملیاتهای setCourseName و displayMessage مقداری برگشت نمی دهند (نوع برگشتی آنها void است)، از اینرو در دیاگرام کلاس UML نوع برگشتی از پرانتزها برای آنها در نظر گرفته نشده است. UML از void همانند C++ در زمانیکه تابع مقداری برگشت نمی دهد، استفاده نمی کند.
شکل ۶-۳ | دیاگرام کلاس UML برای کلاس GradeBook با یک صفت private برای courseName و عملیاتهای public برای توابع setCourseName، getCourseName و displayMessage
۷-۳ مقداردهی اولیه شی ها با سازندهها
همانطوری که از بخش ۶-۳ بخاطر دارید، زمانیکه یک شی از کلاس GradeBook ایجاد شد (شکل ۵-۳) عضو داده آن یعنی courseName بطور پیشفرض با یک رشته تهی مقداردهی اولیه شده بود. اگر بخواهیم به هنگام ایجاد یک شی از GradeBook نام دورهای تدارک دیده شود، چه کاری باید انجام داد؟ هر کلاسی که اعلان میکنید میتواند یک سازنده (constructor) داشته باشد که میتوان با استفاده از آن مبادرت به مقداردهی اولیه یک شی از کلاس به هنگام ایجاد شی کرد. سازنده یک تابع عضو ویژه است که بایستی همنام با نام کلاس تعریف شده باشد، از اینروست که کامپایلر میتواند آن را از دیگر توابع عضو کلاس تشخیص دهد. مهمترین تفاوت موجود مابین سازندهها و توابع دیگر در این است که سازندهها نمیتواند مقدار برگشت دهند، بنابر این نمیتوانند نوع برگشتی داشته باشند (حتی void). معمولاً، سازندهها بصورت public اعلان میشود. غالباً کلمه “constructor” در برخی از نوشتهها بصورت خلاصه شده “ctor” بکار گرفته میشود، که استفاده از آن را ترجیح ندادهایم.
C++ نیازمند فراخوانی یک سازنده برای هر شی است که ایجاد میشود، در چنین حالتی مطمئن خواهیم بود که شی قبل از اینکه توسط برنامه بکار گرفته شود، بدرستی مقداردهی اولیه شده است. فراخوانی سازنده به هنگام ایجاد شی، بصورت غیرصریح یا ضمنی انجام می شود. در هر کلاسی که بصورت صریح سازندهای را مشخص نکرده است، کامپایلر یک سازنده پیشفرض تدارک می بیند، این سازنده دارای پارامتر نمی باشد. برای مثال، زمانیکه خط ۴۶ از برنامه شکل ۵-۳ یک شی GradeBook ایجاد میکند، سازنده پیشفرض فراخوانی میشود، چرا که در اعلان myGradeBook بطور صریح هیچ آرگومان سازندهای مشخص نشده است. سازنده پیشفرض تدارک دیده شده از سوی کامپایلر یک شی GradeBook بدون هیچ گونه مقادیر اولیه برای اعضای داده شی ایجاد می کند. [نکته: برای اعضای داده که شی های از سایر کلاسها هستند، سازنده پیشفرض بصورت ضمنی هر سازنده پیشفرض عضو داده را برای اطمینان از اینکه اعضاء داده به درستی مقداردهی اولیه شدهاند، فراخوانی می کند. در واقع به این دلیل است که عضو داده courseName از نوع رشته (در برنامه شکل ۵-۳) با یک رشته تهی مقداردهی اولیه شده است. سازنده پیشفرض برای کلاس string مبادرت به تنظیم یک مقدار رشتهای با رشته تهی میکند، در بخش ۳-۱۰ مطالب بیشتری در ارتباط با مقداردهی اولیه اعضای داده که شی های از کلاسهای دیگر هستند خواهید آموخت.]
در مثال برنامه شکل ۷-۳، نام یک دوره را به هنگام ایجاد یک شی از GradeBook مشخص کردهایم (خط ۴۹). در این مورد، آرگومان “CS101 Introduction to C++ Programming” به سازنده شی GradeBook ارسال میشود (خطوط ۱۷-۲۰) و مبادرت به مقداردهی اولیه courseName مینماید. برنامه شکل ۷-۳ یک کلاس GradeBook اصلاح شده تعریف کرده که حاوی یک سازنده با یک پارامتر رشتهای است که نام دوره اولیه را دریافت میکند.
شکل ۷-۳ | نمونهسازی شی های مضاعف از کلاس GradeBook و استفاده از سازنده GradeBook برای مشخص کردن نام دوره به هنگام ایجاد هر شی GradeBook.
دیدگاهها