Можете ли вы объяснить принцип замены Лискова хорошим примером C #?

Можете ли вы объяснить принцип замены Лискова («L» SOLID) с хорошим примером C #, охватывающим все аспекты принципа в упрощенном виде? Если это действительно возможно.

    3 Solutions collect form web for “Можете ли вы объяснить принцип замены Лискова хорошим примером C #?”

    (Этот ответ был переписан в 2013-05-13, прочитал обсуждение в нижней части комментариев)

    LSP идет о заключении контракта базового classа.

    Вы можете, например, не генерировать новые исключения в подclassах, поскольку тот, который использует базовый class, не ожидал этого. То же самое происходит, если базовый class бросает ArgumentNullException если аргумент отсутствует, а подclass позволяет аргументу быть нулевым, а также нарушение LSP.

    Вот пример структуры classа, которая нарушает LSP:

     public interface IDuck { void Swim(); // contract says that IsSwimming should be true if Swim has been called. bool IsSwimming { get; } } public class OrganicDuck : IDuck { public void Swim() { //do something to swim } bool IsSwimming { get { /* return if the duck is swimming */ } } } public class ElectricDuck : IDuck { bool _isSwimming; public void Swim() { if (!IsTurnedOn) return; _isSwimming = true; //swim logic } bool IsSwimming { get { return _isSwimming; } } } 

    И вызывающий код

     void MakeDuckSwim(IDuck duck) { duck.Swim(); } 

    Как вы можете видеть, есть два примера уток. Одна органическая утка и одна электрическая утка. Электрическая утка может плавать только в том случае, если она включена. Это нарушает принцип LSP, поскольку он должен быть включен, чтобы плавать, поскольку IsSwimming (который также является частью контракта) не будет установлен как в базовом classе.

    Вы можете, конечно, решить это, сделав что-то вроде этого

     void MakeDuckSwim(IDuck duck) { if (duck is ElectricDuck) ((ElectricDuck)duck).TurnOn(); duck.Swim(); } 

    Но это нарушит принцип Open / Closed и должно быть реализовано повсеместно (и, тем не менее, оно порождает неустойчивый код).

    Правильным решением было бы автоматически включить утку в методе Swim и, таким образом, заставить электрическую утку вести себя точно так, как это определено интерфейсом IDuck

    Обновить

    Кто-то добавил комментарий и удалил его. Он имел действительную точку, о которой я хотел бы обратиться:

    Решение с включением утки внутри метода Swim может иметь побочные эффекты при работе с фактической реализацией ( ElectricDuck ). Но это можно решить, используя явную реализацию интерфейса . imho, скорее всего, вы получите проблемы, НЕ включив его в Swim так как ожидается, что он будет плавать при использовании интерфейса IDuck

    Обновление 2

    Перефразируйте некоторые части, чтобы сделать их более ясными.

    LSP – практический подход

    Повсюду я ищу примеры LSP на C #, люди использовали мнимые classы и интерфейсы. Вот практическая реализация LSP, которую я реализовал в одной из наших систем.

    Сценарий: предположим, что у нас есть 3 базы данных (клиенты ипотечных кредитов, клиенты текущих счетов и клиенты сберегательных счетов), которые предоставляют данные о клиентах, и нам нужны данные о клиенте для имени последнего клиента. Теперь мы можем получить более 1 клиентскую информацию из этих 3 баз данных против данной фамилии.

    Реализация:

    БИЗНЕС-МОДЕЛЬНЫЙ СЛОЙ:

     public class Customer { // customer detail properties... } 

    ДАННЫЙ ДОСТУПНЫЙ СЛОЙ:

     public interface IDataAccess { Customer GetDetails(string lastName); } 

    Над интерфейсом реализован абстрактный class

     public abstract class BaseDataAccess : IDataAccess { ///  Enterprise library data block Database object.  public Database Database; public Customer GetDetails(string lastName) { // use the database object to call the stored procedure to retrieve the customer details } } 

    Этот абстрактный class имеет общий метод «GetDetails» для всех 3 баз данных, который расширяется каждым из classов базы данных, как показано ниже

    ДОСТУП ДАННЫХ КЛИЕНТОВ ИПОТЕКИ:

     public class MortgageCustomerDataAccess : BaseDataAccess { public MortgageCustomerDataAccess(IDatabaseFactory factory) { this.Database = factory.GetMortgageCustomerDatabase(); } } 

    ТЕКУЩИЙ СЧЕТ ДОСТУПА КЛИЕНТА КЛИЕНТА:

     public class CurrentAccountCustomerDataAccess : BaseDataAccess { public CurrentAccountCustomerDataAccess(IDatabaseFactory factory) { this.Database = factory.GetCurrentAccountCustomerDatabase(); } } 

    СБЕРЕГАТЕЛЬНЫЙ СЧЕТ ДОСТУПА КЛИЕНТОВ КЛИЕНТОВ:

     public class SavingsAccountCustomerDataAccess : BaseDataAccess { public SavingsAccountCustomerDataAccess(IDatabaseFactory factory) { this.Database = factory.GetSavingsAccountCustomerDatabase(); } } 

    Как только эти 3 classа доступа к данным установлены, теперь мы обращаем наше внимание на клиента. На бизнес-уровне у нас есть class CustomerServiceManager, который возвращает детали клиента своим клиентам.

    БИЗНЕС-СЛОЙ:

     public class CustomerServiceManager : ICustomerServiceManager, BaseServiceManager { public IEnumerable GetCustomerDetails(string lastName) { IEnumerable dataAccess = new List() { new MortgageCustomerDataAccess(new DatabaseFactory()), new CurrentAccountCustomerDataAccess(new DatabaseFactory()), new SavingsAccountCustomerDataAccess(new DatabaseFactory()) }; IList customers = new List(); foreach (IDataAccess nextDataAccess in dataAccess) { Customer customerDetail = nextDataAccess.GetDetails(lastName); customers.Add(customerDetail); } return customers; } } 

    Я не показывал инъекцию зависимостей, чтобы сохранить ее просто, поскольку она уже усложняется.

    Теперь, если у нас есть новая firebase database клиентов, мы можем просто добавить новый class, который расширяет BaseDataAccess и предоставляет свой объект базы данных.

    Конечно, нам нужны идентичные хранимые процедуры во всех участвующих базах данных.

    Наконец, клиент для classа CustomerServiceManager вызовет только метод GetCustomerDetails, передает lastName и не должен заботиться о том, откуда и откуда поступают данные.

    Надеюсь, это даст вам практический подход к пониманию LSP.

    Вот код для применения Принципа замены Лискова.

     public abstract class Fruit { public abstract string GetColor(); } public class Orange : Fruit { public override string GetColor() { return "Orange Color"; } } public class Apple : Fruit { public override string GetColor() { return "Red color"; } } class Program { static void Main(string[] args) { Fruit fruit = new Orange(); Console.WriteLine(fruit.GetColor()); fruit = new Apple(); Console.WriteLine(fruit.GetColor()); } } 

    LSV утверждает: «Производные classы должны быть заменяемы для их базовых classов (или интерфейсов)» и «Методы, которые используют ссылки на базовые classы (или интерфейсы), должны иметь возможность использовать методы производных classов, не зная об этом или не зная подробностей «.

    Interesting Posts

    Являются ли переменные Свифта атомарными?

    Как инициализировать структуру в соответствии со стандартами языка программирования C

    идентифицировать группы связанных эпизодов, которые соединяются вместе

    Как получить абсолютные координаты вида

    Получение пароля учетной записи администратора Windows 7?

    Google Chrome против конфиденциальности железа

    Формула Excel для среднесуточной прибыли

    Есть ли способ пропустить меню «Пуск» и перейти прямо к рабочему столу (без отключения современного пользовательского интерфейса)?

    Является ли порядок инициализации статического classа в C # детерминированным?

    Что делает дефрагментация на самом деле?

    Запись файла на внешнем накопителе в Android

    Извлечь цифры из строки в Java

    Заполнение вкладки оболочки Bash, не расширяйте ~

    Только чтение ограниченного количества столбцов

    Оставляя ноутбук включенным все время

    Давайте будем гением компьютера.