مرا اسکن کن!


گروه های مقاله های آموزشی

فریم ورک لاراول تکنولوژی های طراحی وب آخرین اخبار دنیای وب GOLANG معماری نرم افزار توسعه فردی شبکه و ارتباطات بهترین طراحی سایت فتوشاپ مدیریت پروژه آموزش Git Windows 10 الگوی طراحی Design Pattern Mac OS PHP LINUX


مگا وب  دیباگ کردن یک کد چندین مرتبه از نوشتن آن سخت‌تر است. بنابراین اگر کد اولیه خود را بسیار هوشمندانه بنویسید، جهت دیباگ کردن آن به اندازه‌ کافی باهوش نخواهید بود.  مگا وب

Brian Kernighan



آزمون چهار گزینه ای
سنجش مهارت در برنامه نویسی



معرفی Liskov Substitution Principle  در قاعده SOLID

معرفی Liskov Substitution Principle در قاعده SOLID



یکی دیگر از اصول SOLID اصل Liskov Substitution Principal نام دارد که به اختصار LSP گویند.

(اگر با مفهوم SOLID آشنایی ندارین پیشنهاد میکنم اول این پست را مطالعه کنید)

 

اصل جانشینی لیسکف چیست

اصل جانشینی لیسکف اولین بار در سال 1987 توسط خانم باربارا لیسکف (Barbara Liskov) در کنفرانسش با عنوان “انتزاع (Abstraction) داده” معرفی شد. او بعدها در مقاله ای که به همراه خانم ژانت وینگ منتشر کرد، مفهوم جایگزینی لیسکف به صورت زیر تعریف می شود:

 

اگر یک موجودیت مثلا کلاس S زیر مجموعه یک کلاس T باشد(برای مثال از آن ارث برده باشد)، اشیایی که از نوع T هستند باید بتواند با اشیایی که از نوع S هستند، جایگزین شوند. این جایگزینی نباید باعث تغییر در ویژگی های مطلوب T شود.

 

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

مقایسه با جهان واقعی : 

شغل یک پدر تجارت املاک است درحالی که پسرش دوست دارد فوتبالیست شود.

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

در یک مثال عمومی‌تر بررسی میکنیم :

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

public class Rectangle

{

    public int Width { get; set; }

    public int Height { get; set; }

}

public class Square:Rectangle

{

    //codes specific to

    //square will be added

}

 

و می توان گفت :

Rectangle o = new Rectangle();

o.Width = 5;

o.Height = 6;

 

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

Rectangle o = new Square();

o.Width = 5;

o.Height = 6;

 

موضوع چیست؟ مگر مربع می‌تواند طول و عرض نا برابر داشته باشد؟! امکان ندارد.

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

آیا ما میتوانیم طول و عرض را در کلاس Square طبق کد زیر دوباره نویسی کنیم؟

public class Square : Rectangle

{

    public override int Width

    {

        get{return base.Width;}

        set

        {

            base.Height = value;

            base.Width = value;

        }

    }

    public override int Height

    {

        get{return base.Height;}

        set

        {

            base.Height = value;

            base.Width = value;

        }

    }

}

 

باز هم اصل LSP نقض میشود چون ما داریم رفتار خاصیت‌های طول و عرض در کلاس مشتق شده را تغییر میدهیم. ولی با توجه به کد بالا یک مستطیل نمیتواند طول و عرض برابر داشته باشد چون در صورت برابری دیگر مستطیل نیست.

اما راه حل چیست؟

یک کلاس انتزاعی (abstract) را به شکل زیر ایجاد و سپس دوکلاس Square و Rectangle  را از آن مشتق میکنیم :

public abstract class Shape

{

    public virtual int Width { get; set; }

    public virtual int Height { get; set; }

}

 

هم اکنون ما دو کلاس مستقل از هم داریم. یکی Square و دیگری Rectangle که هر دو از کلاس Shape مشتق شده اند.

حالا می توانیم بنویسیم :

Shape o = new Rectangle();

o.Width = 5;

o.Height = 6;



Shape o = new Square();

o.Width = 5; //both height and width become 5

o.Height = 6; //both height and width become 6

 

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

در مثالی دیگر از اصل LPC خواهیم داشت: «فرض کنید کلاس C از کلاس B مشتق شده است، بر اساس قاعده LSP، در هر قسمت از برنامه که شئ ای از نوع B استفاده شده است، باید بتوان شئ ای از نوع C را جایگزین کرد، بدون اینکه تغییری در روند اجرای برنامه رخ دهد یا پیغام خطایی دریافت کنیم!» جا افتادن مفهوم این قاعده کمی دشوار است، اما سعی می کنیم با مثالی ساده از پیچیدگی این موضوع کم کنیم و بتوانیم توضیح شفافی از LSP ارائه دهیم. ابتدا حالتی را پیاده سازی می کنیم که قاعده LSP را نقض می کند و در قدم بعدی کد را اصلاح می کنیم که مطابق قاعده LSP باشد. کد زیر را در نظر بگیرید:

public class CollectionBase

{

    public int Count { get; set; }

}



public class Array : CollectionBase

{

     

}

 

بر اساس قواعد  OOP، شما از کد بالا می توانید به صورت زیر استفاده کنید: 

CollectionBase collection = new Array();

var items = collection.Count;

 

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

public class CollectionBase

{

    public int Count { get; set; }



    public virtual void Add(object item)

    {

            

    }

}



public class List : CollectionBase

{

    public override void Add(object item)

    {

        // add item to list

    }

}



public class Array : CollectionBase

{

    public override void Add(object item)

    {

        throw new InvalidOperationException();

    }

}

 

دقت کنید، متد Add را داخل CollectionBase تعریف کردیم، کلاس List از متد Add پشتیبانی می کند اما کلاس آرایه به دلیلی که بالا گفتیم زمان فراخوانی متد Add، ایجاد خطا می کنید: 

CollectionBase array = new Array();

CollectionBase list = new List();

list.Add(2); // works

array.Add(3); // throw exception

 

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

public interface IList

{

    void Add(object item);

}

public class CollectionBase

{

    public int Count { get; set; }

}

public class List : CollectionBase, IList

{

    public void Add(object item)

    {

        // add item to list

    }

}

public class Array : CollectionBase

{

}

 

همانطور که مشاهده می کنید در کد بالا، متد Add، به جای تعریف در کلاس CollectionBase، داخل یک interface به نام IList تعریف شده و کلاس List این interface را پیاده سازی کرده است. با این کار، دیگر امکان فراخوانی متد Add برای کلاس Array وجود ندارد. کد بالا مبتنی بر قاعده LSP است و دیگر آن را نقض نمی کند.

 


نوشته شده توسط :

وحید صمدیان وحید صمدیان



پنجشنبه, 19 مهر 1397

تعداد بازديد : 913

برچسب ها : الگوی طراحی Design Pattern

3.0 ستاره