شبیه ولی متفاوت مثل Abstract و Interface

کلاس Abstract و Interface دو ویژگی مهم در زبان C# هستند که به شما اجازه می‌دهند تا عقب‌نشینی (abstraction) و پلی‌مورفیسم را در برنامه‌نویسی شیء‌گرا پیاده‌سازی کنید، اما دارای تفاوت‌های مهمی هستند:abstraction چیست ؟ در مهندسی نرم‌افزار و برنامه‌نویسی، "Abstraction" به معنای فرآیند تخصیص نمایی از جزئیات یک شیء یا یک نظام است تا فقط جوانب مهم و معنادار آن نمایش داده شود. این اصل برنامه‌نویسی به برنامه‌نویسان این امکان را می‌دهد که به شکل مفهومی و انتزاعی به مسائل نگاه کنند و جزئیات پیچیده را مخفی کنند. Abstraction یکی از اصول مهم معماری نرم‌افزار و برنامه‌نویسی شیء‌گرا است.در Abstraction، ما فقط به نکات مهم و اساسی یک موضوع توجه می‌کنیم و جزئیات پیچیده و مبهم را مخفی می‌کنیم. این به ما این امکان را می‌دهد که مفهومی از یک شیء یا نظام را تعریف کرده و با آن کار کنیم، بدون نگرانی از پیچیدگی داخلی یا جزئیات پیاده‌سازی.برای مثال، در برنامه‌نویسی شیء‌گرا، یک کلاس می‌تواند از Abstraction بهره ببرد. می‌توانید یک کلاس به عنوان یک مدل از یک مفهوم ایجاد کنید و جزئیات داخلی و پیچیدگی‌های پیاده‌سازی را مخفی کنید. سپس با استفاده از شیء ایجاد شده از این کلاس، می‌توانید با مفهوم انتزاعی کار کنید بدون آنکه نیاز به دانستن همه جزئیات داخلی کلاس داشته باشید.Abstraction یکی از اصول اصلی معماری نرم‌افزار است و به ما کمک می‌کند کد منظم‌تری بنویسیم، قابلیت تعمیر و تغییر را افزایش دهیم و تجزیه‌تحلیل و طراحی موثرتری داشته باشیم. این اصل به ما این امکان را می‌دهد که با پیچیدگی‌های مسائل برنامه‌نویسی به شکل موثرتری برخورد کنیم و برنامه‌های بهتری ایجاد کنیم.پلی‌مورفیسم چیست ؟لی‌مورفیسم یکی از مفاهیم کلیدی در برنامه‌نویسی شیء‌گرا است و به معنای "بیشتر از یک شکل" (Poly + Morphism) است. این مفهوم به معنای توانایی ایجاد یک رفتار یا عملکرد متفاوت در یک شیء در زمان اجرا اشاره دارد. با استفاده از پلی‌مورفیسم، می‌توانید کد خود را به شکلی نوشته و طراحی کنید که بتواند با انواع مختلفی از شیء‌ها به منظور انجام عملیات متفاوت کار کند.دو نوع پلی‌مورفیسم وجود دارد:پلی‌مورفیسم کامپایلری (Compile-time Polymorphism یا Static Polymorphism): در این نوع پلی‌مورفیسم، تصمیمات مرتبط با انتخاب تابع مناسب در زمان کامپایل اتخاذ می‌شود. این به معنای استفاده از توابع اضافی یا اورلود (Overload) و تعداد ثابت و معلومی از پارامترها می‌باشد. برای مثال، در زبان‌های برنامه‌نویسی مانند C++ و C#, می‌توانید چندین نسخه از یک تابع با نام یکسان اما با تعداد و نوع پارامترهای مختلف ایجاد کنید و در زمان کامپایل تصمیم بگیرید که کدام نسخه از تابع باید صدا زده شود.پلی‌مورفیسم اجرایی (Run-time Polymorphism یا Dynamic Polymorphism): در این نوع پلی‌مورفیسم، تصمیمات مرتبط با انتخاب تابع مناسب در زمان اجرا (runtime) اتخاذ می‌شود. این نوع پلی‌مورفیسم معمولاً با استفاده از ارث‌بری و اجرای متدها در کلاس‌های مشتق شده از یک کلاس پایه (که به عنوان والدین شناخته می‌شود) تحقق می‌یابد. در اینجا، یک شیء از نوع کلاس والدین ایجاد می‌شود و تابع مناسب به توجه به نوع واقعی شیء در زمان اجرا اجرا می‌شود.مزیت اصلی پلی‌مورفیسم این است که کد قابلیت انعطاف بیشتری دارد و به برنامه‌نویس این امکان را می‌دهد که با استفاده از یک رابط مشترک برای اشیاء مختلف، عملکرد مشابه را برای همه آنها فراهم کند. این به بهبود قابلیت تعمیر و نگهداری کد، کاهش تکرار کد، و ایجاد ساختارهای کد مرتبط با منطقی و تعاملی کمک می‌کند.در زبان‌های برنامه‌نویسی متفاوت، پلی‌مورفیسم به شکل‌های مختلفی معمولاً با استفاده از مفاهیم مانند اورلود (Overloading) و اوررایدینگ (Overriding) پیاده‌سازی می‌شود.کلاس Abstract:یک کلاس Abstract یک کلاس پایه (Base Class) است که از آن نمی‌توان شیء ایجاد کرد. به عبارت دیگر، این کلاس به تنهایی نیاز به یک کلاس فرزند (Concrete Class) دارد که از آن ارث‌بری می‌کند و تمامی عضوهای کلاس پایه را پیاده‌سازی می‌کند.یک کلاس Abstract می‌تواند شامل عضوهای داده‌ای و عضوهای عملیاتی (متدها) باشد.یک کلاس Abstract می‌تواند دارای متدهای Abstract باشد. متدهای Abstract هستند که فقط امضا آنها در کلاس پایه تعریف می‌شود و پیاده‌سازی آنها در کلاس‌های فرزند انجام می‌شود.مثال کلاس Abstract در C#:public abstract class Shape{ // متدهای Abstract public abstract class Shape { // متدهای Abstract public abstract double CalculateArea(); public abstract double CalculatePerimeter(); } public class Circle : Shape { private double radius; public Circle(double radius) { this.radius = radius; } public override double CalculateArea() { return Math.PI * radius * radius; } public override double CalculatePerimeter() { return 2 * Math.PI * radius; } }کلاس Abstract و Interface دو ویژگی مهم در زبان C# هستند که به شما اجازه می‌دهند تا عقب‌نشینی (abstraction) و پلی‌مورفیسم را در برنامه‌نویسی شیء‌گرا پیاده‌سازی کنید، اما دارای تفاوت‌های مهمی هستند:کلاس Abstract:یک کلاس Abstract یک کلاس پایه (Base Class) است که از آن نمی‌توان شیء ایجاد کرد. به عبارت دیگر، این کلاس به تنهایی نیاز به یک کلاس فرزند (Concrete Class) دارد که از آن ارث‌بری می‌کند و تمامی عضوهای کلاس پایه را پیاده‌سازی می‌کند.یک کلاس Abstract می‌تواند شامل عضوهای داده‌ای و عضوهای عملیاتی (متدها) باشد.یک کلاس Abstract می‌تواند دارای متدهای Abstract باشد. متدهای Abstract هستند که فقط امضا آنها در کلاس پایه تعریف می‌شود و پیاده‌سازی آنها در کلاس‌های فرزند انجام می‌شود.مثال کلاس Abstract در C#:csharppublic abstract class Shape { // متدهای Abstract public abstract double CalculateArea(); public abstract double CalculatePerimeter(); } public class Circle : Shape { private double radius; public Circle(double radius) { this.radius = radius; } public override double CalculateArea() { return Math.PI * radius * radius; } public override double CalculatePerimeter() { return 2 * Math.PI * radius; } }Interface:یک Interface یک قرارداد بیرونی است که تعیین می‌کند که چه عضوهای عملکردی (متدها) باید یک کلاس آن را پیاده‌سازی کند. از یک Interface نمی‌توان شیء ایجاد کرد و برخلاف کلاس Abstract، هیچ امکان پیاده‌سازی بدنه متد در اینترفیس وجود ندارد.یک کلاس می‌تواند از چندین Interface به عنوان پایه ارث‌بری کند، اما از تنها یک کلاس ارث‌بری می‌کند.مثال Interface در C#:public interface IShape { double CalculateArea(); double CalculatePerimeter(); } public class Circle : IShape { private double radius; public Circle(double radius) { this.radius = radius; } public double CalculateArea() { return Math.PI * radius * radius; } public double CalculatePerimeter() { return 2 * Math.PI * radius; } }در مثال بالا، ما از یک Interface به نام IShape برای تعیین قرارداد متدهای CalculateArea و CalculatePerimeter استفاده کردیم و سپس کلاس Circle این قرارداد را پیاده‌سازی کرد. توجه کنید که متدهای Interface بدون کلمه کلیدی override اجرا می‌شوند.برای انتخاب بین استفاده از کلاس Abstract و Interface، باید موارد مورد نیاز پروژه و معماری برنامه‌ی شما را در نظر بگیرید. اگر شما نیاز به یک قالب عمومی دارید که تعدادی عضوهای داده‌ای و عملیاتی (متدها) دارد و از آن برای ساخت کلاس‌های فرزند استفاده خواهید کرد، از کلاس Abstract استفاده کنید. اگر شما فقط می‌خواهید تعیین کنید که کلاس‌ها باید چه متدهای عملکردی را پیاده‌سازی کنند، از Interface استفاده کنید. همچنین، در C# یک کلاس می‌تواند از چندین Interface به عنوان پایه ارث‌بری کند، اما از تنها یک کلاس ارث‌بری می‌کند.توجه کنید که در کلاس های Abstract شما نمیتونید برای توابع بدنه تعریف کنید و صرفا یک امضا دارن . سوال اینه که پس اگه نمیشه توی کلاس های Abstract هم بدنه تعریف کرد پس فرقش با Interface چیه ؟ خب داخل C# ما نمیتونیم از چند تا کلاس ارث بری کنیم ولی میشه از چند تا Interface ارث بری کرد ، جالب این که کلاس های Abstract میتونن از هم ارث بری کنند پس مزیت Interface چیه واقعا ؟ در C#، یک کلاس می‌تواند از تنها یک کلاس پایه (Abstract یا Concrete) به عنوان پایه ارث‌بری کند، اما از چندین Interface به عنوان پایه ارث‌بری کند. این امکان به برنامه‌نویس این امکان را می‌دهد که ویژگی‌های مختلف را از چندین منبع (Interface) ترکیب کند و از تمام آنها در کلاس جدید استفاده کند. این یکی از مزایای کار با Interface در مقایسه با کلاس‌های Abstract است.در مقابل، یک کلاس Abstract به عنوان کلاس پایه ارث‌بری می‌شود و از این نظر محدودتر است. این به این معناست که اگر یک کلاس از یک کلاس Abstract ارث‌بری کند، نمی‌تواند از یک کلاس Abstract دیگر به عنوان پایه همزمان استفاده کند.به عبارت دیگر، وقتی از یک کلاس Abstract به عنوان پایه استفاده می‌شود، مشترک بودن بخشی از پیاده‌سازی از کلاس پایه در کلاس فرزند انتظار می‌رود. اما وقتی از چندین Interface به عنوان پایه استفاده می‌شود، هیچ ارتباط وابستگی به پیاده‌سازی مشترک وجود ندارد و کلاس فرزند می‌تواند ویژگی‌های مختلفی را از اینترفیس‌ها به شکل مستقل پیاده‌سازی کند.صحیح است که اگر یک کلاس Abstract از یک کلاس Abstract دیگر ارث‌بری کند، به نظر می‌آید تفاوت معنی‌داری با ارث‌بری از چندین Interface وجود ندارد، از نظر توانایی پیاده‌سازی متدها. اما اینجا چند نکته وجود دارد که نیاز به استفاده از Interface را متمایز می‌کند:پلی‌مورفیسم چندگانه (Multiple Inheritance): در C#, یک کلاس نمی‌تواند از چندین کلاس پایه (Concrete یا Abstract) به عنوان پایه ارث‌بری کند. اما یک کلاس می‌تواند از چندین Interface به عنوان پایه ارث‌بری کند. این ویژگی به برنامه‌نویس این امکان را می‌دهد که ویژگی‌های مختلفی را از منابع متفاوت (Interface‌ها) به کلاس خود اضافه کند و از آنها استفاده کند.ساختار زبانی: استفاده از Interface معمولاً منطقی‌تر است زیرا برای تعیین قرارداد‌های عمومی و ویژگی‌های مشترک، زبان‌های برنامه‌نویسی از Interface به عنوان ابزار معمولی استفاده می‌کنند. این شیوه کار باعث می‌شود که کد شما بیشتر خواناتر و قابل‌فهم‌تر باشد.قابلیت جدایی و تعامل پویا: با استفاده از Interface، می‌توانید تعامل‌پذیری بیشتری در کد خود ایجاد کنید. به عبارت دیگر، شما می‌توانید شیء‌ها را بر اساس قراردادها (Interface) تعامل دهید بدون اینکه به اطلاعات دقیقی در مورد نوع واقعی شیء نیاز داشته باشید. این قابلیت اهمیت بسیاری در طراحی و توسعه برنامه‌های بزرگ دارد.**قابلیت تست و نگهداری: ** از آنجا که Interface‌ها به عنوان قراردادهای عمومی عمل می‌کنند، می‌توانند به سادگی تست و نگهداری کد را تسهیل کنند. به عنوان مثال، می‌توانید موک‌ها (Mocks) را برای تست‌های واحد (Unit Testing) با استفاده از Interface ایجاد کنید.از آنجا که C# از مفهوم ارث‌بری چندگانه (Multiple Inheritance) برای کلاس‌ها پشتیبانی نمی‌کند، استفاده از Interface یک راه منطقی برای به اشتراک گذاری ویژگی‌ها و متدهای مشترک بین کلاس‌ها در C# است. در مقابل، کلاس‌های Abstract به عنوان کلاس‌های پایه برای ایجاد سلسله مراتب از کلاس‌ها با عملکرد مشابه به کار می‌روند اما محدودیت‌هایی دارند که با استفاده از Interface قابل پوشش هستند.امیدوارم که مفید باشه ...