روی خط زندگی

گشت و گذار من در هنر نرم افزار

روی خط زندگی

گشت و گذار من در هنر نرم افزار

۸ مطلب با کلمه‌ی کلیدی «OOP» ثبت شده است

در بسیاری از اوقات، برنامه نویسان و توسعه دهندگان، تنها تفاوت بین Interface و abstract class را در این می دانند که یک Interface پیاده سازی ندارد ولی abstract class می تواند رفتار ها یا در واقع متد ها را پیاده سازی کند. اما این تنها تفاوت این دو نیست و به واقع این دو از نقطه نظر ماهیت با هم تفاوت دارند و نه پیاده سازی.


وقتی یک کلاس از یک Interface ارث بری می کند، در واقع به مانند آن است که یک قرارداد را امضا می کند. کلاس با ارث بری از این Interface به ما تضمین می دهد که متد های آن را من پیاده سازی خواهم کرد و این یعنی من این رفتار ها را دارم. بدین جهت است که اگر به اسم Interface ها دقت کرده باشید، عموما به able، Provider و مانند آن ختم می شوند، مانند ICloneable . حتی آن کاراکتر «I» که در ابتدای این نام می آید اذعان می کند که من می توانم! یا من فراهم می کنم!

در مستندات راهنمای مایکروسافت، برای نام گذاری Interface ها توصیه شده که از نام ها، اصطلاحات یا صفت هایی استفاده شود که بیان کننده رفتاری باشد که آن Interface به کلاس ها القا می کند.


و اما abstract class.

وقتی به یک مفهوم از دیدگاه abstraction نگاه می کنیم، نتیجه آن پیاده سازی آن مفهوم در قالب یک abstract class است. در واقع یک abstract class مفهوم مجازی یک واقعیت است. مفهومی که ممکن است در دنیای واقعی وجود مستقیم نداشته باشد ولی کلاس هایی که از آن به ارث می روند در دنیای واقعی وجود دارند. مثلا وقتی می گوییم «نمایش دهنده» یک مفهوم مبهم به ذهن متبادر می شود و نه یک تصویر مشخص. ولی در دنیای واقعی اشیایی هستند که ماهیت این مفهوم را در خود دارند، مانند تلویزیون یا مانیتور کامپیوتر. برای همین به کلاس هایی که از یک abstract class ارث بری می کنند می گوییم: concrete class یعنی کلاس های واقعی! 


در واقع اگر بخواهیم در روابط ارث بری از دیدگاه کلاس پدر به مساله نگاه کنیم، به لحاظ ماهیتی، یک interface یک مجموعه رفتار مرتبط را به اشتراک می گذارد مانند IPrintable ولی یک abstract class یک مفهوم مجازی را به ما عرضه می کند مانند Person.

و اگر بخواهیم از دیدگاه فرزندان به مساله نگاه کنیم، کلاس هایی که از یک Interface ارث بری می کنند، در واقع می گویند ما می توانیم فلان کار را انجام دهیم یا ما این رفتار را با خود به همراه داریم و کلاس هایی که از یک abstract class ارث بری می کنند به ما می گویند ما پیاده سازی واقعی یک مفهوم مجازی هستیم.

۴ نظر موافقین ۰ مخالفین ۰ ۰۵ خرداد ۹۴ ، ۲۰:۵۳
حسین گویا

وقتی می گوییم Object Oriented Analysis and Design منظور چیست و چه تفاوتی با Object Oriented Programming یا همان OOP دارد؟


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

Analysis (تحلیل): فهمیدن آن که مشکل چیست و چه راه حلی برای حل آن وجود دارد.

Design (طراحی): طراحی کردن مساله و برنامه ریزی برای حل آن.

Programming (برنامه نویسی): ساخت و پیاده سازی راه حل.

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


برای پیاده سازی یک پروژه، ابتدا باید بدانیم که کلاس های اصلی ما که همان دامین مساله را تشکیل می دهند، کدامند؟ برای پاسخ به این سوال از فرآیند تحلیل و طراحی شی گرا استفاده می کنیم. نتیجه نهایی این فرآیند آن است که کلاس های اصلی ما کدامند و چه کاری انجام می دهند. برای نایل شدن به این هدف باید پنج گام را طی نماییم:

گام اول) شناسایی و جمع آوری نیازمندی ها

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


گام دوم) شرح و بسط مساله

یعنی سیستمی که می خواهیم بنویسیم چه کاری را می خواهد انجام دهد و چه امکاناتی دارد. برای انجام این مهم تکنیک ها و مستندات مختلفی وجود دارد. از قبیل Use Case و User Story


گام سوم) مشخص کردن اشیا وEntity  های اصلی 

این ها همان بازیگران اصلی سیستم هستند که با مشخص شدن آنها، گام اول برای طراحی کلاس ها را برداشته ایم. برای طی این مرحله از همان مستنداتی که در گام دوم ایجاد شده اند استفاده می کنیم (مانند Use Case و User Story) تا مفاهیم و اجزای اصلی سیستم که دامین پروژه را تشکیل می دهند، را بیابیم.


گام چهارم) طراحی ارتباطات و تعاملات این اجزا با یکدیگر

حال که در گام سوم اجزا را شناختیم، باید ارتباطات و تعاملات این اجزا را با هم و با جهان بیرون از سیستم، بیابیم. برای این کار می توانیم از نمودار های تعاملی UML مانند Swim lane و Sequence Diagram استفاده کنیم. این نمودار ها به ما کمک می کنند که وظایف و مسئولیت های اجزا و Object های مختلف سیستم را بهتر بشناسیم.


گام پنجم) طراحی کلاس دیاگرام (Class Diagram)

در این گام به نتیجه دلخواه که همانا طراحی نمودار کلاس ها می باشد رسیده ایم. این طراحی هم شامل کلاس ها و رفتار هایشان است و هم شامل اصول و قواعد طراحی شی گرا مانند Inheritance، Abstraction .


توجه داشته باشید که این پنج گام را تنها یکبار طی نخواهیم کرد و در هر گذار (Iteration یا Sprint) از عمر یک پروژه ممکن است دوباره این گام ها یا چند عدد از آنها را مجددا طی کنیم تا شناخت بهتری نسبت به سیستم بدست آوریم.


۰ نظر موافقین ۰ مخالفین ۰ ۲۷ فروردين ۹۴ ، ۲۲:۰۷
حسین گویا

چند ریختی در سطح کلان یعنی ما رفتار مناسب را در هنگام استفاده از یک شی داشته باشیم. یک مثال خوب، عملگر جمع (+) است. مثال های زیر را در نظر بگیرید:

  1. 2+3
  2. ‘a’+’b’
  3. “Hello” + “world”

در مثال اول عملگر جمع در حال جمع دو عدد است، در مثال دوم همین عملگر در حال جمع دو کاراکتر و در مثال سوم در حال جمع دو رشته است. این معنای همان رفتار مناسب در زمان استفاده است. ما در زمان استفاده تغییری در عملگر ایجاد نمی کنیم ولی عملگر جمع رفتار مناسب را از خودش نمایش می دهد.

دو شکل از چند ریختی یا همان Polymorphism وجود دارد:

شکل اول) Static Polymorphism

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

Add (int a, int b)

Add  (string a, string b)


شکل دوم) Dynamic Polymorphism

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


۱ نظر موافقین ۰ مخالفین ۰ ۱۰ فروردين ۹۴ ، ۱۹:۴۵
حسین گویا

شاید اصلی ترین ایده در مفاهیم شی گرایی وراثت است. با وراثت می توان یک کلاس را به چندین کلاس توسعه داد بدون آنکه کلاس اصلی ما دستخوش تغییر شود. این یکی از بهترین اشکال برای Code Reuse می باشد. وقتی یک کلاس از کلاس دیگری به ارث می رود، بدون آنکه نیاز به کد نویسی باشد، کلیه رفتار ها و ویژگی های آن کلاس را به ارث می برد (البته با توجه به دسترسی های تعریف شده برای رفتار ها، تنها رفتار هایی با سطح دسترسی public و protected به ارث می روند).


در شکل زیر کلاس های Student و Employee از کلاس Person ارث بری کرده اند. به کلاس Person می گوییم Superclass و به کلاس های Student و Employee می گوییم Subclass.


یکی از مهترین فواید استفاده از وراثت آن است که با اعمال تغییرات در کلاس پدر، تمامی کلاس های فرزند که از کلاس پدر به ارث رفته اند، بدون هیچ کاری تغییرات را دریافت می کنند.

لازم به ذکر است که در برخی از زبان ها مانند ++C امکان ارث بری از چندین کلاس وجود دارد، در واقع یک کلاس فرزند می تواند چندین پدر داشته باشد!، ولی در زبان هایی مانند #C و Java هر کلاس تنها از یک کلاس می تواند ارث بری داشته باشد و امکان وراثت چندگانه وجود ندارد. لازم به ذکر است که وراثت چندگانه علی رقم آزادی عملی که به توسعه گر می دهد، مشکلاتی را ایجاد می کند (از جمله افزایش سطح پیچیدگی یا Complexity ) که بحث آن در این مقال نمی گنجد.

۰ نظر موافقین ۰ مخالفین ۰ ۲۱ اسفند ۹۳ ، ۲۰:۴۰
حسین گویا

یک مانیتور را تجسم کنید. برای روشن کردن این مانیتور چه می کنید؟ آیا کاری با خازن ها و برد الکترونیکی آن دارید؟ مسلما نه! تنها کاری که باید انجام دهید فشردن یک دکمه است و مانیتور به تبع آن روشن می شود!

کپسوله سازی، همان طور که از نامش پیداست به معنای دسته بندی و جمع کردن یک مجموعه از ویژگی ها و رفتار ها در قالب یک مجموعه واحد می باشد. اما این تمام ماجرا نیست. جمع کردن این موارد در کنار هم به تنهایی مفهوم کپسوله سازی را نمی دهد. نکته ای که این مفهوم را تکمیل می کند، نحوه دسترسی و دستیابی به اعضای داخلی این مجموعه است. یک شی یا یک کلاس به عنوان یک کپسول، سعی می کند تمام ارتباطات با اعضای داخلی خود را محدود کند و در واقع جهان بیرون از مجموعه از ارتباطات و اعضای داخل این مجموعه خبر نداشته باشند (به این مهم Information Hiding می گویند). یک کپسول تنها چیزهایی را به دنیایی بیرون ارائه می دهد که واقعا نیاز باشد. توجه داشته باشید در دنیای برنامه نویسی منظور از جهان بیرون، لزوما استفاده کنندگان از نرم افزار نمی باشد. مثلا وقتی یک کامپوننت (Component) را کپسوله می کنیم، استفاده کنندگان از این کامپوننت سایر کامپوننت ها و کلاس های دیگر می باشند و نه کاربر نهایی.


در یک کلاس، پیاده سازی سطوح دسترسی به اجزای کلاس را توسط Access Modifier ها نظیر public، private، protected اعمال می کنیم. برای انجام یک عمل مانند خاموش روشن کردن مانیتور، تنها به یک متد که کل این فرآیند را مدیریت می کند اجازه می دهیم که از بیرون قابل دسترسی باشد و سایر جزییات را از دید بیرون مخفی می کنیم. این عمل مصداق کامل ایجاد یک جعبه سیاه (Black Box) است. اگر تکنولوژی روشن و خاموش کردن مانیتور عوض شود ( به طور مثال تغییر تکنولوژی از پلاسما به LCD و بعد به LED) دکمه خاموش و روشن کردن مانیتور همچنان همان کار قبلی را می کند، بدون آنکه ما دغدغه ای از بابت این تغییر داشته باشیم.


شاید برای شما هم این سوال پیش بیاید که چرا باید کد هایی را که خودم نوشته ام از دید خودم یا دوستان همکارم پنهان کنم. توجه داشته باشید که مخفی کردن اجزای داخلی یک کلاس از جهان بیرونی، به منظور حفظ امنیت صورت نمی گیرد، بلکه دغدغه اصلی اینجا کاهش وابستگی (Dependency) اجزا یا کامپوننت های مختلف یک سیستم از یکدیگر است. 


۱ نظر موافقین ۰ مخالفین ۰ ۱۴ اسفند ۹۳ ، ۱۹:۰۱
حسین گویا

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

در دنیای مجازی ما، Object ها نمونه هایی از یک Class هستند. در واقع کلاس ها دیدگاه های انتزاعی به یک شی می باشند. توجه داشته باشید که در مورد مفهوم انتزاع یا Abstraction صحبت می کنیم و نه در مورد abstract class (در واقع abstract class ها خود دیدگاهی انتزاعی از یک class می باشد که در ادامه راجعه به آن هم صحبت می کنیم)

مثلا کلاسی که برای مدیریت یک حساب بانکی است حاوی اطلاعاتی به مانند زیر است:


کلاس حساب بانکی (مثلا بانک ملت) :

  • صفت ها (attributes)
    • شماره حساب
    • نام صاحب حساب
    • موجودی
    • نوع حساب (جام، کوتاه مدت، طلایی)
  • رفتار ها (behaviors)
    • افزایش موجودی
    • کاهش موجودی
    • افتتاح حساب
    • بستن حساب

کلاس حساب بانکی، یک مفهوم انتزاعی از حساب بانکی افراد (مثلا حساب بانکی موسسه خیریه کهریزک) می باشد. در واقع اینجا مشخص نیست که شماره حساب چند است و صاحب حساب کیست. این یعنی ما یک کلاس برای حساب بانکی احمد و یک کلاس برای حساب بانکی محمود ایجاد نمی کنیم، بلکه یک کلاس برای حساب بانکی ایجاد می کنیم.

وقتی از منظر انتزاعی یا Abstraction به اشیا و مسائل نگاه می کنیم به این معنی است که:
  • به چیز های ضروری و اساسی دقت می کنیم.
  • از جزییات و موارد بی ربط به اساس مساله پرهیز می کنیم.
در بسیاری از مصاحبه های استخدامی که تاکنون با برنامه نویسان مختلف انجام داده ام وقتی می پرسم: abstraction چیست؟ می گویند: «کلاسی است که هیچ پیاده سازی ندارد.». این در حالی است که تاکید می کنم که اولا ما در حوزه مفاهیم صحبت می کنیم و نه در حوزه پیاده سازی، دوما abstraction مفهومی است که abstract class از آن نشئت می گیرد و این دو را با هم اشتباه نگیرید. برای روشن تر شدن کمی به abstract class بپردازیم تا تفاوت آن با مفهوم abstraction مشخص شود.

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

با توجه به این مطلب، پر واضح است که abstraction مفهومی است که abstract class بواسطه آن ایجاد می شود و نباید این دو را معادل هم گرفت.
۹ نظر موافقین ۱ مخالفین ۰ ۰۹ بهمن ۹۳ ، ۲۱:۰۰
حسین گویا

مفاهیم برنامه نویسی شی گرا یا (Object Oriented Programming (OOP بر محور چهار ایده اصلی بنا شده است. این چهار ایده و مفهوم زیربنایی عبارتند از:

  • انتزاع یا تجرید (Abstraction)
  • چند ریختی (Polymorphism)
  • وراثت (Inheritance)
  • کپسوله سازی (Encapsulation)

هر کدام از این چهار ایده به تنهایی مجالی می طلبد تا به کفایت بررسی گردد. لذا در مطالب بعدی به بررسی هر کدام به صورت جداگانه خواهم پرداخت.


۰ نظر موافقین ۱ مخالفین ۰ ۰۲ بهمن ۹۳ ، ۲۰:۲۱
حسین گویا

Code Smell ها علائم سطحی ای هستند که حاکی از وجود مشکلات عمیق در سیستم می باشند. (مارتین فولر)

این تعریف جناب فولر، بیان عمیق و درستی از مفهوم Code Smell ها ارائه می دهد. وقتی در یک سیستم نشانه هایی از Code Smell ها مشاهده می کنیم می توانیم حدس بزنیم که این سیستم احتمالا در جایی به مشکل خواهد خورد و یا توسعه و پشتیبانی آن دچار اشکال خواهد بود. Code Smell ها نشانه هایی از زلزله ای قریب الوقوع در سیستم می باشند. به برخی از مهمترین Code Smell ها به اختصار اشاره می کنیم:


  • Duplicated code: کد های تکراری در برنامه که ناقض اصل DRY هستند.
  • Long Method: وجود متد های طولانی در سیستم علاوه بر نقض اصول OOP بخصوص اصل تک مسئولیتی (Single Responsibility) ، پیگیری خطاها را مشکل خواهد کرد.
  • Large Class یا God Object: اصل تک مسئولیتی را نقض می کنند و به مانند متد های طولانی رهگیری خطاها را دشوار می سازند. همچنین عموما این کلاس ها سبب ایجاد درهم تنیدگی (Coupling) در سیستم می شوند.

  • Feature envy: استفاده بیش از حد یک کلاس از کلاس دیگر. این مورد شاید توضیح بیشتری لازم داشته باشد. فرض کنید یک کلاس برای مدیریت نمرات دانش آموزان داریم. حال می خواهیم معدل دانش آموزان را حساب کنیم. برای این کار می توانیم تک تک نمرات دانش آموز را از کلاس بگیریم و معدل وی را در خارج از کلاس دانش آموز محاسبه کنیم، اما این یک مشکل بزرگ ایجاد می کند. ما (به عنوان یک کلاس دیگر) بیش از حد به کلاس دانش آموز وابسته شده ایم و با هر تغییری در لیست نمرات کد های کلاس ما نیز دچار مشکل می شود. بهتر آن است که این وظیفه را به خود کلاس دانش آموز بدهیم و در واقع کلاس دانش آموز یک تابع ارائه نماید که معدل دانش آموزان را حاسبه کرده و نتیجه را در اختیار ما قرار می دهد. به بیان دیگر، یک کلاس باید در ارائه خصوصیات به دنیای بیرون خساست به خرج دهد و فقط چیز هایی را به بیرون ارائه دهد که لازم است.

۰ نظر موافقین ۱ مخالفین ۰ ۱۸ دی ۹۳ ، ۲۲:۲۹
حسین گویا