برنامه نویسی شئ گرا

برنامه نویسی شی گرا: یک فاجعه چند هزار میلیارد دلاری – قسمت آخر

این مقاله ترجمه ایی از مقاله “Object-Oriented Programming — The Trillion Dollar Disaster” از Medium است.

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

اینطور نیست. مردم شروع به تسلیم شدن زیر بار انتزاعات و گراف های پیچیده اشیا تغییر پذیر کرده اند. زمان گرانبها و قدرت مغز به جای اینکه صرف حل کردن مساعل واقعی شود , صرف ”انتزاعات – abstractions” و “الگو های طراحی – design patterns” میشود.

بسیاری از مردم از جمله مهندسان نرم افزار انتقاداتی به برنامه نویسی شی گرا دارند. به جهنم, حتی مخترع OOP هم یکی از منتقدان شناخته شده OOP مدرن است.

هدف نهایی هر توسعه دهنده نرم افزار باید نوشتن کد قابل اعتماد باشد. هیچ چیز دیگری اهمیت ندارد اگر کد حاوی باگ و غیر قابل اعتماد باشد. و بهترین راه برای نوشتن کد قابل اعتماد چیست؟ سادگی. سادگی متضاد پیچیدگی است. بنابراین در درجه اول مسعولیت ما به عنوان یک توسعه دهنده نرم افزار باید کاستن پیچیدگی باشد.

پیچیدگی کد

با زبان های برنامه نویسی تاثیر گرفته از OOP , نرم افزار های کامپیوتری طولانی تر, ناخواناتر, غیر تشریحی تر و سخت تر برای نگهداری و تغییر میشوند — Richard Mansfield

مهمترین جنبه توسعه نرم افزار, پایین نگه داشتن پیچیدگی کد است. هیچ کدام از قابلیت های فانتزی مهم نیست اگر کد غیرقابل نگهداری نباشد. حتی پاس کردن ۱۰۰٪ تست ها هم ارزشی ندارد اگر کد پیچیده و غیر قابل نگهداری باشد.

چه چیزی کد را پیچیده میکند؟ موارد زیادی را باید در نظر گرفت اما به نظر من عوامل اصلی این ها هستند:اشتراک وضعیت تغییرپذیر, انزوا نادرست و نسبت سیگنال به نویز کم (با کمی جست و جو در مورد اینها میتوانید اطلاعات بدست اورید که موضوع بحث ما نیست). همه اینها در OOP رواج دارد.

مشکلات وضعیت

وضعیت(یا حالت یا state) چیست؟ به بیان ساده, وضعیت هرگونه اطلاعات موقت ذخیره شده در حافظه است. مثل متغیر ها یا فیلد/properties در OOP. برنامه نویسی مطلق (از جمله OOP), محاسبات را از طریق وضعیت برنامه و تغییرات وضعیت توضیح میدهد. برنامه نویسی اعلانی(functional) به جای ان نتایج مورد نظر را توصیف میکند و تغییرات وضعیت را صراحتا مشخص نمیکند.

وضعیت تغییر پذیر –عمل دستکاری ذهن

من فکر میکنم برنامه های شی گرا با ساختن نمودار های بزرگ اشیا تغییر پذیر برای کاهش پیچیدگی تقلا میکنند. میدانید, سعی در درک و یادآوری در ذهن خود دارید که هنگام فراخوانی یک متد چه اتفاقی رخ میدهد و عوارض جانبی ان چیست. –Rich Hickey خالق زبان Clojure

وضعیت به خودی خود کاملا بی ضرر است. با این حال وضعیت تغییر پذیر یک مشکل بزرگ است. مخصوصا اگر انتشار یافته باشد. وضعیت تغییر پذیر دقیقا چیست؟ هر وضعیتی که قابلیت تغییر داشته باشد. مثل متغیر ها و فیلد ها در OOP.

یک مثال واقعی, خواهش میکنم!

شما یک کاغذ سفید دارید, یادداشتی روی ان مینویسید و در نهایت همان تکه کاغذ اما با وضعیت (یادداشت) متفاوت را دارید. شما به طور موثری وضعیت کاغذ را تغییر داده اید. این امر در دنیای واقعی کاملاً خوب است زیرا احتمالاً هیچ کس دیگری به آن کاغذ اهمیت نمی دهد. مگر اینکه این تکه کاغذ نقاشی اصلی مونا لیزا باشد.

محدودیت های ذهن انسان

 

وضعیت تغییر پذیر چنین مشکل بزرگی است؟ مغز انسان قدرتمندترین ماشین در جهان شناخته شده است.با این حال ، مغز ما در کار با وضعیت واقعاً بد است زیرا ما فقط می توانیم حدود 5 مورد را در یک زمان در حافظه خود نگه داریم. بسیار راحت تر است که اگر در مورد کاری که یک قطعه کد انجام میدهد فکرکنید , نه اینکه چه متغیر هایی را در کد تغییر میدهد.

برنامه نویسی به وضعیت تغییر پذیر یک عمل دستکاری ذهن است.

متاسفانه این دستکاری ذهنی وضعیت تغییر پذیر در همه جای OOP وجود دارد. تنها دلیل برای وجود متد هایی برای یک شی ,تغییر دادن همان شی است.

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

یک مثال واقعی , خواهش میکنم!

بیایید برای چند ثانیه فراموش کنیم بزرگ شده ایم, و تلاش کنیم یک کامیون لگو با حال درست کنیم.

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

بله ، در نهایت آن کامیون را مونتاژ خواهید کرد ، اما چقدر طول خواهد کشید؟

ارتباط این با برنامه نویسی چیست؟

در برنامه نویسی functional معمولا وضعیت ایزوله است. همیشه میدانید یک وضعیت از کجا می آید. وضعیت هرگز بین تابع های شما پراکنده نمیشود. در OOP هر شی وضعیت خود را دارد, هنگام ساخت برنامه , باید وضعیت همه اشیا ای که با انها کار میکنید را بدانید.

برای اسانتر کردن زندگی بهتر است کد ,معاملات خیلی کمی با وضعیت داشته باشد. بگذارید قسمتهای اصلی برنامه شما خالص باشد.

وضعیت منتشر شده اشکارا

وضعیت قابل تغییر در دنیای واقعی تقریباً هرگز مشکلی به وجود نمی اورد ، زیرا همه چیز خصوصی نگه داشته می شود و هرگز مشترک نیست.این یعنی “انزوا مناسب” . نقاشی را تصور کنید که در حال کشیدن نقاشی بعدی مونا لیزا است. او به تنهایی روی نقاشی کار می کند ، به پایان میرساند و سپس شاهکار خود را به میلیون ها نفر میفروشد.

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

انتشار وضعیت تغییر ناپذیر در جهان واقعی مفهومی ندارد. این دقیقاً اتفاقی است که در برنامه های OOP رخ می دهد – وضعیت آشکارا بین اشیاء مختلف به اشتراک گذاشته می شود ، و آنها به هر شکلی که مناسب می بینند ، آن را ارتقا و تغییر می دهند. این به نوبه خود ، به دلیل رشد کد استدلال در مورد برنامه را سخت تر و سخت تر می کند.

مساعل مربوط به همزمانی

با وجود انتشار وضعیت بی قاعده در OOP , موازی کردن ان کد غیر ممکن میشود. مکانیسم های پیچیده ای برای رفع این مشکل اختراع شده است. Thread locking, mutex و مکانیسم های دیگری اختراع شده است. البته ، چنین رویکردهای پیچیده اشکالات خاص خود را دارد – deadlock ها ، عدم سازگاری ، دیباگ کردن کد های چند نخی بسیار سخت و زمانگیر است. من حتی در مورد افزایش پیچیدگی ناشی از استفاده از مکانیزم های همزمانی صحبت نمی کنم.

همه وضعیت ها شر نیستند

همه وضعیت ها شر هستند؟ نه وضعیت الن کی احتمالا بد نیست. تغییر وضعیت ها اگر به طور صحیح ایزوله شده باشد احتمالا خوب است.

داشتن اشیاء تغییر ناپذیر برای انتقال داده خیلی هم خوب است. نکته اینجا”تغییر ناپذیر” است. از چنین اشیا ای برای انتقال داده ها بین توابع استفاده می شود.

با این حال ,‌ چنین اشیایی ممکن است متد ها و ویژگی ها (properties) در OOP را کاملا زائد کند. وقتی متد ها و ویژگی های یک شی نمیتواند تغییر کند پس کاربرد انها چیست؟

تغییر پذیری در OOP طبیعی(ذاتی) است

بعضی ها ممکن است استدلال کنند که تغییر وضعیت در OOP یک انتخاب است نه یک اجبار. این استدلال مشکلاتی دارد. این یک انتخاب در طراحی نیست, تقریبا تنها گزینه است. بله میتوان در جاوا و C#اشیا تغییر ناپذیر را به متد ها منتقل کرد.اما این به ندرت انجام می شود زیرا بسیاری از برنامه نویسان به صورت پیش فرض در حال تغییر داده ها هستند. حتی اگر توسعه دهندگان بخواهند از تغییر ناپذیری در برنامه شی گرا شان درست استفاده کنند, زبان ها مکانیسم داخلی ای برای تغییر ناپذیری و استفاده موثر از تغییر ناپذیری فراهم نکرده اند.

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

این غیر ممکن است که از OOP استفاده کنیم و وضعیت تغییر نکند.

مشکل وضعیت عمومی(جهانی) – The global state problem

به ما گفته شده است که وضعیت عمومی ریشه همه شرها است. باید به هر قیمتی از ان جلوگیری کرد. آنچه که هرگز به ما گفته نشده این است که در حقیقت, encapsulation خودش یک وضعیت عمومی است.

برای کارآمدتر کردن کد ، اشیاء نه با مقدار خود بلکه با ارجاع-reference به آنها منتقل می شوند. اینجاست که “تزریق وابستگی-dependency injection” ضایع میشود.

بگذارید توضیح بدهم. هر وقت یک شیء را در OOP ایجاد می کنیم ، به وابستگی آن به سازنده-constructor اشاره می کنیم. سازنده ها هم وضعیت درونی خود را دارند. این شیء تازه ایجاد شده با خوشحالی منابع خود را به ان وابستگی(constructor), وابسته میکند و خوشحال است که به هر شکلی که بخواهد میتواند وضعیت داخلی سازنده را اصلاح کند. و همچنین این ارجاع را به موارد دیگری که ممکن است از ان استفاده کند منتقل میکند.

این یک نمودار پیچیده از اشیاء مشترک ایجاد می کند که در آخر وضعیت یکدیگر را تغییر میدهند. این به نوبه خود ، باعث ایجاد مشکلات عظیمی می شود زیرا نمی توان دید که چه عواملی باعث تغییر وضعیت برنامه شده است. ممکن است روزها در تلاش برای دیباگ کردن چنین تغییر وضعیت هایی تلف شود. اگر خوش شانس باشید و با همروندی-concurrency سر و کار نداشته باشید(بعدا در این مورد بیشتر حرف میزنم).

متد ها و ویژگی ها-Properties

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


مشکل مدل سازی دنیای واقعی

برخی افراد می گویند OOP سعی می کند دنیای واقعی را شبیه سازی کند. این درست نیست – OOP هیچ ارتباطی با دنیای واقعی ندارد. تلاش برای مدل سازی برنامه ها با استفاده از اشیاء ، یکی از بزرگترین اشتباهات OOP است.

دنیای واقعی سلسله مراتبی نیست

برنامه نویسی شی گرا تلاش می کند تا همه چیز را به عنوان سلسله مراتب اشیاء مدل سازی کند. متأسفانه ، در دنیای واقعی اشیا اینطور کار نمیکنند. اشیاء در دنیای واقعی با استفاده از پیام ها با یکدیگر در تعامل هستند اما اکثراً مستقل از یکدیگر هستند.

وراثت در دنیای واقعی

وراثت در OOP از دنیای واقعی الگوبرداری نمیکند. شیء پدر در دنیای واقعی قادر به تغییر رفتارشی فرزند در زمان اجرا نیست. حتی اگر شما DNA خود را از والدین خود به ارث ببرید ، آنها قادر به تغییر در DNA شما مطابق میل خود نیستند. شما “رفتارها” را از والدین به ارث نمی برید ، شما رفتارهای خودتان را توسعه می دهید. و شما قادر به “غلبه-override” بر رفتارهای والدین خود نیستید.

دنیای واقعی متد ندارد

آیا تکه کاغذی که روی ان می نویسید متد “نوشتن” دارد؟ نه! شما یک کاغذ خالی دارید، یک قلم بر میدارید و متنی را می نویسید. شما به عنوان یک فرد متد “نوشتن” ندارید — شما تصمیم می گیرید که متن را بر اساس رویدادهای خارجی یا افکار داخلی خود بنویسید.

قلمرو اشیا

اشیاء ,توابع و ساختار داده ها را در واحدهای غیر قابل تفکیک در کنار هم جمع میکنند. من فکر می کنم این یک خطای اساسی است زیرا توابع و ساختار داده ها کاملا به دنیا های متفاوتی تعلق دارند. —Joe Armstrong خالق زبان ارلانگ

اشیاء هسته اصلی OOP هستند. یک محدودیت اساسی OOP این است که همه چیز را به اشیا سوق می دهد. و همه چیز را نباید بر پایه اشیا مدل سازی کرد. توابع نباید بر اساس اشیاء مدل شوند. چرا ما باید کلاسMultiplier (ضرب) را ایجاد کنیم وقتی همه آنچه که نیاز داریم یک تابع است که دو عدد را ضرب کند؟ به سادگی یک تابع Multiply داشته باشید ، بگذارید داده ها(data) داده و توابع توابع باشند!

در زبان های غیر OOP ، انجام کارهای مهم مانند ذخیره داده ها در یک فایل ساده است .

یک مثال واقعی, خواهش میکنم!

برگردیم به مثال نقاش(در قسمت سه) ، نقاش صاحب یک PaintingFactory (کارخانه نقاشی!)است. او یک BrushManager اختصاصی ، ColorManager ، یک CanvasManager و یک MonaLisaProvider را ایجاد کرده است. زامبی دوست خوب او از BrainConsumingStrategy استفاده می کند. این اشیاء به نوبه خود متد های زیر را تعریف می کنند: CreatPainting ، FindBrush ، PickColor ، CallMonaLisa و ConsumeBrainz.

البته ، این حماقت آشکار است ، و هرگز نمی توانست در دنیای واقعی اتفاق بیفتد. چقدر پیچیدگی غیر ضروری برای ترسیم یک نقاشی ایجاد شده است؟ وقتی توابع میتوانند مستقل از یک شی وجود داشته باشند , دیگر نیازی به ایجاد مفاهیم عجیب و غریب (کلاس ها)برای نگه داشتن متد ها نیست.

تست واحد – Unit Testing

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

برخی ممکن است مخالف باشند ، اما تست واحد کد OOP بسیار سخت است. تست واحد فرضیه آزمایش جداگانه چیز ها را اراعه میکند و برای اینکه یک متد قابل تست باشد:

  • به یک کلاس مجزا وابسته باشد.
  • یک واسط(interface) برای کلاس ایجاد شده ساخته شده باشد.
  • یک فیلد برای نگهداری کلاس جدید ایجاد شده باشد
  • برای “تزریق وابستگی” از یک چارچوب برای تزریق وابستگی استفاده کنید.

چقدر باید پیچیدگی بیشتری ایجاد شود تا یک تکه کد قابل تست کردن باشد؟ چقدر زمان صرف شده است تا برخی از کدها قابل تست باشند؟

پ.ن: ، ما همچنین باید یک کلاس کامل را مقداردهی(instantiate) کنیم تا بتوانیم یک متد واحد را آزمایش کنیم.

به لطف OOP ، نوشتن تست برای کد موروثی(ویکی پدیا) حتی سخت تر است – تقریبا غیرممکن است. شرکتهای زیادی پیرامون مسئله آزمایش کد موروثی ایجاد شده اند.

کد اضافی – Boilerplate code

کد اضافی احتمالاً بزرگترین متخلف در رابطه با نسبت سیگنال به نویز است. کد اضافی “نویز” است که برای کامپایل برنامه لازم است. برای نوشتن کد اضافی وقت نیاز دارید و باعث می شود که کد به دلیل نویز اضافه شده ، خوانایی کمتری داشته باشد.

در حالی که “program to an interface, not to an implementation” روش پیشنهادی در OOP است ، هر چیزی نباید به یک واسط تبدیل شود. ما تنها باید به منظور قابلیت تست کردن از واسط ها در همه کد استفاده کنیم. ما همچنین احتمالاً باید از تزریق وابستگی استفاده کنیم ، که باعث پیچیدگی های غیر ضروری می شود.

تست کردن متد های خصوصی- private

بعضی ها می گویند که متد های خصوصی نباید تست شوند … من تمایل ندارم که مخالف باشم ، تست واحد به یک دلیل “واحد” نامیده می شود – واحدهای کوچک کد را در انزوا آزمایش کنید. اما آزمایش متد های خصوصی در OOP تقریبا غیرممکن است. ما نباید فقط به خاطر تست پذیری ، متد های خصوصی را ایجاد کنیم.

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

ریفکتورینگ – refactoring

refactoring یکی از قسمت های مهم کار های روزانه برنامه نویسان است. از قضا ، کد OOP به سختی قابل ریفکتور است. refactoring قرار بود پیچیدگی را کمتر و کد را قابل اعتماد تر کند. در مقابل ، کد OOP ریفکتور شده بطور قابل توجهی پیچیده تر می شود – برای قابل تست کردن کد ، باید از تزریق وابستگی استفاده کنیم و واسط-interface ای برای کلاس ریفکتور شده ایجاد کنیم. حتی ، ریفکتور کردن کد OOP بدون ابزار اختصاصی مانند Resharper بسیار سخت است.

این کد ها را داریم:

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

این مورد را با یک ریفکتور مشابه در کد غیر OOP در JavaScript مقایسه کنید:

قبل از ریفکتور:

بعد از ریفکتور کردن:

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

این مورد را با یک ریفکتور مشابه در کد غیر OOP در JavaScript مقایسه کنید:

قبل از ریفکتور:

بعد از ریفکتور:

کد به معنای واقعی کلمه یکسان باقی مانده است – ما فقط متد isValidInput را به فایلی دیگر منتقل کردیم و برای وارد کردن آن متد یک خط اضافه کردیم. ما همچنین به منظور قابلیت تست _isValidInput به امضای تابع اضافه کردیم.

این یک مثال ساده است ، اما در عمل پیچیدگی بصورت نمایی با بزرگتر شدن کد بیشتر می شود.

و این همه چیز نیست. ریفکتور کردن کد OOP بسیار خطرناک است. نمودارهای وابستگی پیچیده و وضعیت های پراکنده در سراسر کد OOP ، مغز انسان را قادر نمی سازد که همه مسائل بالقوه را در نظر بگیرد.

چسب زخم

وقتی چیزی کار نمیکند چه کار میکنیم؟ این ساده است ، ما فقط دو گزینه داریم – آن را دور بیندازید یا سعی کنید آن را اصلاح کنید. OOP چیزی است که به راحتی قابل دور ریختن نیست ، میلیون ها توسعه دهنده در OOP آموزش می بینند. و میلیون ها سازمان در سراسر جهان از OOP استفاده می کنند.

احتمالاً اکنون می بینید که OOP واقعاً کار نمی کند ، کد ما را پیچیده و غیر قابل اعتماد می کند. و شما تنها نیستید! مردم چندین دهه است که تلاش می کنند تا مشکلات موجود در کد OOP را حل کنند ، سخت فکر می کنند.که نتیجه آن تولید بی شمار الگو طراحی شده است.

الگو های طراحی

برنامه نویسی شی گرا مجموعه ای از دستورالعمل ها را ارائه می دهد که از نظر تئوری باید به توسعه دهندگان اجازه دهد سیستم های بزرگتر و بزرگتر را بصورت تدریجی بسازند: اصل SOLID ، تزریق وابستگی ، الگوهای طراحی و سایر موارد.

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

کارخانه مشکل

در حقیقت ، نوشتن کد شیء گرا و خوب ، غیرممکن است. در یک طرف ماجرا ، یک کد OOP داریم که نا سازگار است و به نظر نمی رسد که مطابق استاندارد باشد. در طرف دیگر ماجرا ، ما یک برجی از کدهای بیش از حد مهندسی و یک دسته از انتزاعات نادرست داریمک که یکی در بالای دیگری ساخته شده است. الگوهای طراحی در ساخت چنین برج هایی از انتزاعات بسیار مفید هستند.

به زودی ، اضافه کردن قابلیت های جدید و حتی حس کردن همه پیچیدگی ها ، سخت تر و سخت تر می شود. کد مملو از مواردی مانند SimpleBeanFactoryAwareAspectInstanceFective ، AbstractInterceptorDrivenBeanDefinitionDecorator ، TransactionAwarePersistenceManagerFactoryProxyorRequestProcessorFactoryFective خواهد بود.

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

فرو ریختن چهار ستون OOP

چهار ستون OOP عبارت اند از : انتزاع-Abstraction و وراثت و محصور سازی-Encapsulation و چند ریختی-Polymorphism.

بیایید یکی یکی ببینیم واقعاً چه هستند

وراثت

من فکر می کنم عدم قابلیت استفاده مجدد در زبانهای شی گرا و جود دارد، نه در زبانهای فانکشنال. مشکل زبانهای شی گرا این است که آنها کلی محیط ضمنی را با خود حمل میکنند. شما یک موز می خواستید اما آنچه شما گرفتید یک گوریل موز و کل جنگل بود. —- Joe Armstrong خالق ارلانگ

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

وراثت چند مشکل دارد:

  • کدی را وارد کنید که کلاس شما حتی نیازی به آن نداشته باشد (مسئله موز و جنگل).
  • داشتن بخش هایی از کلاس شما که در جایی دیگر تعریف شده است ، تحلیل آن را دشوار می کند ، مخصوصاً با وجود چندین سطح وراثت.
  • در اکثر زبانهای برنامه نویسی ، وراثت چندگانه حتی ممکن نیست. این بیشتر وراثت را به عنوان مکانیسم اشتراک کد ، بی فایده می کند.

چند ریختی OOP

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

از طرف دیگر برنامه نویسی functional به ما این امکان را می دهد که به شکلی ظریف تر به همان چند ریختی دست یابیم … به سادگی با ک استفاده از متدی که رفتار دلخواه ما در زمان اجرا را مشخص میکند. چه چیزی می تواند ساده تر از این باشد؟ نیازی به تعریف دسته ای از متد مجازی و اضافی بارگذاری شده در چندین فایل (و واسط) نیست.

محصور سازی

همانطور که قبلاً بحث کردیم ، محصور کردن اسب تروجان OOP است. این در واقع یک وضعیت قابل تغییر عمومی(جهانی- global) قابل تغییر است و باعث می شود کد ناامن به نظر بی خطر برسد. کد ناامن ستونی است که برنامه نویسان OOP در کار روزانه خود به آن تکیه میکنند …

انتزاع

انتزاع در OOP تلاش می کند با پنهان کردن جزئیات غیرضروری از برنامه نویس ، با پیچیدگی ها مقابله کند. از لحاظ تئوریک ، این امکان را برای توسعه دهنده فراهم می کند که بدون نیاز به فکر کردن در مورد پیچیدگی پنهان شده ، کد را تحلیل کند.

من نمی دانم چه بگویم … یک کلمه جالب برای یک مفهوم ساده. در زبانهای رویه ای / functional ما می توانیم به سادگی جزئیات اجرا (همون پیچیدگی هایی که باید مخفی شود) را در یک فایل همسایه مخفی کنیم. نیازی نیست که این عمل پایه ای را “انتزاع” بنامیم.

چرا OOP بر صنعت حاکم است؟

پاسخ ساده است ، نژاد بیگانه ای از خزندگان با NSA (و روسها) توطئه کرده اند تا ما برنامه نویسان را تا حد مرگ شکنجه کنند…

اما به طور جدی ، جاوا احتمالاً پاسخ مورد نظراست.

جاوا نگران کننده ترین چیزی است که از زمان MS-DOS (ویکی پدیا)برای کامپیوتر اتفاق افتاده است. — الن کی – خالق OOP

جاوا ساده بود

هنگامی که اولین بار در سال 1995 معرفی شد ، جاوا در مقایسه با گزینه های دیگر یک زبان برنامه نویسی بسیار ساده بود . در آن زمان ، موانع زیادی برای ورود به نوشتن برنامه های دسک تاپ بود. توسعه برنامه های دسک تاپ شامل نوشتن API های سطح پایین win32 در C است ، و توسعه دهندگان نیز مجبور بودند که خود را با مدیریت حافظه دستی نگران کنند. جایگزین دیگر ویژوال بیسیک بود ، اما احتمالاً بسیاری نمی خواستند خود را در اکوسیستم مایکروسافت قفل کنند.

هنگامی که جاوا معرفی شد ، برای بسیاری از برنامه نویسان واضح بود چونکه رایگان بود ، و می توانست در تمام سیستم عامل ها مورد استفاده قرار گیرد. مواردی مانند جمع آوری زباله های داخلی ، API هایی با نام دوستانه (در مقایسه با API های رمزنگاری win32) ، مکان های نام مناسب و سینتکس آشنای C مانند جاوا را قابل دسترسی تر می کند.

برنامه نویسی GUI نیز رایج تر شد و به نظر می رسید که کامپوننت های مختلف UI به خوبی با کلاس ها سازگار شده اند. تکمیل خودکار متد در IDE همچنین باعث شد تا مردم ادعا کنند که API های OOP ساده تر هستند.

شاید جاوا اینقدر بد نمی بود اگر برنامه نویسان را به استفاده از OOP مجبور نمیکرد. همه چیز در مورد جاوا بسیار خوب به نظر می رسید. جمع آوری زباله ، قابلیت حمل و نقل ، ویژگی های دست زدن به استثناء ، که سایر زبان های برنامه نویسی اصلی فاقد آن بودند ، در سال 1995 واقعاً عالی بودند ،

سی شارپ از راه میرسد

در ابتدا ، مایکروسافت به شدت به جاوا تکیه کرده بود. هنگامی که همه چیز بدتر شد (و پس از یک نزاع حقوقی طولانی با Sun Microsystems بر سر صدور مجوز جاوا) ، مایکروسافت تصمیم گرفت روی نسخه ای شخصی از جاوا سرمایه گذاری کند. این زمانی است که C # 1.0 به دنیا آمد. C # به عنوان یک زبان همیشه به عنوان “جاوا بهتر” تصور می شد. با این حال ، یک مشکل بزرگ وجود دارد – این همان زبان OOP با همان نقص ها بود ، که در زیر یک سینتکس کمی بهبود یافته پنهان شده بودند.

مایکروسافت سرمایه گذاری زیادی در اکوسیستم .NET خود انجام داده است ، که شامل ابزارهای توسعه دهنده خوبی نیز می باشد. سالهاست که ویژوال استودیو احتمالاً یکی از بهترین IDE های موجود بوده است. این به نوبه خود منجر به تصویب گسترده چارچوب .NET ، به ویژه در شرکت شده است.

اخیراً مایکروسافت با هل دادن TypeScript خود سرمایه گذاری زیادی در اکوسیستم مرورگر انجام داده است. TypeScript بسیار عالی است زیرا می تواند جاوا اسکریپت خالص را کامپایل کند و مواردی مانند بررسی نوع استاتیک را اضافه می کند. انچه در ان جالب نیست این است که از پشتیبانی مناسب برای ایجاد برنامه های فانکشنال برخوردار نیست – هیچ ساختار داده غیرقابل تغییری ساخته شده نشده ، ترکیب متد وجود ندارد و عدم تطبیق الگوی مناسب. TypeScript اول OOP است ، و بیشتر C # برای مرورگر ها است. Anders Hejlsberg حتی مسئول طراحی هر دو C # و TypeScript بود.

زبان های فانکشنال

از سوی دیگر ، زبانهای functional هرگز توسط کسی به اندازه مایکروسافت پشتیبانی نشده اند. F # حساب حساب نمیشود زیرا سرمایه گذاری ناچیزی پشت ان بود. توسعه زبانهای functional بیشتر جامعه محور است. این احتمالاً تفاوت محبوبیت بین زبانهای OOP و FP را توضیح می دهد.

زمان حرکت کردن است؟

اکنون می دانیم که OOP تجربه ای است که شکست خورده است. وقت حرکت است. زمان آن رسیده است که ما به عنوان یک جامعه بپذیریم که این ایده ما را ناکام کرده است و باید از آن ترک کنیم. — Lawrence Krubner

چرا ما از چیزی استفاده می کنیم که اساساً یک راه کم اهمیت برای سازماندهی برنامه ها است؟ آیا این جاهلیت آشکار است؟ من شک دارم ، افرادی که در مهندسی نرم افزار کار می کنند احمق نیستند. آیا با استفاده از اصطلاحات فانتزی OOP مانند “الگوهای طراحی” ، “انتزاع” ، “محصور سازی” ، “چند ریختی” و “تفکیک واسط-interface segregation” ، میخواهیم بین همسالانمان “هوشمند” به نظر برسیم؟ احتمالا نه.

من فکر می‌کنم که استفاده از چیزی که چندین دهه است از آن استفاده کرده‌ایم، ادامه پیدا کند. اکثر مردم هرگز واقعا یک برنامه‌نویسی functional را امتحان نکرده اند. آن‌هایی که مثل من (مثل خودم) امتحان کرده اند هرگز نمی‌توانند به نوشتن کد OOP برگردند.

هنری فورد یک جمله معروف دارد: “اگر من از مردم میپرسیدم چه چیزی میخواهند ، آنها سریع می گفتند اسب”. در دنیای نرم افزار ، بیشتر افراد احتمالاً “زبان OOP بهتر” می خواهند. مردم به راحتی می توانند مشکلی را که دارند (بدست اوردن کد سازماندهی شده و ساده تر) توصیف کنند ، اما بهترین راه حل نیست.

گزینه های روی میز چیست؟

اگر اصطلاحاتی از قبیل functor ها و monad ها شما را کمی ناراحت می کند ، پس تنها نیستید! برنامه نویسی کاربردی چندان ترسناک نبود اگر نام های شهودی بیشتری به برخی از مفاهیم آن داده شده بود. Functor؟ خیلی ساده,‌چیزی است که با یک متد میشود ان را توصیف کرد ,مثلا list.map. موناد-Monad؟ به سادگی محاسباتی که می توان زنجیره ای کرد!

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

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

دو زبان functional عالی با منحنی یادگیری بسیار ملایم Elixir و Elm هستند. آنها به توسعه دهنده روی چیزی که مهم تر است تمرکز کنند — نوشتن نرم افزار قابل اعتماد در حالی که تمام پیچیدگی هایی را که زبانهای کاربردی سنتی تر دارند از بین ببرد.

گزینه های دیگر چیست؟ آیا شرکت شما قبلاً از C # استفاده می کرد؟ سعی کنید F # را امتحان کنید – یک زبان کاربردی شگفت انگیز است ، و قابلیت همکاری عالی با کد NET موجود را فراهم می کند. استفاده از جاوا؟ سپس استفاده از Scala یا Clojure هر دو گزینه خوب هستند. از JavaScript استفاده می کنید؟ با راهنمایی و پوشش صحیح ، جاوا اسکریپت می تواند یک زبان functional خوب باشد.

مدافعان OOP

اف تی اپیک

من انتظار نوعی عکس العمل از مدافعان OOP دارم. آنها خواهند گفت که این مقاله مملو از غلط است. برخی حتی ممکن است شروع به نام بردن اسم کنند. آنها حتی ممکن است من را یک توسعه دهنده “جوان” بنامند که هیچ تجربه OOP در دنیای واقعی ندارد. ممکن است برخی بگویند فرضیات من اشتباه است و مثال های آن بلا استفاده است.

آنها حق نظر خود را دارند. با این حال ، استدلال های آنها در دفاع از OOP معمولاً بسیار ضعیف است. طعنه آمیز است که احتمالاً اکثر آنها هرگز واقعا با زبان functional برنامه نویسی نکرده اند. اگر کسی که واقعاً هر دو را امتحان نکرده باشد ، چگونه می تواند بین دو چیز مقایسه کند؟ چنین مقایسه هایی مفید نیستند.

قانون Demeter خیلی هم مفید نیست – هیچ کاری برای پرداختن به مسئله عدم قطعیت نمی کند ، وضعیت تغییر پذیر هنوز هم وضعیت تغییر پذیر است ، مهم نیست که چگونه به آن حالت دسترسی پیدا کرده یا تغییر پیدا کند. a.total () خیلی بهتر از a.getB().getC().total()نیست؟.

قانون Domain-Driven Design؟ این یک روش طراحی مفید است ، و کمی به کاهش پیچیدگی کمک می کند. با این حال ، هنوز هیچ کاری برای پرداختن به مسئله اساسی وضعیت مشترک قابل تغییر پذیر نمی کند.

فقط یک ابزار در جعبه ابزار…

من اغلب می شنوم که مردم می گویند OOP فقط یکی دیگر از ابزارهای جعبه ابزار است. بله ، به همان اندازه یک ابزار در جعبه ابزار است زیرا اسب ها و ماشین ها هر دو وسیله حمل و نقل هستند … از اینها که بگذریم ، همه آنها به همان هدف خدمت می کنند ، درست است؟ چرا وقتی می توانیم سوار اسب های قدیمی خوب شویم ، از اتومبیل استفاده می کنیم؟

تاریخ دوباره تکرار میشود

این در واقع چیزی را به من یادآوری می کند. در آغاز قرن بیستم ، اتومبیل ها شروع به جایگزینی اسب ها کردند. در سال 1900 در نیویورک تنها چند اتومبیل در جاده ها وجود داشت ، مردم از اسب ها برای حمل و نقل استفاده می کردند. در سال 1917 ، دیگر اسب در جاده ها باقی نماند. صنعت عظیمی حول حمل و نقل با حمل اسب قرار داشت. مشاغل زیادی پیرامون مواردی مانند تمیز کردن کود ایجاد شده بود.

و مردم در برابر تغییر مقاومت کردند. آنها اتومبیل ها را یکی دیگر از “مد” خواندند که در نهایت می گذرد.هر چه باشد، اسب ها قرن ها در اینجا بوده اند! برخی حتی از دولت خواستند که مداخله کند.

یعنی چه؟ صنعت نرم افزار تقریبا OOP محور است. میلیون ها نفر در OOP آموزش دیده اند ، و میلیون ها شرکت در کد خود از OOP استفاده می کنند. البته آنها سعی خواهند کرد هر چیزی را که تهدید کننده نان و کره آنها است ، بی اعتبار کنند! این فقط عقل سلیم است.

ما به وضوح می بینیم که تاریخ در حال تکرار است – در قرن بیستم این اتومبیل ها در مقابل اسب ها بودند ، در قرن بیست و یکم این برنامه نویسی شیء گرا در مقابل functional است.

error: