مطالب
مفاهیم برنامه نویسی ـ مروری بر کلاس و شیء
من قصد دارم در قالب چند مطلب برخی از مفاهیم پایه و مهم برنامه نویسی را که پیش نیازی برای درک اکثر مطالب موجود در وب سایت است به زبان ساده بیان کنم تا دایره افرادی که می‌توانند از مطالب ارزشمند این وب سایت استفاده کنند وسعت بیشتری پیدا کند. لازم به توضیح است از آنجا که علاقه ندارم اینجا تبدیل به نسخه فارسی MSDN یا کتاب آنلاین آموزش برنامه نویسی شود این سری آموزش‌ها بیشتر شامل مفاهیم کلیدی خواهند بود.
این مطلب به عنوان اولین بخش از این سری مطالب منتشر می‌شود.

هدف این نوشته بررسی جزییات برنامه نویسی در رابطه با کلاس و شیء نیست. بلکه دریافتن چگونگی شکل گرفتن ایده شیء گرایی و علت مفید بودن آن است.

مشاهده مفاهیم شیء گرایی در پیرامون خود

حتماً در دنیای برنامه نویسی شیء گرا بارها با کلمات کلاس و شیء روبرو شده اید. درک صحیح از این مفاهیم بسیار مهم و البته بسیار ساده است. کار را با یک مثال شروع می‌کنیم. به تصویر زیر نگاه کنید.
 



در سمت راست بخشی از نقشه یک ساختمان و در سمت چپ ساختمان ساخته شده بر اساس این نقشه را می‌بینید. ساختمان همان شیء است. و نقشه ساختمان کلاس آن است چراکه امکان ایجاد اشیائی که تحت عنوان ساختمان طبقه بندی (کلاس بندی) می‌شوند را فراهم می‌کند. به همین سادگی. کلاس‌ها طرح اولیه، نقشه یا قالبی هستند که جزییات یک شی را توصیف می‌کنند.
حتماً با من موافق هستید اگر بگویم:
  • در نقشه ساختمان نمی‌توانید زندگی کنید اما در خود ساختمان می‌توانید.
  • از روی یک نقشه می‌توان به تعداد دلخواه ساختمان ساخت.
  • هنگامی که در یک ساختمان زندگی می‌کنید نیازی نیست تا دقیقاً بدانید چگونه ساخته شده و مثلاً سیم کشی یا لوله کشی‌های آن چگونه است! تنها کافیست بدانید برای روشن شدن لامپ باید کلید آن را بزنید.
  • ساختمان دارای ویژگی هایی مانند متراژ، ضخامت دیوار، تعداد پنجره و ابعاد هر یک و ... است که در هنگام ساخت و بر اساس اطلاعات موجود در نقشه تعیین شده اند.
  • ساختمان دارای کارکرد هایی است. مانند بالا و پایین رفتن آسانسور و یا باز و بسته شدن درب پارکینگ. هر یک از این کارکرد‌ها نیز بر اساس اطلاعات موجود در نقشه پیاده سازی و ساخته شده اند.
  • ساختمان تمام اجزای لازم برای اینکه از آن بتوانیم استفاده کنیم و به عبارتی در آن بتوانیم زندگی کنیم را در خود دارد.
در محیط پیرامون ما تقریباً هر چیزی را می‌توان در یک دیدگاه شیء تصور کرد. به عبارتی هر چیزی که بتوانید به صورت مستقل در ذهن بیاورید و سپس برخی ویژگی‌ها و رفتارها یا کارکردهای آن‌را برشمارید تا آن چیز را قابل شناسایی کند شیء است. مثلاً من به شما می‌گویم موجودی چهار پا دارد، مو... مو... می‌کند و شیر می‌دهد و ... . شما خواهید گفت گاو! و نمی‌گویید گربه. چرا؟ چون توانستید در ذهن خود موجودیتی را به صورت مستقل تصور کنید و از روی ویژگی‌ها و رفتارش آن‌را دقیقاً شناسایی کنید.
سوال: کلاس یا نقشه ایجاد گاو چیست؟ اگر از من بپرسید خواهم گفت طرح اولیه گاو هم ممکن است وجود داشته باشد البته در اختیار خداوند و با سطح دسترسی ملکوت!
اتومبیل، تلویزیون و ... همگی مثال هایی از اشیاء پیرامون ما در دنیای واقعی هستند که حتماً می‌توانید کلاس یا نقشه ایجاد آن‌ها را نیز بدست آورید و یا ویژگی‌ها و کارکرد‌های آن‌ها را برشمارید.

مفاهیم شیء گرایی در مهندسی نرم افزار

مفاهیمی که تاکنون در مورد دنیای واقعی مرور کردیم همان چیزی است که در دنیای برنامه نویسی ـ به عقیده من دنیای واقعی‌تر از دنیای واقعی ـ با آن سر و کار داریم. علت این امر آن است که اصولاً ایده روش برنامه نویسی شیء گرا با مشاهده محیط پیرامون ما به وجود آمده است.
برای نوشتن برنامه جهت حل یک مسئله بزرگ باید بتوان آن مسئله را به بخش‌های کوچکتری تقسیم نمود. در این رابطه مفهوم شیء و کلاس با همان کیفیتی که در محیط پیرامون ما وجود دارد به صورت مناسبی امکان تقسیم یه مسئله بزرگ به بخش‌های کوچکتر را فراهم می‌کند. و سبب می‌شود هماهنگی و تقارن و تناظر خاصی بین اشیاء برنامه و دنیای واقعی بوجود آید که یکی از مزایای اصلی روش شیء گراست.
از آنجا که در یک برنامه اصولاً همه چیز و همه مفاهیم در قالب کدها و دستورات برنامه معنا دارد، کلاس و شیء نیز چیزی بیش از قطعاتی کد نیستند. قطعه کد هایی که بسته بندی شده اند تا تمام کار مربوط به هدفی که برای آن‌ها در نظر گرفته شده است را انجام دهند.
همان طور که در هر زبان برنامه نویسی دستوراتی برای کارهای مختلف مانند تعریف یک متغیر یا ایجاد یک حلقه و ... در نظر گرفته شده است، در زبان‌های برنامه نویسی شیء گرا نیز دستوراتی وجود دارد تا بتوان قطعه کدی را بر اساس مفهوم کلاس بسته بندی کرد.
به طور مثال قطعه کد زیر را در زبان برنامه نویسی سی شارپ در نظر بگیرید.
class Player
{
   public string Name;
   public int Age;
   public void Walk()
   {
      // کدهای مربوط به پیاده سازی راه رفتن
   }
   public void Run()
   {
      // کدهای مربوط به پیاده سازی دویدن
   }
}
در این قطعه کد با استفاده از کلمه کلیدی class در زبان سی شارپ کلاسی ایجاد شده است که دارای دو ویژگی نام و سن و دو رفتار راه رفتن و دویدن است.
این کلاس به چه دردی می‌خورد؟ کجا می‌توانیم از این کلاس استفاده کنیم؟
پاسخ این است که این کلاس ممکن است برای ما هیچ سودی نداشته باشد و هیچ کجا نتوانیم از آن استفاده کنیم. اما بیایید فرض کنیم برنامه نویسی هستیم که قصد داریم یک بازی فوتبال بنویسیم. به جای آنکه قطعات کد مجزایی برای هر یک از بازیکنان و کنترل رفتار و ویژگی‌های آنان بنویسیم با اندکی تفکر به این نکته پی می‌بریم که همه بازیکنان مشترکات بسیاری دارند و به عبارتی در یک گروه یا کلاس قابل دسته بندی هستند. پس سعی می‌کنیم نقشه یا قالبی برای بازیکن‌ها ایجاد کنیم که دربردارنده ویژگی‌ها و رفتارهای آن‌ها باشد.
همان طور که در نقشه ساختمان نمی‌توانیم زندگی کنیم این کلاس هم هنوز آماده انجام کارهای واقعی نیست. چراکه برخی مقادیر هنوز برای آن تنظیم نشده است. مانند نام بازیکن و سن و ....
و همان طور که برای سکونت لازم است ابتدا یک ساختمان از روی نقشه ساختمان بسازیم برای استفاده واقعی از کلاس یاد شده نیز باید از روی آن شیء بسازیم. به این فرآیند وهله سازی یا نمونه سازی نیز می‌گویند. یک زبان برنامه نویسی شیء گرا دستوراتی را برای وهله سازی نیز در نظر گرفته است. در C# کلمه کلیدی new این وظیفه را به عهده دارد.
Player objPlayer = new Player();
objPlayer.Name = “Ali Karimi”;
objPlayer.Age = 30;
objPlayer.Run();
وقتی فرآیند وهله سازی صورت می‌گیرد یک نمونه یا شیء از آن کلاس در حافظه ساخته می‌شود که در حقیقت می‌توانید آنرا همان کدهای کلاس تصور کنید با این تفاوت که مقداردهی‌های لازم صورت گرفته است. به دلیل تعیین مقادیر لازم، حال شیء تولید شده می‌تواند به خوبی اعمال پیش بینی شده را انجام دهد. توجه نمایید در اینجا پیاده سازی داخلی رفتار دویدن و اینکه مثلاً در هنگام فراخوانی آن چه کدی باید اجرا شود تا تصویر یک بازیکن در حال دویدن در بازی نمایش یابد مد نظر و موضوع بحث ما نیست. بحث ما چگونگی سازماندهی کد‌ها توسط مفهوم کلاس و شیء است. همان طور که مشاهده می‌کنید ما تمام جزییات بازیکن‌ها را یکبار در کلاس پیاده سازی کرده ایم اما به تعداد دلخواه می‌توانیم از روی آن بازیکن‌های مختلف را ایجاد کنیم. همچنین به راحتی رفتار دویدن یک بازیکن را فراخوانی میکنیم بدون آنکه پیاده سازی کامل آن در اختیار و جلوی چشم ما باشد.
تمام آنچه که بازیکن برای انجام امور مربوط به خود نیاز دارد در کلاس بازیکن کپسوله می‌شود. بدیهی است در یک برنامه واقعی ویژگی‌ها و رفتارهای بسیار بیشتری باید برای کلاس بازیکن در نظر گرفته شود. مانند پاس دادن، شوت زدن و غیره.
به این ترتیب ما برای هر برنامه می‌توانیم مسئله اصلی را به تعدادی مسئله کوچکتر تقسیم کنیم و وظیفه حل هر یک از مسائل کوچک را به یک شیء واگذار کنیم. و بر اساس اشیاء تشخیص داده شده کلاس‌های مربوطه را بنویسیم. برنامه نویسی شیء گرا سبب می‌شود تا مسئله توسط تعدادی شیء که دارای نمونه‌های متناظری در دنیای واقعی هستند حل شود که این امر زیبایی و خوانایی و قابلیت نگهداری و توسعه برنامه را بهبود می‌دهد.
احتمالاً تاکنون متوجه شده اید که برای نگهداری ویژگی‌های اشیاء از متغیر‌ها و برای پیاده سازی رفتارها یا کارکرد‌های اشیاء از توابع استفاده میکنیم.
با توجه به این که هدف این مطلب بررسی مفهوم شیء گرائی بود و نه جزییات برنامه نویسی، بنابراین بیان برخی مفاهیم در این رابطه را که بیشتر در مهندسی نرم افزار معنا دارند تا در دنیای واقعی در مطالب بعدی بررسی می‌کنیم.
مطالب
مفاهیم برنامه نویسی ـ مروری بر فیلدها، متدها و ساخت اشیاء
شکستن یک مسئله بزرگ به تعدادی مسئله کوچک‌تر راهکار موثری برای حل آن است. این امر در برنامه نویسی نیز که هدف آن چیزی جز حل یک مسئله نیست همواره مورد توجه بوده است. به همین دلیل روش هایی که به کمک آن‌ها بتوان یک برنامه بزرگ را به قطعات کوچکتری تقسیم کرد تا هر قطعه کد مسئول انجام کار خاصی باشد پیشتر به زبان‌های برنامه نویسی اضافه شده اند. یکی از این ساختار‌ها تابع (Function) نام دارد. برنامه ای که از توابع برای تقسیم کدهای برنامه استفاده می‌کند یک برنامه ساخت‌یافته می‌گوییم.
در مطلب پیشین به پیرامون خود نگاه کردیم و اشیاء گوناگونی را مشاهده کردیم که در حقیقت دنیای ما را تشکیل داده اند و فعالیت‌های روزمره ما با استفاده از آن‌ها صورت می‌گیرد. ایده ای به ذهنمان رسید. اشیاء و مفاهیم مرتبط به آن می‌تواند روش بهتر و موثرتری برای تقسیم کدهای برنامه باشد. مثلاً اگر کل کدهای برنامه که مسئول حل یکی از مسئله‌های کوچک یاد شده است را یکجا بسته بندی کنیم و اصولی که از اشیاء واقعی پیرامون خود آموختیم را در مورد آن رعایت کنیم به برنامه بسیار با کیفیت‌تری از نظر خوانایی، راحتی در توسعه، اشکال زدایی ساده‌تر و بسیاری موارد دیگر خواهیم رسید.
توسعه دهندگان زبان‌های برنامه نویسی که با ما در این مورد هم عقیده بوده اند دست به کار شده و دستورات و ساختار‌های لازم برای پیاده کردن این ایده را در زبان برنامه نویسی قرار دادند و آن را زبان برنامه نویسی شیء گرا نامیدند. حتی جهت برخورداری از قابلیت استفاده مجدد از کد و موارد دیگر به جای آنکه کدها را در بسته هایی به عنوان یک شیء خاص قرار دهیم آن‌ها را در بسته هایی به عنوان قالب یا نقشه ساخت اشیاء خاصی که در ذهن داریم قرار می‌دهیم. یعنی مفهوم کلاس یا رده که پیشتر اشاره شد. به این ترتیب یک بار می‌نویسیم و بارها استفاده می‌کنیم. مانند همان مثال بازیکن در بخش نخست. هر زمان که لازم باشد با استفاده از دستورات مربوطه از روی کدهای کلاس که نقشه یا قالب ساخت اشیاء هستند شیء مورد نظر را ساخته و در جهت حل مسئله مورد نظر به کار می‌بریم.
حال برای آنکه به طور عملی بتوانیم از ایده شیء گرایی در برنامه هایمان استفاده کنیم و مسائل بزرگ را حل کنیم لازم است ابتدا مقداری با جزییات و دستورات زبان در این مورد آشنا شویم.
تذکر: دقت کنید برای آنکه از ایده شیء گرایی در برنامه‌ها حداکثر استفاده را ببریم مفاهیمی در مهندسی نرم افزار به آن اضافه شده است که ممکن است در دنیای واقعی نیازی به طرح آن‌ها نباشد. پس لطفاً تلاش نکنید با دیدن هر مفهوم تازه بلافاصله سعی در تطبیق آن با محیط اطراف کنید. هر چند بسیاری از آن‌ها به طور ضمنی در اشیاء پیرامون ما نیز وجود دارند.
زبان برنامه نویسی مورد استفاده برای بیان مفاهیم برنامه نویسی در این سری مقالات زبان سی شارپ است. اما درک برنامه‌های نوشته شده برای علاقه مندان به زبان‌های دیگری مانند وی بی دات نت نیز دشوار نیست. چراکه اکثر دستورات مشابه است و تبدیل Syntax نیز به راحتی با اندکی جستجو میسر می‌باشد. لازم به یادآوری است زبان سی شارپ به بزرگی یا کوچکی حروف حساس است.

تشخیص و تعریف کلاس‌های برنامه

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

public class Rectangle
{
   public double Width;
   public double Height;
 
   public double Area()
   {
      return Width*Height;
   }
 
   public double Perimeter()
   {
      return 2*(Width + Height);
   }
}

در این قطعه برنامه نکات زیر قابل توجه است:
  • کلاس با کلمه کلیدی class تعریف می‌شود.
  • همان طور که مشاهده می‌کنید تعریف کلاس با کلمه public آغاز شده است. این کلمه محدوده دسترسی به کلاس را تعیین می‌کند. در اینجا از کلمه public استفاده کردیم تا بخش‌های دیگر برنامه امکان استفاده از این کلاس را داشته باشند.
  • پس از کلمه کلیدی class نوبت به نام کلاس می‌رسد. اگرچه انتخاب نام مورد نظر امری اختیاری است اما در آینده حتماً اصول و قراردادهای نام‌گذاری در دات‌نت را مطالعه نمایید. در حال حاضر حداقل به خاطر داشته باشید تا انتخاب نامی مناسب که گویای کاربرد کلاس باشد بسیار مهم است.
  • باقیمانده کد، بدنه کلاس را تشکیل می‌دهد. جاییکه ویژگی ها، رفتار‌ها و ... یا به طور کلی اعضای کلاس تعریف می‌شوند.
نکته: کلماتی مانند public که پیش از تعریف کلاس یا اعضای آن قرار می‌گیرند Modifier یا پیراینده نام دارند. که البته به نظر من ترجمه این گونه واژه‌ها از کارهای شیطان است. بنابراین از این پس بهتر است همان Modifier را به خاطر داشته باشید. از آنجا که public مدیفایری است که سطح دسترسی را تعیین می‌کند به آن یک Access Modifier می‌گویند. در یک بخش از این سری مقالات تمامی مدیفایرها بررسی خواهند شد.

ایجاد شیء از یک کلاس و نحوه دسترسی به شیء ایجاد شده

شیء و کلاس چیزهای متفاوتی هستند. یک کلاس نوع یک شیء را تعریف می‌کند. اما یک شیء یک موجودیت عینی و واقعی بر اساس یک کلاس است. در اصطلاح از شیء به عنوان یک نمونه (Instance) یا وهله ای از کلاس مربوطه یاد می‌کنیم. همچنین به عمل ساخت شیء نمونه سازی یا وهله سازی گوییم.
برای ایجاد شیء از کلمه کلیدی new و به دنبال آن نام کلاسی که قصد داریم بر اساس آن یک شیء بسازیم استفاده می‌کنیم. همان طور که اشاره شد کلاس یک نوع را تعریف می‌کند. پس از آن می‌توان همانند سایر انواع مانند int, string, … برای تعریف متغیر استفاده نمود. به مثال زیر توجه کنید.
Rectangle rectangle = new Rectangle();
در این مثال rectangle که با حرف کوچک شروع شده و می‌توانست هر نام دلخواه دیگری باشد ارجاعی به شیء ساخته شده را به دست می‌دهد. وقتی نمونه ای از یک کلاس ایجاد می‌شود یک ارجاع به شیء تازه ساخته شده برای برنامه نویس برگشت داده می‌شود. در این مثال rectangle یک ارجاع به شیء تازه ساخته شده است یعنی به آن اشاره می‌کند اما خودش شامل داده‌های آن شیء نیست. تصور کنید این ارجاع تنها دستگیره ای برای شیء ساخته شده است که دسترسی به آن را برای برنامه نویس میسر می‌کند. درک این مطلب از این جهت دارای اهمیت است که بدانید می‌شود یک دستگیره یا ارجاع دیگر بسازید بدون آنکه شیء جدیدی تولید کنید.
Rectangle rectangle;
البته توصیه نمی‌کنم چنین ارجاعی را تعریف کنید چرا که به هیچ شیء خاصی اشاره نمی‌کند. و تلاش برای استفاده از آن منجر به بروز خطای معروفی در برنامه خواهد شد. به هر حال یک ارجاع می‌توان ساخت چه با ایجاد یک شیء جدید و یا با نسبت دادن یک شیء موجود به آن.
Rectangle rectangle1 = new Rectangle();
Rectangle rectangle2 = rectangle1;
در این کد دو ارجاع یا دستگیره ایجاد شده است که هر دو به یک شیء اشاره می‌کنند. بنابراین ما با استفاده از هر دو ارجاع می‌توانیم به همان شیء واحد دسترسی پیدا کنیم و اگر مثلاً با rectangle1 در شیء مورد نظر تغییری بدهیم و سپس با rectangle2 شیء را مورد بررسی قرار دهیم تغییرات داده شده قابل مشاهده خواهد بود چون هر دو ارجاع به یک شیء اشاره می‌کنند. به همین دلیل کلاس‌ها را به عنوان نوع ارجاعی می‌شناسیم در مقایسه با انواع داده دیگری که اصطلاحاً نوع مقداری هستند.
حالا می‌توان شیء ساخته شده را با استفاده از ارجاعی که به آن داریم به کار برد.
Rectangle rectangle = new Rectangle();
rectangle.Width = 10.5;
rectangle.Height = 10;
double a = rectangle.Area();
ابتدا عرض و ارتفاع شیء چهارضلعی را مقدار دهی کرده و سپس مساحت را دریافت کرده ایم. از نقطه برای دسترسی به اعضای یک شیء استفاده می‌شود.

فیلدها

اگر به تعریف کلاس دقت کنید مشخص است که دو متغییر Width و Height را با سطح دسترسی عمومی تعریف کرده ایم.
به متغیرهایی از هر نوع که مستقیماً درون کلاس تعریف شوند (و نه مثلاً داخل یک تابع درون کلاس) فیلد می‌گوییم. فیلدها از اعضای کلاس دربردارنده آن‌ها محسوب می‌شوند.
تعریف فیلدها مستقیماً در بدنه کلاس با یک Access Modifier شروع می‌شود و به دنبال آن نوع فیلد و سپس نام دلخواه برای فیلد می‌آید.
تذکر: نامگذاری مناسب یکی از مهمترین اصولی است که یک برنامه نویس باید همواره به آن توجه کافی داشته باشد و به شدت در بالا رفتن کیفیت برنامه موثر است. به خاطر داشته باشید تنها اجرا شدن و کار کردن یک برنامه کافی نیست. رعایت بسیاری از اصول مهندسی نرم افزار که ممکن است نقش مستقیمی در کارکرد برنامه نداشته باشند موجب سهولت در نگهداری و توسعه برنامه شده و به همان اندازه کارکرد صحیح برنامه مهم هستند. بنابراین مجدداً شما را دعوت به خواندن مقاله یاد شده بالا در مورد اصول نامگذاری صحیح می‌کنم. هر مفهوم تازه ای که می‌آموزید می‌توانید به اصول نامگذاری همان مورد در مقاله پیش گفته مراجعه نمایید. همچنین افزونه هایی برای Visual Studio وجود دارد که شما را در زمینه نامگذاری صحیح و بسیاری موارد دیگر هدایت می‌کنند که یکی از مهمترین آن‌ها Resharper نام دارد.
مثال:
// public field (Generally not recommended.)
public double Width;
همان طور که در این قطعه کد به عنوان توضیح درج شده است استفاده از فیلدهایی با دسترسی عمومی توصیه نمی‌شود. علت آن واضح است. چون هیچ کنترلی برای مقداری که برای آن در نظر گرفته می‌شود نداریم. به عنوان مثال امکان دارد یک مقدار منفی برای عرض یا ارتفاع شیء درج شود حال آنکه می‌دانیم عرض یا ارتفاع منفی معنا ندارد. در قسمت بعدی این سری مقالات این مشکل را بررسی و حل خواهیم نمود.
فیلد‌ها معمولاً با سطح دسترسی خصوصی و برای نگهداری از داده‌هایی که مورد نیاز بیش از یک متد (یا تابع) درون کلاس است و آن داده‌ها باید پس از خاتمه کار یک متد همچنان باقی بمانند استفاده می‌شود. بدیهی است در غیر اینصورت به جای تعریف فیلد می‌توان از متغیرهای محلی (متغیری که درون خود تابع تعریف می‌شود) استفاده نمود.
همان طور که پیشتر اشاره شد برای دسترسی به یک فیلد ابتدا یک نقطه پس از نام شیء درج کرده و سپس نام فیلد مورد نظر را می‌نویسیم.
Rectangle rectangle = new Rectangle();
rectangle.Width = 10.5;
در هنگام تعریف یک فیلد در صورت نیاز می‌توان برای آن یک مقدار اولیه را در نظر گرفت. مانند مثال زیر:
public class Rectangle
{
   public double Width = 5;
   // ...
}

متدها

متدها قطعه کدهایی شامل یک سری دستور هستند. این مجموعه دستورات با فراخوانی متد و تعیین آرگومان‌های مورد نیاز اجرا می‌شوند. در زبان سی شارپ به نوعی تمام دستورات در داخل متدها اجرا می‌شوند. در این زبان تمامی توابع در داخل کلاس‌ها تعریف می‌شوند و بنابراین همه متد هستند.
متدها نیز مانند فیلد‌ها در داخل کلاس تعریف می‌شوند. ابتدا یک Access Modifier سطح دسترسی را تعیین می‌نماید. سپس به ترتیب نوع خروجی، نام متد و لیست پارامترهای آن در صورت وجود درج می‌شود. به مجموعه بخش‌های یاد شده امضای متد می‌گویند.
پارامترهای یک متد داخل یک جفت پرانتز قرار می‌گیرند و با کاما (,) از هم جدا می‌شوند. یک جفت پرانتز خالی نشان دهنده آن است که متد نیاز به هیچ پارامتری ندارد.
بار دیگر به بخش تعریف متدهای کلاسی که ایجاد کردیم توجه نمایید.
public class Rectangle
{
   // ...
 
   public double Area()
   {
      return Width*Height;
   }
 
   public double Perimeter()
   {
      return 2*(Width + Height);
   }
}
در این کلاس دو متد به نام‌های  Area و Perimeter به ترتیب برای محاسبه مساحت و محیط چهارضلعی تعریف شده است. همانطور که پیشتر اشاره شد متدها برای پیاده سازی رفتار اشیاء یا همان کارکردهای آن‌ها استفاده می‌شوند. در این مثال شیء ما قادر است مساحت و محیط خود را محاسبه نماید. چه شیء خوش رفتاری!
همچنین توجه نمایید این شیء برای محاسبه مساحت و محیط خود نگاهی به ویژگی‌های خود یعنی عرض و ارتفاعش که در فیلدهای آن نگهداری می‌کنیم می‌اندازد.
فراخوانی متد یک شیء همانند دسترسی به فیلد آن است. ابتدا نام شیء سپس یک نقطه و به دنبال آن نام متد مورد نظر به همراه پرانترها. آرگومان‌های مورد نیاز در صورت وجود داخل پرانتزها قرار می‌گیرند و با کاما از هم جدا می‌شوند. که البته در این مثال متد ما نیازی به آرگومان ندارد. به همین دلیل برای فراخوانی آن تنها یک جفت پرانتز خالی قرار می‌دهیم.
در این بخش به دو مفهوم پارامتر و آرگومان اشاره شد. تفاورت آن‌ها چیست؟
در هنگام تعریف یک متد نام و نوع پارامترهای مورد نیاز را تعیین و درج می‌نماییم. حال وقتی قصد فراخوانی متد را داریم باید مقادیر واقعی که آرگومان نامیده می‌شود را برای هر یک از پارامترهای تعریف شده فراهم نماییم. نوع آرگومان باید با نوع پارامتر تعریف شده تطبیق داشته باشد. اما اگر یک متغیر را به عنوان آرگومان در هنگام فراخوانی متد استفاده می‌کنیم نیازی به یکسان بودن نام آن متغیر و نام پارامتر تعریف شده نیست.
متدها می‌توانند یک مقدار را به کدی که آن متد را فراخوانی کرده است بازگشت دهند.
Rectangle rectangle = new Rectangle();
rectangle.Width = 10.5;
rectangle.Height = 10;
double p = rectangle.Perimeter();
در این مثال مشاهده می‌کنید که پس از فراخوانی متد Perimeter مقدار بازگشتی آن در متغیری به نام p قرار گرفته است. اگر نوع خروجی یک متد که در هنگام تعریف آن پیش از نام متد قرار می‌گیرد void یا پوچ نباشد، متد می‌تواند مقدار مورد نظر را با استفاده از کلمه کلیدی return بازگشت دهد. کلمه return و به دنبال آن مقداری که از نظر نوع باید با نوع خروجی تعیین شده تطبیق داشته باشد، مقدار درج شده را به کد فراخوان متد بازگشت می‌دهد.
نکته: کلمه return علاوه بر بازگشت مقدار مورد نظر سبب پایان اجرای متد نیز می‌شود. حتی در صورتی که نوع خروجی یک متد void تعریف شده باشد استفاده از کلمه return بدون اینکه مقداری به دنبال آن بیاید می‌تواند برای پایان اجرای متد، در صورت نیاز و مثلاً برقراری شرطی خاص مفید باشد. بدون کلمه return متد زمانی پایان می‌یابد که به پایان قطعه کد بدنه خود برسد. توجه نمایید که در صورتی که نوع خروجی متد چیزی به جز void است استفاده از کلمه return به همراه مقدار مربوطه الزامی است.
مقدار خروجی یک متد را می‌توان هر جایی که مقداری از همان نوع مناسب است مستقیماً به کار برد. همچنین می‌توان آن را در یک متغیر قرار داد و سپس از آن استفاده نمود.
به عنوان مثال کلاس ساده زیر را در نظر بگیرید که متدی دارد برای جمع دو عدد.
public class SimpleMath
{
   public int AddTwoNumbers(int number1, int number2)
   {
      return number1 + number2;
   }
}
و حال دو روش استفاده از این متد:
SimpleMath obj = new SimpleMath();

Console.WriteLine(obj.AddTwoNumbers(1, 2));

int result = obj.AddTwoNumbers(1, 2);
Console.WriteLine(result);
در روش اول مستقیماً خروجی متد مورد استفاده قرار گرفته است و در روش دوم ابتدا مقدار خروجی در یک متغیر قرار گرفته است و سپس از مقدار درون متغیر استفاده شده است. استفاده از متغیر برای نگهداری مقدار خروجی اجباری نبوده و تنها جهت بالا بردن خوانایی برنامه یا حفظ مقدار خروجی تابع برای استفاده‌های بعدی به کار می‌رود.

در بخش‌های بعدی بحث ما در مورد سایر اعضای کلاس و برخی جزییات پیرامون اعضای پیش گفته خواهد بود.
 
نظرات مطالب
مفاهیم برنامه نویسی ـ مروری بر کلاس و شیء
حالا با توجه به توضیحات بالا که گفته شد «در نقشه ساختمان نمی‌توانید زندگی کنید اما در خود ساختمان می‌توانید.» با یک پراید می‌شود رانندگی کرد اما با ماشین که بیشتر یک مفهوم است خیر. نه؟ 
مطالب دوره‌ها
معرفی #F
یکی از قدیمی‌ترین روش‌های برنامه نویسی روش برنامه نویسی تابع گراست. زبان IPL به عنوان قدیمی‌ترین زبان برنامه نویسی تابع گرا در سال 1955(یک سال قبل از خلق فرترن) است. دومین زبان تابع گرا زبان LISP بوده است که در سال 1958(یک سال قبل از خلق کوبول) متولد شد. هر دو زبان کوبول و فرترن زبان‌های امری و رویه ای بودند. بعد از آن‌ها در سال 1970 شروع عرصه زبان‌های شی گرا بود و تا امروز بیشترین کاربرد را در تولید نرم افزار‌ها داشته اند.
#F یک زبان برنامه نویسی تابع گرا است و گزینه ای بسیار مناسب برای حل مسایل کامپیوتری. اما استفاده از زبان برنامه نویسی تابعی محض برای نوشتن و تولید پروژه‌های نرم افزاری مناسب نمی‌باشد. به همین دلیل نیاز به استفاده  از این زبان‌ها در کنار سایر زبان‌های شی گرا احساس می‌شود. #F یک زبان همه منظوره دات نت است که برای حالت اجرا به صورت همه منظوره استفاده می‌شود. برخی زبان‌های تابع گرا دیگر نظیر Lisp و Haskel و OCaml (که #F بسیار نزدیک به این زبان می‌باشد) با دستورات زبان اجرای سفارشی کار می‌کنند و این مسئله باعث نبود زبان برنامه نویسی چند فعالیته می‌شود. شما می‌توانید از برنامه نویسی توصیفی هم استفاده کنید و توابع را به راحتی با هم ترکیب کنید و یا روش‌های شی گرایی و دستوری را در همان برنامه استفاده کنید.

تاریخچه

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

استفاده در دات نت
#F کاملا از دات نت پشتیبانی می‌کند و این قابلیت را به برنامه نویسان می‌دهد که هر چیزی را که در سایر زبان‌های دات نت استفاده می‌کنند در این زبان نیز قابل استفاده باشد. همچنین می‌تواند برای کد نویسی IL نیز استفاده شود.
#F به راحتی قابل اجرا در محیط لینوکس و مکینتاش نیز است.

استفاده کنندگان #F
#F در شرکت مایکرو سافت به شدت استفاده می‌شود. رالف هربریش که یکی از مدیران دوگانه گروه بازی‌های مایکروسافت و از متخصصین آموزش ماشین است در این باره می‌گوید:
*اولین برنامه کاربردی برای انتقال 110 گیگا بایت از طریق 11000 فایل متنی در بیش از 300 دایرکتوری و وارد کردن آن‌ها در دیتابیس بود. کل برنامه 90 خط بود و در کمتر از 18 ساعت توانست اطلاعات مربوطه را در SQL ذخیره کند. یعنی ده هزار خط برنامه متنی در هر ثانیه مورد پردازش قرار گرفت.همچنین توجه کنید که من برنامه را بهینه نکردم بلکه به صورت کاملا عادی نوشتم. این جواب بسیار قابل توجه بود زیرا من انتظار داشتم حداقل یک هفته زمان ببرد.

دومین برنامه، برنامه پردازش میلیون‌ها Feekback مشتریان بود. ما روابط مدلی زیادی را توسعه دادیم و من این روابط را در #F قرار دادم و داده‌های مربوط به SQL را در آن فراخوانی کردم و نتایج را در فایل داده ای MATLAB قرار دادم و کل پروژه در حد صد خط بود به همراه توضیحات. زمان اجرای پروژه برای دریافت خروجی ده دقیقه بود در حالی که  همین کار را توسط برنامه #C قبلا توسعه داده بودیم که بیش از هزار خط بود و نزدیک به دو روز زمان می‌برد.*


استفاده از #F تنها در مایکروسافت نیست بلکه در سایر شرکت‌های بزرگ و نام دار نیز استفاده می‌شود و همچنان نیز در حال افزایش است. شرکت
Derivative One  که یک شرکت بزرگ در تولید نرم افزار‌های شبیه ساز مالی است مدل‌های مالی نرم افزار‌های خود را در #F پیاده سازی کرده است.

چرا #F ؟
همیشه باید دلیلی برای انتخاب یک زبان باشد. در حال حاضر #F یکی از قدرتمند‌ترین زبان‌های برنامه نویسی است. در ذیل به چند تا از این دلایل اشاره خواهم کرد:
  • #F یک زبان استنباطی است. برای مثال در هنگام تعریف متغیر و شناسه نیاز به ذکر نوع آن نیست. کامپایلر با توجه به مقدار اولیه تصمیم می‌گیرد که متغیر از چه نوعی است.
  • بسیار راحت می‌توان به کتابخانه قدرتمند دات نت دسترسی داشت و از آن‌ها در پروژه‌های خود استفاده کنید.
  • #F از انواع روش‌های برنامه نویسی نظیر تابعی، موازی، شی گرا و دستوری پیشتیبانی می‌کند.
  • برخلاف تصور بعضی افراد، در #F امکان تهیه و توسعه پروژه‌های وب و ویندوز و حتی WPF و Silverlight هم وجود دارد.
  • نوع کدنویسی و syntax زبان #F به برنامه نویسان این اجازه را میدهد که الگوریتم‌های پیچیده مورد نظر خود را بسیار راحت‌تر پیاده سازی کنند. به همین دلیل بعضی برنامه نویسان این زبان را با Paython مقایسه می‌کنند.
  • #F به راحتی با زبان #C و VB تعامل دارد. یعنی می‌تونیم در طی روند تولید پروژه از قدرت‌های هر سه زبان بهره بگیریم. 
  • طبق آمار گرفته شده از برنامه نویسان، #F به دلیل پشتیبانی از نوع داده ای قوی و مبحث Unit Measure، خطا‌ها و Bug‌های نرم افزار را کاهش می‌دهد.
  • به دلیل پشتیبانی VS.Net از زبان #F و وجود ابزار قدرتمند برای توسعه نرم افزار به کمک این زبان (unitTesting و ابزارهای debuging و ..)این زبان تبدیل به قدرت‌های دنیای برنامه نویسی شده است.
  • #F یک زبان بسیار مناسب برای پیاده سازی الگوریتم‌های data-mining است.
  • #F از immutability در تعریف شناسه‌ها پشتیبانی می‌کند.(در فصل‌های مربوطه بحث خواهد شد)
  • و.....

چرا #F نه ؟

#F هم مانند سایر زبان ها، علاوه بر قدرت بی همتای خود دارای معایبی نیز می‌باشد. (مواردی که در پایین ذکر می‌شود صرفا بر اساس تجربه است نه مستندات).

  • نوع کدنویسی و syntax زبان #F برای برنامه نویسان دات بیگانه ( و البته کمی آزار دهنده) است. اما به مرور این مشکل، تبدیل به قدرت برای مانور‌های مختلف در کد می‌شود.
  • درست است که در #F امکان تعریف اینترفیس وجود دارد و یک کلاس می‌تواند اینترفیس مورد نظر را پیاده سازی کند ولی هنگام فراخوانی متد‌های کلاس (اون هایی که مربوط به اینترفیس است) حتما باید instance کلاس مربوطه به اینترفیس cast شود و این کمی آزار دهنده است.(در فصل شی گرایی در این مورد شرح داده شده است).
  • زبان #F در حال حاضر توسط  VS.Net به صورت Visual پشتیبانی نمی‌شود.(امکاناتی نظیر drag drop کنترل‌ها برای ساخت فرم و ....). البته برای حل این مشکل نیز افزونه هایی وجود دارد که در جای مناسب بحث خواهیم کرد.

آیا برای یادگیری #F نیاز به داشتن دانش در برنامه نویسی #C یا VBداریم؟

به طور قطع نه. نوع کد نویسی (نه مفاهیم)در #F کاملا متفاوت در #C است و این دو زبان از نظر کد نویسی شباهتشان در حد صفر است. برای یادگیری #F بیشتر نیاز به داشتن آگاهی اولیه در برنامه نویسی (آشنایی با تابع، حلقه تکرار، متغیر ها) و شی گرایی(مفاهیم کلاس، اینترفیس، خواص، متد‌ها و...) دارید تا آشنایی با #C یا VB.

چگونه شروع کنیم؟

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


مطالب
طراحی شیء گرا: OO Design Heuristics - قسمت اول
هدف از طراحی چیست؟

ما طراحی می‌کنیم تا علاوه بر نیاز‌های عملیاتی، به نیاز‌های غیر عملیاتی (Non Functional Requirements) نیز فکر کنیم؛ در حالیکه در زمان برنامه نویسی صرفا به Functionality فکر می‌کنیم.

کتاب Object Oriented Design Heuristics اولین کتاب در زمینه طراحی و توسعه شیء گرا می‌باشد. خواندن آن برای برنامه نویسان در هر رده ای که هستند، مفید خواهد بود و میتوانند از این Heuristicها (قواعد شهودی) به عنوان ابزاری برای تبدیل شدن به یک توسعه دهنده برتر، استفاده کنند.

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

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

Introduction to Classes and Objects

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

این مفاهیم را با یک مثال واقعی، بهتر می‌توان شرح داد. یک اتاق پر از جمعیت را درنظر بگیرید؛ اگر شما می‌پرسیدید «چه تعداد از حاضرین در این اتاق می‌توانند یک ساعت زنگدار(alarm clock ) را با در دست داشتن تمام قطعات آن، بسازند؟» در بهترین حالت یک یا دو نفر تمایل داشتند دست خود را بالا ببرند. اگر در همین اتاق می‌پرسیدید، «چه تعداد از حاضرین در این اتاق می‌توانند یک ساعت زنگدار را برای ساعت 9 صبح تنظیم کنند؟» بدون شک بیشتر جمعیت تمایل داشتند دست خود را بالا ببرند.

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

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

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

این فلسفه، دقیقا یکی از ایده‌های پایه‌ای در پارادایم شیءگرا می‌باشد. تمام جزئیات پیاده سازی در سیستم شما باید در پشت یک واسط عمومی مستحکم و سازگار، از کاربران آنها پنهان باشد. نیاز کاربران، دانستن درباره واسط عمومی می‌باشد؛ اما هرگز مجاز به دیدن جزئیات پیاده سازی آنها نیستند. با این روش، پیاده ساز میتواند به هرشکلی که مناسب است، پیاده سازی را تغییر دهد؛ درحالیکه واسط عمومی مانند سابق می‌باشد. به عنوان مسافری که مکرر سفر میکنم، به شما اطمینان میدهم که استفاده از ساعت‌های زنگدار با وجود عدم اطلاع از پیاده سازی آنها، فواید عظیمی دارند. در هتل‌های زیادی که از دسته بندی‌های گسترده‌ای از ساعت‌ها مانند الکتریکی، قابل کوک (windup)، باتری خور، در هر دو مدل دیجیتال و آنالوگ استفاده میکنند، اقامت کرده‌ام. یکبار هم اتفاق نیفتاده‌است در حالیکه در هواپیما نشسته باشم، نگران این باشم که قادر نخواهم بود از ساعت زنگی اتاقم در هتل استفاده کنم.

بیشتر خوانندگان این کتاب، با وجود اینکه در نزدیکی آنها شاید ساعت زنگداری هم نباشد، ولی منظور بنده را با عبارت «ساعت زنگدار» متوجه شدند. به چه دلیل؟ شما در زندگی خودتان ساعت‌های زنگدار زیادی را می‌بینید و متوجه می‌شوید که همه آنها از یکسری خصوصیات مشترک مانند زمان، یک زمان هشدار و طراحی‌ای که مشخص میکند هشدار روشن یا خاموش است، بهره می‌برند. همچنین متوجه می‌شوید که همه ساعت‌های زنگداری که دیده‌اید امکان تنظیم کردن زمان، تنظیم زمان هشدار و روشن و خاموش کردن هشدار را به شما می‌دهند. در نتیجه، شما الان مفهومی را به نام «ساعت زنگدار» دارید که مفهومی را از داده و رفتار، در یک بسته بندی مرتب برای همه ساعت‌های زنگدار، تسخیر می‌کند. این مفهوم به عنوان یک Class (کلاس) شناخته می‌شود. یک ساعت زنگدار فیزیکی که شما در دست خود آن را نگه داشته‌اید، یک Object (وهله، Instance) ای از کلاس ساعت زنگدار می‌باشد. رابطه بین مفهوم کلاس و وهله، Instantiation Relationship (وهله سازی) نام دارد. به یک object، ساعت زنگدار وهله سازی شده (Instantiated) از کلاس ساعت زنگدار گفته می‌شود؛ در حالیکه از کلاس ساعت زنگدار به عنوان تعمیم (Generalization) از همه object‌های کلاس ساعت زنگدار که شما با آنها روبرو شده‌اید، یاد می‌شود. 

شکل 2.1 An Alarm Class and Its Objects 

شکل 2.1 An Alarm Class and Its Objects

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

یک object همیشه دارای 4 جنبه مهم زیر خواهد بود:
  • هویت خود (ممکن است آدرس آن در حافظه باشد) - its own identity
  • خواص کلاس خود (معمولا استاتیک) و مقادیر این خواص (معمولا پویا) - attributes of its class 
  • رفتار کلاس خود (از دید پیاده ساز) -  behavior of its class
  • واسط منتشر شده کلاس خود (از دید استفاده کننده) - published interface of its class

یک کلاس را  می توان با record definition (ساختار داده پایه، struct) و لیستی از عملیاتی که مجاز به کار بر روی این record definition هستند، پیاده سازی کرد. در زبان‌های رویه‌ای (Procedural) یافتن وابستگی داده‌ها در یک تابع معین، آسان می‌باشد. این کار را می‌توان به سادگی با بررسی کردن جزئیات پیاده سازی تابع و مشاهده نوع داده پارامترهای آن، مقادیر بازگشتی و متغییرهای محلی‌ای که تعریف شده‌اند، انجام داد. اگر قصد شما پیدا کردن وابستگی‌های تابعی بر روی یک داده می‌باشد، باید همه کد را بررسی کرده و به دنبال توابعی باشید که به داده شما وابسته هستند. در مدل شیء گرا، هر دو نوع وابستگی (داده به رفتار و رفتار به داده) به راحتی در دسترس می‌باشند. وهله‌ها، متغیرهایی از یک نوع داده کلاس هستند. جزئیات داخلی آنها باید فقط برای لیست توابع مرتبط با کلاس‌هایشان آشکار باشد. این محدودیت دسترسی به جزئیات داخلی وهله‌ها، Information Hiding نامیده می‌شود. اختیاری بودن این بحث در خیلی از زبان‌های شیء گرا ما را به سمت اولین قاعده شهودی هدایت می‌کند.

قاعده شهودی 2.1 
همه داده‌ها باید در داخل کلاس خود پنهان شده باشند. (All data should be hidden within its class)

با نقض این قاعده، امکان نگهداری را هم از دست می‌دهید. اجبار به پنهان کردن اطلاعات در مراحل طراحی و پیاده سازی، بخش عظیمی از فواید پارادایم شیء گرا می‌باشد. اگر داده به صورت عمومی تعریف شده باشد، تشخیص اینکه کدام بخش از عملیات (functionality) سیستم به آن داده وابسته است، سخت و مشکل خواهد بود. در واقع، نگاشت تغییرات داده به عملیات سیستم، همانند طراحی و پیاده سازی در دنیای action-oriented می‌باشد. ما مجبور می‌شویم برای تشخیص اینکه کدام عملیات به داده مورد نظر ما وابسته است، تمام عملیات سیستم را بررسی کنیم، تا به این ترتیب متوجه شویم.

برخی اوقات، یک توسعه دهنده استدلال می‌کند «نیاز دارم این بخش از داده را عمومی تعریف کنم زیرا ....» در این وضعیت، توسعه دهنده باید از خود سوال کند «کاری که تلاش دارم با این داده انجام دهم چیست و چرا کلاس این عملیات را خودش برای من انجام نمی‌دهد؟» در همه موارد  این کلاس است که به سادگی عملیات ضروری را فراموش کرده‌است. کمی بر روی شکل 2.2 فکر کنید. توسعه دهنده به صورت تصادفی فکر کرده است که عضو byte_offset را برای مجاز ساختن دسترسی تصادفی I/O، به صورت عمومی تعریف کند. اما چیزی که واقعا برای انجام این کار به آن نیاز داشت، تعریف یک operation بود (در زبان سی، توابع fseek و ftell برای ممکن کردن دسترسی تصادفی I/O، موجود هستند).

مراقب توسعه دهنده‌هایی که جسورانه می‌گویند: «ما می‌توانیم این بخش از داده را تغییر دهیم، زیرا هیچوقت تغییر نخواهد کرد!» باشید. طبق قانون برنامه نویسی مورفی، اولین بخشی که نیاز به تغییر خواهد داشت همین بخش از داده است.

شکل 2.2 Accidental Public Data   

 Accidental Public Data

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

شکل 2.3  The danger of public data 

 خطر داده‌های عمومی

مطالب
تزریق وابستگی‌ها در ASP.NET Core - بخش 1 - مقدمه
مقدمه

زمانیکه یک برنامه را بر پایه‌ی شیء گرایی طراحی می‌کنید و می‌نویسید، به صورت معمول جریان وابستگی‌ها در برنامه‌ی شما به صورت زیر است:


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

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

اصل معکوس سازی / وارونگی وابستگی‌ها Dependency Inversion Principle
 
یکی از اصول پنجگانه‌ی طراحی برنامه‌های شیء گرا  که با نام اصول SOLID  شناخته می‌شوند، اصل «وارونگی وابستگی‌ها» است که روشی برای مشکل جفت شدگی و وابستگی کلاس‌ها به یکدیگر را به صورت تئوری ارائه می‌دهد.

 اصل وارونگی وابستگی‌ها بیان می‌کند:
  •   ماژول‌های (کلاس‌های) سطح بالا نباید به ماژولهای (کلاس‌های) سطح پایین وابسته باشند و هر دو باید به انتزاعات وابسته باشند (برای مثال interface‌ها).
  •   انتزاعات نباید وابسته به جزئیات باشند؛ بلکه جزئیات (پیاده سازی) باید وابسته به انتزاعات باشند.

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

در شکل زیر، حالت عادی جریان کنترل را می‌بینید .
 


همانطور که می‌بینید، کلاس M برای اجرا، وابسته به کلاس N و متد F در درون آن است. در اینجا ما با استفاده از اینترفیس‌ها می‌توانیم جریان کنترل را معکوس یا وارونه کنیم که به این عمل «وارونگی کنترل یا Inversion of Control» می‌گویند.


 
شیء گرایی در واقع در مورد نحوه‌ی جریان کنترل است. در اینجا اینترفیس‌ها به ما کنترل کاملی را بر جریان کنترل (Flow of control) می‌دهند که با استفاده از این امکان می‌توانیم از نوشتن کدهای جفت شده، شکننده و کلاس‌هایی یکبار مصرف، بپرهیزیم.


الگوی Dependency Injection 
تزریق وابستگی یا Dependency Injection، یک الگوی طراحی است که از آن برای طراحی و پیاده سازی IoC Container‌ها استفاده می‌شود. این الگو به ما اجازه می‌دهد که اشیاء وابسته را خارج از کلاس بسازیم و آنها را به طریقی دیگر به کلاس، جهت استفاده ارائه دهیم. به‌وسیله‌ی DI ما ساخت و اتصال اشیاء وابسته به کلاس را از تعریف آن خارج می‌کنیم.

الگوی تزریق وابستگی 3 نوع کلاس را درگیر می‌کند:
  •  کلاس کلاینت / Client Class : کلاس کلاینت (کلاس وابسته) کلاسی است که به کلاس سرویس وابسته است .
  • کلاس سرویس  /  Service Class :  کلاس سرویس (وابستگی) کلاسی است که یک سرویس را به کلاس کلاینت ارائه می‌دهد.
  •  کلاس تزریق کننده / Injector Class  :  کلاس تزریق کننده، نمونه‌ای از کلاس سرویس را ساخته و به کلاس کلاینت، تزریق می‌کند.

شکل زیر وابستگی بین کلاس‌ها را شرح می‌دهد:

 
همانطور که می‌بینید، کلاس Injector، نمونه‌ای از کلاس سرویس را می‌سازد و آن را به نمونه‌ای از کلاس Client تزریق می‌کند. با این کار، DI، وظیفه‌ی ساخت یک نمونه از کلاس Service را از درون کلاس Client جدا می‌کند.

انواع تزریقات وابستگی‌ها:
به صورت کلی به سه روش و در سه مکان، امکان تزریق وابستگی کلاس سرویس، درون کلاس کلاینت وجود دارد:
  •   تزریق درون سازنده / Constructor Injection  : در تزریق درون سازنده، در سازنده‌ی کلاس کلاینت، لیستی از سرویس‌های مورد نیاز کلاس، که کلاس، برای عملکرد خود به آن‌ها «وابسته» است، ثبت می‌شوند و کلاس Injector، سرویس (وابستگی) مورد نظر را درون سازنده‌ی کلاس Client ارائه می‌دهد.
  •   تزریق درون Property  کلاس / Property Injection : در این حالت که همچنین با نام (Setter Injection) هم شناخته می‌شود، تزریق کننده، وابستگی را به وسیله‌ی یک Property عمومی کلاس کلاینت ارائه می‌دهد.
  •  تزریق درون متد / Method Injection  : در این حالت، خود کلاس کلاینت، یک پیاده سازی از یک interface را ارائه می‌کند که درون آن متدهایی برای ارائه‌ی وابستگی‌ها به کلاینت تعریف شده‌اند. در این وضعیت، تزریق کننده از این اینترفیس برای ارائه‌ی وابستگی‌ها به کلاینت درون متدها، استفاده می‌کند. 

هر کدام از روش‌های فوق مزایا و معایب خود را دارند، ولی در NET Core. بیشتر از «تزریق درون سازنده» استفاده می‌شود. در صورت لزوم می‌توانید از اینجا نمونه‌هایی از تزریق وابستگی را به هر کدام از سه روش بالا، مشاهده کنید.

Inversion of Control Container
در واژگان فنی مهندسی نرم افزار، Container (محفظه)  به جزیی از برنامه گفته می‌شود که می‌تواند اجزای دیگر برنامه را در بر بگیرد.  IoC Container ‌ها در واقع فریم ورک‌ها/چارچوب‌ها یا کتابخانه‌های برنامه نویسی هستند که ما در آنها می‌توانیم اشیاء مختلف را به سبک‌های خاصی تعریف و ثبت کنیم و در مواقع لزوم آنها را واکشی و به کلاس‌ها تزریق کنیم. معمولا  IoC Container‌ها لیستی از اشیاء هستند که در آن اینترفیس‌ها و پیاده سازی‌های مربوط به هر کدام از آن‌ها ثبت می‌شوند. درون IoC Container برای پیاده سازی اصل وارونگی وابستگی‌ها، معمولا از یکی از دو الگوی زیر استفاده می‌کنند (گاها هم دو الگو را با هم پیاده سازی می‌کنند) :
  •  Dependency Injection 
  • Service Locator

تمرکز اصلی ما در این نوشتار بر روی DI Container هاست. فرق Dependency Injection و Service Locator در این است که در DI، وابستگی‌ها توسط IoC Container از درون محفظه واکشی می‌شوند و به درون کد تزریق می‌شوند ولی در Service Locator در هر جایی از برنامه می‌توان با استفاده‌ی مستقیم از Container و با استفاده از متدهایی که به ما ارائه می‌دهد،  پرس و جو کرد (کوئری زد) و وابستگی مورد نظر را واکشی کرد.
در تزریق وابستگی، کلاس استفاده کننده از سرویس‌ها، درگیر نحوه‌ی واکشی و نمونه سازی از سرویس مورد نظر خود نمی‌شود و همه‌ی کار توسط DI Container انجام می‌گیرد. ولی در Service Locator باید سرویس مورد نظر، درون خود کلاس، مستقیما از Container دریافت و ساخته شود.
برای استفاده از Service Locator، تنها پیش نیاز، دسترسی به شیء Service Locator است.

به صورت کلی IoC Container ‌ها سه وظیفه‌ی اساسی را برعهده دارند:
  • ثبت سرویس درون خود
  •   ساخت نمونه‌های مورد نظر از سرویس‌ها و ارائه دادن آن‌ها به کلاس‌هایی که نیاز دارند.
  •  از بین بردن نمونه سرویس‌های ساخته شده (Dispose) کردن آن‌ها .

در ادامه با ساخت پروژه‌ای، اولین سرویس خودمان را درون Microsoft Dependency Injection Container یا به اختصار DI Container، ثبت کرده و آن را واکشی می‌کنیم.
نظرات مطالب
مفاهیم برنامه نویسی ـ مروری بر کلاس و شیء
تشکر از نظر شما. متوجه صحبت شما هستم ولی این توضیح برای دوستان دیگه می‌تونه مفید باشه.
پراید به عنوان رده ای از اتومبیل‌ها کلاس است. اما اگر به کسی یک اتومبیل پراید در حال عبور را نشان دهید که دارای پلاک و ... است، آن شیء ای است از کلاس پراید. اگر در یک پارکینگ تعدادی اتومبیل باشد و از کسی بخواهیم بر اساس نوع گروه بندی یا کلاس بندی کند، بعد از چند دقیقه خواهیم دید اتومبیل‌های هم نوع را جدا کرده. مثلا همه اتومبیل‌های از نوع پراید را کنار هم قرار داده. این همان مفهوم کلاس بندی است. برای تولید اتومبیلی از نوع پراید کارخانه یک نقشه یا طرحی ایجاد می‌کند که بسیاری مشخصات و چگونگی ساخت را در خود دارد. چگونگی انجام برخی کارکرد‌ها مانند حرکت را دارد. این طرح کلاس نامیده می‌شود. اما آیا می‌توان برای آن پلاک در نظر گرفت؟ خیر چون فقط نقشه ایجاد اتومبیل است (یا به عبارتی فقط مفهوم است). همچنین کلاس پراید احتمالاً از کلاس اتومبیل یا ماشین برخی خصوصیات و رفتار‌ها را با ارث برده است.

بیاد داشته باشیم هر فردی در هر سطحی از دانش هم مسلماً بسیاری چیزها را هنوز نمی‌داند و دانسته‌های ما در مقابل نادانسته‌ها قطره ای بیش نیست. تا جایی که می‌توان گفت همه ما از نظر نادانسته‌ها برابریم. تفاوت در دانسته هاست. کسانی که سال‌ها برنامه نویسی می‌کنند هم حتما دانسته هایی دارند که می‌توانند این کار را ادامه دهند. پس کافی است آنچه نمی‌دانند را سعی کنیم به اشتراک بگذاریم تا بدانند و از دانسته هایشان استفاده کنیم.
مطالب
طبقه بندی Bad Code Smell ها
نقل قول‌های زیادی، در مورد کیفیت کد وجود دارند. دستور العمل‌های فراوانی نیز در این راستا وجود دارند. یکی از ابزارهایی که برای نوشتن کدهایی با کیفیت مطلوب وجود دارد، مجموعه الگوهای بد کد نویسی است که به Code smell یا بوی بد کد مشهور هستند.  
بوی بد کد، نشانه‌هایی در کد هستند که حکایت از مشکلات عمیق‌تری دارند. بوی بد کد مساوی با باگ نیست. ولی خطر افزایش باگ‌ها و یا مشکلاتی را در آینده، به دنبال خواهند داشت. بوی بد کد معمولا حاصل رعایت نکردن یک سری اصول اولیه برنامه نویسی و یا طراحی شیء گرا هستند. 
برای بهبود کیفیت نرم افزار در دراز مدت نیاز است موارد بوی بد کد به دقت بررسی و رفع شوند. رفع شدن آنها ریسک انباشته شدن بوی بد کد را در پروژه کم خواهد کرد. یکی از فواید جلوگیری از انباشته شدن چنین الگوهای بدی در پروژه، بهبود فرآیند نگهداشت آن می‌باشد که موضوعی بسیار مهم برای چابکی یک تیم نرم افزاری است. 
هنگام مشاهده‌ی بوی بد، در بخشی از کدها، معمولا اولین اقدام، رفع آن است (Refactoring). در فرآیند رفع آن ممکن است الگوهای بد دیگری در کد یافت شوند که با آنها نیز به همین صورت برخورد خواهد شد. 
انوع بوهای بد کد به دسته‌های زیر طبقه بندی می‌شوند. 

کدهای متورم (Bloaters) 


این دسته در واقع تکه کدهایی (متد، کلاس و ...) هستند که به دلیل بزرگی بیش از اندازه عملا امکان کار با آن‌ها وجود ندارد. این بخش‌های بزرگ کد معمولا با توسعه تدریجی محصول ایجاد و روی هم انباشته می‌شوند. بوهای بد این دسته بندی به صورت زیر هستند:

1 - متدهای بلند (Long method): در این الگوی بد، متدها تعداد خطهای زیادی از کد را شامل می‌شوند. به طور معمول متدهایی با تعداد خطوط بیشتر از 10 خط، متدهای بلند محسوب می‌شوند. نکته قابل توجه این است که هیچ کس متدی را با تعداد خطوط زیاد طراحی نمی‌کند! معمولا به مرور زمان تعداد خط‌های یک متد افزایش می‌یابند. 
2 - کلاس‌های بزرگ (Large class): کلاسی که تعداد فیلدها، متدها و خطوط کد زیادی دارد. 
3 - وسواس استفاده از متغیرهای داده‌ای اولیه (Primitive obsession): این بوی بد معمولا به سه شکل بروز می‌کند. 
  • استفاده از متغیرهای اولیه بجای ساختارهای کوچک برای کارهای اولیه مانند Currency, DateTime, PhoneNumber 
  • استفاده از constant‌ها برای کد کردن اطلاعات مانند USER_ADMIN_ROLE = 1 
  • استفاده از constant‌های رشته‌ای به عنوان نام فیلدها در آرایه‌های داده 
4 - تعداد پارامترهای زیاد متد (Long parameter list): تعداد پارامترهای بیشتر از سه یا چهار عدد در یک متد. 
5 - توده داده (Data clumps): در بعضی موارد ممکن است از متغیرها به صورت دسته‌ای در مکان‌های مختلف کد استفاده شود. مانند استفاده از دسته‌ای از متغیرها برای نگه داشتن اطلاعات مربوط به اتصال پایگاه داده. این دسته‌ها باید به کلاس‌های حمل کننده داده خود تغییر کنند. 
 

بد استفاده کنندگان از شیء گرایی (Object orientation abusers)  


تکه کدهای این بخش در واقع بد استفاده کنندگاه یا ناقص استفاده کنندگان از اصول شیء گرایی هستند. در این دسته بندی موارد زیر وجود دارند:  

1 - گذاره‌های switch: وجود یک گذاره switch پیچیده یا دنباله‌ای از گذاره‌های if  
2 - درخواست رد شده (Refused request): در این حالت یک کلاس مجموعه محدودی از اعضای کلاس پدر خود را پیاده سازی می‌کند و باقی اعضای کلاس پدر یا بدون استفاده می‌مانند یا با استفاده از پرتاب کردن استثناء (Exception throwing) از کار انداخته می‌شوند. 
3 - فیلد موقتی (Temporary field): در این حالت متغیرها مقدار خود را در شرایط خاصی می‌گیرند و در بقیه شرایط خالی هستند. 
4 - کلاس هایی دقیقا مشابه در کارایی ولی متفاوت در مشخصات (Alternative Classes with Different Interfaces): دو کلاس دقیقا یک کار را انجام می‌دهند ولی نام اعضای آنها (متد و ...) متفاوت است. 

جلوگیری کنندگان از تغییر(Change preventers) 


این نشانه‌ها حاکی از این دارند زمانیکه تغییری در یک بخش کد نیاز باشد، در راستای آن حتما باید دیگر بخش‌های کد نیز به مقدار زیادی تغییر کنند. در این حالات اعمال تغییرات و نگهداری کد به شدت سخت خواهد شد. 
مواردی که در این دسته بندی قرار دارند به صورت زیر می‌باشند:  

1 - تغییر واگرا (Divergent change): این حالت زمانی اتفاق می‌افتد که برای اعمال یک تغییر به کلاس نیاز است متدهای زیادی را تغییر دهید. به طور مثال به ازای هر نوع محصولی که به محصولات شما اضافه می‌شود باید متدهای ذخیره، بازیابی، جستجو را تغییر دهید. 
2 - Shotgun Surgery: این حالت شباهت زیادی به تغییر واگرا دارد. تنها تفاوت آن این است که در این حالت شما به ازای هر تغییر نیاز است کلاس‌های زیادی را تغییر دهید. تغییر واگرا در بدنه یک کلاس اتفاق می‌افتد. 
3 - سلسله مراتب موازی ارث بری (Parallel inheritance hierarchy): این مورد یکی کمتر درک شده‌ترین موارد است. در این حالت زمانی که یک زیر کلاس برای یک کلاس ایجاد می‌کنید به ازای آن ناخودآگاه مجبور می‌شوید یک زیر کلاس برای کلاس دیگری ایجاد کنید. 

کدهای غیر ضروری (Dispensables) 


این دسته از کدها معمولا کدهایی هستند بی دلیل و بی استفاده. کدهایی که نبودنشان بهتر از بودنشان است! حذف کردن این کدها به خوانایی و قابلیت نگهداری کد خواهد افزود. بوهای بدی که در این دسته بندی قرار دارند به صورت زیر می‌باشند: 

1 - کامنت: یک متد، با مقادیر فراوانی از کامنت‌های توضیحی پر شده است. 
2 - کد تکراری: در این بوی بد، دو قطعه کد دقیقا مانند یکدیگر هستند. 
3 - کلاس داده (ِData class): کلاس‌هایی که تنها فیلدهای اطلاعاتی در آنها وجود دارند و متدهای خامی که جهت دریافت یا ذخیره اطلاعات در آنها استفاده می‌شوند. این کلاس‌های معمولا هیچ روال منطقی ای در خود ندارند. یکی از قدرت‌های شیء گرایی افزودن رفتار به کلاس‌ها در کنار اقلام اطلاعاتی موجود در آن است.  
4 - کلاس تنبل (Lazy class):  اگر کلاس کار چندانی که درخور نگهداری و توسعه باشد، انجام نمی‌دهد بهتر است از بین برود. 
5 - کد مرده (Dead code): متغیر، پارامتر، متد یا کلاسی که دیگر هیچ استفاده‌ای از آن متصور نیست و هیچ استفاده‌ای در حال حاضر از آن وجود ندارد. 
6 - کلی نگری بیش از اندازه (Speculative Generality): این الگو نیز کدهایی را شامل می‌شود که بلااستفاده هستند. ولی دلیل بلااستفاده بودن آن کلی نگری و دور اندیشی بدون دلیل است. معمولا کدهای تولیدی برای شرایط فعلی و پیش‌بینی آینده تولید می‌شوند. اگر این پیش‌بینی آینده به درستی و بر مبنای واقعیات انجام نشود، معمولا نتیجه کار، طراحی و پیاده سازی ای بی فایده و بلااستفاده خواهد بود. 

کدهایی بیش از اندازه وابسته به هم (Couplers) 


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

1- متد حسود (Feature envy): متدی که از اعضای یک شیء دیگر بیشتر از اعضای کلاس خود استفاده می‌کند! این اتفاق معمولا زمانی می‌افتد که فیلدهایی به یک "کلاس داده" منتقل می‌شوند. وقتی این اتفاق می‌افتد یکی از راه حل‌ها، انتقال روالهای استفاده کننده از فیلدها به "کلاس داده" مربوطه است.
2 - کلاس‌های بیش از اندازه صمیمی (Inappropriate Intimacy): کلاس‌ها از اعضای internal یکدیگر بیش از اندازه استفاده می‌کنند. کلاس‌های خوب کلاس‌هایی هستند که کمترین اطلاعی را از وضعیت داخلی یکدیگر دارند. 
3 - کتابخانه‌های ناقص (Incomplete Library Class): زمانیکه کتابخانه‌ای آماده می‌شود، بالاخره روزی می‌رسد که این کتابخانه نیازهای پروژه را رفع نمی‌کند و نیاز به توسعه خواهد داشت. ولی از آنجایی که کتابخانه‌ها به صورت فقط خواندنی در اختیار پروژه‌ها قرار می‌گیرند، در صورتیکه توسعه دهنده اصلی آن از توسعه کتابخانه سر باز بزند، مشکلاتی بوجود خواهد آمد. 
4 - زنجیره فراخوانی‌ها (Message chain): زمانیکه یک متد در بدنه خود پیامی به شیء دیگری می‌فرستد که آن شیء نیز به خودی خود پیامی به شیء دیگری می‌فرستد (و الی آخر) یک زنجیره فراخوانی بوجود آمده است. در این روش بیرونی‌ترین استفاده کننده از متد در واقع وابسته به یک زنجیره‌ای از فراخوانی‌ها است که تغییر در هر قدمی از آن باعث خرابی خواهد شد. 
5 - دلال (Middle man): اگر کلاسی تنها کاری که انجام می‌دهد انتقال فراخوانی به کلاس دیگری است، دیگر نیازی به این کلاس وجود نخواهد داشت.

اطلاع از الگوهای بد کد نویسی به همان اندازه اطلاع از الگوهای خوب کد نویسی در کیفیت محصول تولیدی اثر مثبت خواهند داشت. یادگیری طبقه بندی شده این الگوها کار را برای استفاده روزمره از آنها آسان‌تر خواهد کرد.
مطالب
مقدمه‌ای بر LINQ بخش اول
کلمه‌ی LINQ مخفف Language Integrated Query یا زبان پرس و جوی یکپارچه می‌باشد. LINQ برای اولین بار در ویژوال استودیوی 2008 و دات نت فریم ورک 3.5 برای پرکردن خلع بین دنیای اشیاء برنامه نویسی (Object Oriented World) و دنیای داده‌ها (Data World) ارائه شد.

چرا LINQ؟ 
در نگاهی کلی، مزایایی که از طریق LINQ حاصل می‌شوند عبارتند از:
• کاهش حجم کدنویسی 
• درک بهتر از عملکرد کد‌های نوشته شده
• پس از یادگیری اصول LINQ به راحتی می‌توانید از این اصول پرس و جو نویسی برای کار بر روی مجموعه داده‌های مختلف استفاده کنید
• کنترل صحت کدهای پرس و جو‌ها در زمان کامپایل ( Compile-Time Type Checking )

اجزای سازنده‌ی LINQ
دو جزء اصلی سازنده‌ی LINQ عبارت است از:
  • Elements عناصر
  • Sequences توالی‌ها
توالی‌ها می‌توانند لیستی از اطلاعات مختلف باشند. هر آیتم در لیست را عنصر می‌گوییم. توالی نمونه‌ای از یک کلاس است که اینترفیس <IEnumarable<T را پیاده سازی کرده باشد. این اینترفیس تضمین می‌کند که توالی قابلیت پیمایش عناصر را دارد.
به آرایه‌ی تعریف شده‌ی زیر دقت کنید:
int[] fibonacci = {0, 1, 1, 2, 3, 5};
متغیر fibonacci در اینجا نشان دهنده‌ی توالی و هر یک از اعداد آرایه، یک عنصر محسوب می‌شوند.
توالی می‌تواند درون حافظه‌ای باشد (In Memory Object) که به آن Local Sequence می‌گویند و یا می‌تواند یک بانک اطلاعاتی SQL Server باشد که به آن Remote Sequence می‌گویند.
در حالت Remote باید اینترفیس <IQuerable<T پیاده سازی شده باشد.
پرس و جو هایی را که بر روی توالی‌های محلی اجرا می‌شوند، اصطلاحا Local Query و یا LINQ-To-Object  نیز می‌نامند.
عملگرهای پرس و جوی  زیادی به شکل متد الحاقی در کلاس System.Linq.Enumerable طراحی شده‌اند. این مجموعه از عملگرهای پرس جو را اصطلاحا Standard Query Operator می‌گویند.
نکته‌ی مهم این است که عملگرهای پرس و جو تغییری را در توالی ورودی نمی‌دهند و نتیجه‌ی خروجی یک مجموعه جدید و یا یک مقدار عددی می‌باشد.

توالی خروجی و مقدار بازگشتی Scalar
در بخش قبل گفتیم که خروجی یک پرس و جو می‌تواند یک مجموعه و یا یک مقدار عددی باشد. در مثال زیر عملگر Count را بر روی مجموعه‌ی fibonacci  اعمال کردیم و عددی که نشان دهنده‌ی تعداد عناصر مجموعه است، بعنوان خروجی بازگردانده شده است.
int[] fibonacci = { 0, 1, 1, 2, 3, 5 };
int numberOfElements = fibonacci.Count();
Console.WriteLine($"{numberOfElements}");
IEnumerable<int> distinictNumbers = fibonacci.Distinct();
Console.WriteLine("Elements in output sequence:");
foreach (var number in distinictNumbers)
{
    Console.WriteLine(number);
}
در کد بالا توسط تابع Distinct، عناصر یکتا را از توالی ورودی استخراج کرده و بازگردانده‌ایم.
خروجی برنامه‌ی فوق به شکل زیر است :
6
Elements in output sequence:
0
1
2
3
5

مفهوم Deffer Execution  (
اجرای به تاخیر افتاده )
عمده‌ی عملگر‌های پرس و جو بلافاصله پس از ایجاد، اجرا نمی‌شوند. این عملگرها در طول اجرای برنامه اجرا خواهند شد (اجرای با تاخیر). به همین خاطر می‌توان بعد از ساخت پرس و جو  تغییرات دلخواهی را به توالی ورودی اعمال کرد.
در کد زیر  قبل از اجرای پرس و جو ، توالی ورودی ویرایش شده :
int[] fibonacci = { 0, 1, 1, 2, 3, 5 };
// ایجاد پرس و جو 
IEnumerable<int> numbersGreaterThanTwoQuery = fibonacci.Where(x => x > 2);
// در این مرحله پرس و جو ایجاد شده ولی هنوز اجرا نشده است
// تغییر عنصر اول توالی
fibonacci[0] = 99;
// حرکت بر روی عناصر توالی باعث اجرای پرس و جو می‌شود
foreach (var number in numbersGreaterThanTwoQuery)
{
   Console.WriteLine(number);
}
پرس و جو تا زمان اجرای حلقه‌ی Foreach اجرا نخواهد شد. خروجی مثال بالا به شکل زیر است :
99
3
5

به غیر از بعضی از عملگرها مثل Count,Min,Last سایر عملگر‌ها بصورت اجرای با تاخیر عمل می‌کنند. عملگری مثل Count باعث اجرای فوری پرس و جو می‌شود.
تعدادی عملگر تبدیل (Conversion Operator) هم وجود دارد که باعث می‌شوند پرس و جو بلافاصله اجرا شود :
• ToList
• ToArray
• ToLookup
• ToDictionary
عملگر‌های فوق پس از اجرا، خروجی را در یک ساختمان داده‌ی جدید باز می‌گردانند.
در کد زیر اصلاح توالی متغیر Fibonacci بعد از اجرای تابع ToArray صورت گرفته است.
int[] fibonacci = { 0, 1, 1, 2, 3, 5 };
// ساخت پرس و جو
IEnumerable<int> numbersGreaterThanTwoQuery = fibonacci.Where(x => x > 2) .ToArray();
// در این مرحله به خاطر عملگر استفاده شده پرس و جو اجرا می‌شود
// تغییر اولین عنصر توالی
fibonacci[0] = 99;
// حرکت بر روی نتیجه
foreach (var number in numbersGreaterThanTwoQuery)
{
   Console.WriteLine(number);
}
خروجی مثال بالا:
3
5
همانطور که می‌بینید عدد 99 در خروجی مشاهده نمی‌شود. علت فراخوانی عملگر ToArray است که بلافاصله باعث اجرای پرس و جو شده و خروجی را باز می‌گرداند . به همین خاطر تغییر عنصر اول توالی ورودی، تاثیری بر روی نتیجه‌ی خروجی ندارد. 
مطالب
اصول طراحی شیء گرا: OO Design Principles - قسمت سوم

اصل هفتم: Liskove Substitution Principle

"ارث بری باید به صورتی باشد که زیر نوع را بتوان بجای ابر نوع استفاده کرد"

این اصل می‌گوید اگر قرار است از ارث بری استفاده شود، نحوه‌ی استفاده باید بدین گونه باشد که اگر یک شیء از کلاس والد ( Base-Parent-Super type ) داشته باشیم، باید بتوان آن را با شیء کلاس فرزند ( Sub Type-Child ) بدون هیچ گونه تغییری در منطق کد استفاده کننده از شیء مورد نظر، تغییر داد. به زبان ساده باید بتوان شیء فرزند را جایگزین شیء والد کرد.

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

با رعایت این اصل، میتوانیم در مواقعی که شروط مرتبط با کلاس فرزند را نداریم و یک سری منطق و قیود کلی مرتبط با کلاس والد را داریم، از شیء کلاس والد استفاده نماییم و وظیفه  نمونه گیری (instantiation ) آن را به یک کلاس دیگر محول کنیم. به مثال زیر توجه کنید:

 public class Parent
    {
        public string Name { get; set; }
        public int X { get; set; }
        public int Y { get; set; }
        public Parent()
        {
            X = Y = 0;
        }
        public virtual void Move()
        {
            X += 5;
            Y += 5;
        }
        public void Shoot() { }
        public virtual void Pass() { } 
    }
    public class Child1 : Parent
    {
        public override void Move()
        {
            X += 10;
            Y += 10;
        }
    }
    public class Child2 : Parent
    {
        public override void Move()
        {
            X += 20;
            Y += 20;
        }
    }
    public enum State
    {
        Start,
        Move,
        Shoot,
        Pass
    }
    public class Creator
    {
        public static Parent GetInstance(bool? condition)
        {
            if (condition == null)
            {
                return new Parent();
            }
            if (condition == true)
            {
                return new Child1();
            }
            else
            {
                return new Child2();
            }
        }
    }
    public class Context
    {
        public void SetState(ref State s)
        {
            s = State.Move;
        }
        public void Main()
        {
            State state =State.Start;
            
            // در مورد نوع این شیء چیزی نمیدانیم و وابسته به شرایط نوع آن متغیر است
            // در حقیقت شیء کلاس فرزند را جای شیء کلاس والد  قرار می‌دهیم و نه بالعکس

            Parent obj = Creator.GetInstance(null);
            
            // منطق برنامه وضعیت را تغییر می‌دهد
            SetState(ref state);

            // قواعد کلی و عمومی که بدون در نظر گرفتن کلاس (نوع) شیء بر آن اعمال می‌شود
            switch (state)
            {
                case State.Move:
                    obj.Move();
                    break;
                case State.Shoot:
                    obj.Shoot();
                    break;
                case State.Pass:
                    obj.Pass();
                    break;
                default:
                    break;
            }           
        }
    }

همانطور که در کدها نیز توضیح داده‌ام، کلاس‌های فرزند را جایگزین کلاس والد کرده‌ایم. اگر می‌خواستیم عکس رابطه را (شیء والد را به شیء فرزند انتقال دهیم) اعمال کنیم باید تغییر زیر را ایجاد میکردیم که با خطا روبرو خواهد شد:

Child1 obj = Creator.GetInstance(null);



اصل هشتم: Interface segregation

"واسط‌های کوچک بهتر از واسط‌های حجیم است"

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

 public interface IHuman
    {
        void Move();
        void Eat();
        void LevelUp();
        void FireBullet();
    }
    public class Player : IHuman
    {
        public void Eat() { }
        public void FireBullet() { }
        public void LevelUp() { }
        public void Move() { }
    }
    public class Enemy : IHuman
    {
        public void Eat() { }
        public void FireBullet() { }
        public void LevelUp() { }
        public void Move() { }
    }
    public class Citizen : IHuman
    {
        public void Eat() { }
        public void FireBullet() { }
        public void LevelUp() { }
        public void Move() { }
    }

در این مثال که مربوط به مدل یک بازی با نقش‌های بازیکن، دشمن و شهروند (بی گناه!) است، طراحی به گونه‌ای است که دشمن و شهروند، توابعی را که نیاز ندارند، باید پیاده سازی کنند. در دشمن: Eat(), LevelUp() و در شهروند: Eat(), LevelUp(), FireBullet() . لذا واسط IHuman یک واسط کلی با وظیفه‌های متعدد است.

در مدل بهبود یافته که کلاس‌ها با پسوند Better بازنویسی شده‌اند داریم:

public interface IMovable { void Move(); }
    public interface IEatable { void Eat(); }
    public interface IPlayer { void LevelUp(); }
    public interface IShooter { void FireBullet(); }
    public class PlayerBetter : IPlayer, IMovable, IEatable, IShooter
    {
        public void Eat() { }
        public void FireBullet() { }
        public void LevelUp() { }
        public void Move() { }
    }
    public class EnemyBetter : IMovable, IShooter
    {
        public void FireBullet() { }
        public void Move() { }
    }
    public class CitizenBetter : IMovable
    {
        public void Move() { }
    }

در اینجا برای هر وظیفه یک واسط تعریف کرده ایم که باعث قوی شدن معنای هر واسط می‌شود.



اصل نهم: Dependency inversion

"وابستگی بین ماژول‌ها  را به وابستگی آن‌ها به انتزاع (واسط) تغییر بده"

این اصل که نمود آن را در الگو‌های طراحی dependency injection و factory میبینیم، میگوید که ماژول‌های بالادست (ماژول استفاده کننده ماژول پایین دست) به جای آنکه ارجاع مستقیمی را به ماژول‌های پایین دست داشته باشند، به انتزاعی (واسط) ارجاع بدهند که ماژول پایین دست آنرا پیاده سازی می‌کند یا به ارث میبرد. در واقع این اصل برای از بین بردن وابستگی قوی بین ماژول‌های بالا دست و پایین دست، به میدان آمده است. دو حکم اصلی از این اصل بر می‌آید:

الف – ماژول‌های بالا دست نباید وابسته به ماژول‌های پایین دست باشند. هر دو باید وابسته به انتزاع (واسط) باشند. وابستگی ماژول بالا دست از نوع ارجاع و وابستگی ماژول پایین دست از نوع ارث بری است.

ب – انتزاع نباید وابسته به جزییات باشد، بلکه جزییات باید وابسته به انتزاع باشد. یعنی در پیاده سازی منطق برنامه (که جزییات محسوب می‌شود) باید از واسط‌ها یا کلاس‌های انتزاعی استفاده کنیم و همچنین در نوشتن کلاس‌های انتزاعی نباید هیچ گونه ارجاعی را به کلاس‌های جزیی داشته باشیم.

در مثال زیر با نمونه‌ای از طراحی ناقض این اصل روبرو هستیم:

public class Controller
    {
        public Service Service { get; set; }
        public Controller()
        {
            // کنترلر باید نحوه نمونه گیری را بداند (ورودی‌های لازم) و این از وظایف آن خارج است
            Service = new Service(1);
        }
        public void DoWork()
        {
            Service.RunService();
        }
    }

    public class Service
    {
        public int State { get; set; }
        public Service(int s)
        {
            State = s;
        }
        public void RunService() { }
    }

در این مثال کلاس کنترلر، ماژول بالادست و کلاس سرویس، ماژول پایین دست محسوب میگردد. در ادامه طراحی مطلوب را نیز ارائه داده‌ام:

public class ControllerBetter
    {
        // ارجاع به واسط باعث انعطاف و کاهش وابستگی شده است
        public IService Service { get; set; }
        public ControllerBetter(IService service)
        {
            // یک کلاس دیگر وظیفه ارسال سرویس به سازنده کلاس کنترلر را دارد 
            // و مسئولیت نمونه گیری را از دوش کنترلر برداشته است
            Service = service;
        }
        public void DoWork()
        {
            Service.RunService();
        }
    }
    // کاهش وابستگی با تعریف واسط و تغییر وابستگی مستقیم بین کنترلر و سرویس
    public interface IService
    {
        void RunService();
    }
    // وابستگی جزییات به انتزاع
    public class ServiceBetter : IService
    {
        public int State { get; set; }
        public ServiceBetter(int s)
        {
            State = s;
        }
        public void RunService() { }
    }

نحوه بهبود طراحی را در توضیحات داخل کد مشاهده میکنید. در مقاله بعدی به اصول GRASP خواهم پرداخت.