با عرض سلام و احترام.
پیشاپیش از شما دوست عزیز و گرامی، بابت وقتی که برای مطالعه ی این مطلب خواهید گذاشت، سپاسگزارم.
تقاضا دارم، در صورت مشاهده ی اشتباه متنی یا محتوایی، به اینجانب اطلاع دهید تا (ضمن کمک به یادگیری بنده) در اسرع وقت برای اصلاح متن اقدام نمایم.
شماره ی تماس:
09215149218
نشانی پست الکترونیکی:
RezaQadimi.ir@Gmail.com
آدرس سایت ها:
https://Reza-Qadimi.ir - https://WannaDate.ir
هدف من در این مقاله، آشنایی شما با سومین اصل از اصول .S.O.L.I.D، یعنی LSP یا Liskov Substitution Principle است.
اصل Liskov Substitution، یکی دیگر از اصول SOLID است که اولین بار در سال 1988، توسط Barbara Liskov معرفی شد.
موضوع این اصل رابطه والد-فرزندی، یا به عبارتی ویژگی ارث بری/Inheritance در Object Oriented Design می باشد.
این اصل بیان می کند:
"فرض کنید کلاس B از کلاس A ارث بری کرده است. با توجه به این موضوع، هر آبجکتی از کلاس B باید بتواند بدون تحت تاثیر قرار دادن عملکرد و صحتِ "پیاده سازی" یا "برنامه"، جایگزین آبجکتی از کلاس A شود."
به کد زیر توجه کنید:
public interface ISavingAccount { //other method and property... bool Withdrawal(decimal amount); }
public class RegularSavingAccount : object, ISavingAccount { public RegularSavingAccount() : base() { } //other method and property and code... public bool Withdrawal(decimal amount) { decimal moneyAfterWithdrawal = Balance - amount; if (moneyAfterWithdrawal >= 1000) { return true; } else { return false; } } }
public class SalarySavingAccount : object, ISavingAccount { public SalarySavingAccount() : base() { } //other method and property and code... public bool Withdrawal(decimal amount) { decimal moneyAfterWithdrawal = Balance - amount; if (moneyAfterWithdrawal >= 0) { return true; } else { return false; } } }
public class FixDepositSavingAccount : object, ISavingAccount { public FixDepositSavingAccount() : base() { } //other method and property and code... public bool Withdrawal(decimal amount) { string errorMessage = $ "Not supported by { this } account type!" throw new System.Exception(message: errorMessage); } }
public class AccountManager : object { public AccountManager() : base() { } public bool WithdrawalFromAccount( decimal amount, ISavingAccount account) { bool result = account.Withdrawal(amount: amount); return result; } }
OK: ISavingAccount account = new RegularSavingAccount (); AccountManager.WithdrawalFromAccount(amount: amount, account: account);
OK: ISavingAccount account = new RegularSavingAccount (); AccountManager.WithdrawFromAccount(amount: amount, account: account);
Runtime Error: ISavingAccount account = new FixeDepositeSavingAccount(); AccountManager.WithdrawFromAccount(amount: amount, account: account);
مثال بالا، نمونه ای از نقض اصل Liskov Substitution می باشد! چرا که کلاس FixDepositSavingAccount رفتار تابع Withdrawal مربوط به والد خود را نقض کرده است. اما چه باید کرد؟
بار دیگر Liskov Substitution Principle را با هم مرور میکنیم:
کلاس فرزند، نباید functionality والد خود را نقض کند، و آبجکت ساخته شده از کلاس فرزند باید بتواند در هر زمانی جایگزین آبجکت ساخته شده از کلاس والد شود، بدون این که بر عملکرد و یا صحت آن آسیبی وارد نماید.
public interface ISavingAccount { }
public abstract class SavingAccountWithWithdrawal: object, ISavingAccount { public SavingAccountWithWithdrawal() : base() { } public abstract bool Withdrawal(decimal amount); }
public abstract class SavingAccountWithoutWithdrawal : object, ISavingAccount { public SavingAccountWithoutWithdrawal() : base() { } } public class RegularSavingAccount : SavingAccountWithWithdrawal { public RegularSavingAccount() : base() { } //other method and property and code... public override bool Withdrawal(decimal amount ) { // implementation... } }
public class SalarySavingAccount : SavingAccountWithWithdrawal { public SalarySavingAccount() : base() { } //other method and property and code... public override bool Withdrawal(decimal amount ) { // implementation... } }
public class FixDepositSavingAccount : SavingAccountWithoutWithdrawal { public FixDepositSavingAccount () : base() { } }
public class AccountManager : object { public AccountManager() : base() { } public bool WithdrawalFromAccount( decimal amount, SavingAccountWithWithdrawal account) { bool result = account.Withdrawal(amount: amount); return result; } }
با توجه به این موضوع، از این به بعد میتوانیم اطمینان داشته باشیم که پارامتر account پاس داده شده به این تابع از جنس "SavingAccountWithWithdrawal" بوده و متد "Withdrawal" را پیاده سازی کرده است و در صورت ارسال پارامتری که با واسطه یا بی واسطه از این کلاس ارث بری نکرده باشد، در زمان کامپایل با خطا مواجه میشویم (نه Runtime).
OK: ISavingAccount account = new RegularSavingAccount (); AccountManager.WithdrawFromAccount(amount: amount, account: account );
OK: ISavingAccount account = new RegularSavingAccount (); AccountManager.WithdrawFromAccount(amount: amount, account: account);
Compile Error: ISavingAccount account = new FixDepositSavingAccount(); AccountManager.WithdrawFromAccount(amount: amount, account: account);
همانطور که میبینید، در صورت فراخوانی متد "WithdrawFromAccount" کلاس "AccountManager"، با ورودی از جنس "FixDepositSavingAccount"، کامپایلر به ما خطا خواهد داد.
پی نوشت: در مقاله ی بعد، به بررسی اصل Interface Segregation Principle خواهیم پرداخت.
معرفی:
رضا قدیمی هستم. برنامه نویس و دانش آموزِ حوزه ی وب، بسیار مشتاق در یادگیری مفاهیم و اطلاعات جدید در این حوزه.