Переопределение метода - единственный способ нарушить LSP - PullRequest
0 голосов
/ 18 декабря 2018

Я искал знаки в коде, которые могли бы сказать, что Принцип замещения Лискова может быть потенциально нарушен.Сначала я создал простой класс и другой класс, унаследованный от него:

public class Adder
{
    public virtual int Add(int operand1, int operand2)
    {
        return operand1 + operand2;
    }
}

public class AdvancedAdder : Adder
{
}

Затем я создал UnitTest, выявляющий нарушение LSP:

public abstract class AdderUnitTestsBase
{
    protected static Adder Adder;

    [DataTestMethod]
    [DataRow(2, 2, 4)]
    [DataRow(-1, 2, 1)]
    [DataRow(2, -3, -1)]
    [DataRow(0, 0, 0)]
    public void Add_ReturnsCorrectResult(
        int operand1, int operand2, int result)
    {
        Assert.AreEqual(result, Adder.Add(operand1, operand2));
    }
}


[TestClass]
public class AdderUnitTests : AdderUnitTestsBase
{
    [ClassInitialize]
    public static void ClassInit(TestContext context)
    {
        Adder = new Adder();
    }
}

[TestClass]
public class AdvancedAdderUnitTests : AdderUnitTestsBase
{
    [ClassInitialize]
    public static void ClassInit(TestContext context)
    {
        Adder = new AdvancedAdder();
    }
}

Затем я попробовал разные вещи, такие как изменение типа параметровили изменение количества параметров:

public class AdvancedAdder : Adder
{
    public int Add(int operand1, int operand2, int operand3)
    {
        return operand1 + operand2 + operand3;
    }

    public uint Add(uint operand1, uint operand2)
    {
        return operand1 + operand2;
    }
}

Это не нарушало LSP, потому что методы имеют разные подписи.Это просто расширенный функционал.Единственное, что сработало, это использование ключевого слова «переопределить»:

public class AdvancedAdder : Adder
{
    public override int Add(int operand1, int operand2)
    {
        return operand1 - operand2;
    }
}

Похоже, если я избегаю «переопределения» в C #, мне не нужно беспокоиться о возможном нарушении принципа подстановки Лискова. Как вы думаете, это правильно?

Ответы [ 2 ]

0 голосов
/ 19 декабря 2018

Стоит также отметить, что LSP не следует рассматривать как строгое правило, которое вы должны применять в любой ситуации.

Например, составной шаблон (https://en.wikipedia.org/wiki/Composite_pattern) может предоставить ваммеханизм для единообразной обработки всех узлов (что хорошо). Но если вы будете интерпретировать LSP строго, это нарушит LSP.

Как и большинство объектно-ориентированных правил и шаблонов проектирования, это всего лишь руководствоэто должно указывать вам правильное направление, но может быть отменено другими требованиями. В конце концов, общая сложность архитектуры должна быть уменьшена, а не увеличена. Здесь, например, составной шаблон позволит вам обрабатывать объекты более единообразно ипредоставьте более простые алгоритмы.

Для вашего вопроса. Если вы также рассмотрите возможность обмена сообщениями для объектно-ориентированного проектирования, то у вас также будут категории объектов, которые обрабатывают определенное сообщение / событие, тогда это также способ сломать LSP (вместо этогоиспользования только виртуального и переопределения).

В конце концов, вы должны учитывать, что истина всегда где-то посередине.

0 голосов
/ 18 декабря 2018

Переопределение - это просто техническое решение для переопределения метода.LSP больше относится к семантике, чем к технике.

Похоже, если я избегаю "переопределения" в C #, мне не нужно беспокоиться о возможном нарушении принципа подстановки Лискова.

Возможно, вы имеете в виду полиморфизм , потому что override - это всего лишь один способ изменить поведение объектов (динамическое связывание).Вы также можете использовать new для не virtual методов (статическое связывание).Вы даже можете использовать проверку (как особый случай отражения) и изменить поведение на основе этого:

public void method()
{
    switch (this.getType().Name)
    {
        case "Square":
            // do the square thing
            break;
        case "Rectangle":
            // do the rectangle thing
            break;
    }
}

Определенный таким образом метод может очень хорошо сломать LSP без наследования или override / new ключевые слова.

Это действительно сложнее, чем простой синтаксис , и больше о семантике (значение).

Даже классический пример LSP с квадратами / прямоугольникамиэто все о значении.Если вы думаете, что квадрат / прямоугольник - это просто слова без смысловой основы, у вас нет никаких ожиданий относительно их поведения.В результате у вас нет оснований думать, что вы можете заменить один на другой и не можете сломать LSP.

Наследование, с другой стороны, является структурой кода, которая сообщает вам:

PARENT
  ^
  |
CHILD

Ребенок может заменить родителя, потому что он наследует все поведение.

...