Пример плохого наследования в C # - PullRequest
5 голосов
/ 17 февраля 2012

Я читал пример простого наследования и натолкнулся на основную идею, что квадрат является базовым типом, а прямоугольник получен из квадрата.

В примере для установки квадратных размеров использовалось свойство с именем Size.Затем в примере для прямоугольника использовались Width и Height.

Это не имело смысла в моей голове, поэтому я его кодировал.при доступе к rectangle всегда будет сбивающее с толку свойство, называемое «Size».

Правильно ли я понял?Или есть способ скрыть другие классы от просмотра Size при взгляде на rectangle?

public class square
{
    public int Size { get; set; }
    public square(int size)
    {
        this.Size = size;
    }
}

public class rectangle : square
{
    public int Width { get { return base.Size; } set { base.Size = value; } }

    public int Height { get; set; }

    public rectangle(int width, int height)
        : base(width)
    {
        Height = height;
    }
}

Ответы [ 8 ]

13 голосов
/ 17 февраля 2012

Вы на 100% правы, что это обратное наследство. Вместо этого у вас должен быть класс Square, унаследованный от класса Rectangle, поскольку квадрат - это особый вид прямоугольника.

Тогда вы получите что-то вроде

public class Rectangle
{
    public int Width { get; private set; }
    public int Height { get; private set; }

    public Rectangle(int width, int height)
    {
        if (width <= 0 || height <= 0)
            throw new ArgumentOutOfRangeException();
        Width = width;
        Height = height;
    }
}

public class Square : Rectangle
{
    public int Size
    {
        get
        {
            return Width;
        }
    }

    public Square(int size)
        : base(size, size)
    {
    }
}
8 голосов
/ 17 февраля 2012

Проблема в том, что прямоугольник не квадрат - Вы также не можете даже сказать, что Square - это Rectangle, потому что это также вызывает много проблем, поскольку Rectangle (обычно) предлагает методы для независимой установки ширины и высоты - это будет ограничено Square - это классическое нарушение принципа подстановки Лискова. - по словам из Википедии:

Типичным примером, который нарушает LSP, является класс Square, производный от класса Rectangle, при условии, что существуют методы getter и setter для ширины и высоты.Класс Square всегда предполагает, что ширина равна высоте.Если объект Square используется в контексте, где ожидается прямоугольник, может возникнуть непредвиденное поведение, поскольку размеры квадрата нельзя (или скорее не следует) изменять независимо.Эта проблема не может быть легко решена: если мы можем изменить методы сеттера в классе Square, чтобы они сохранили инвариант Square (то есть, сохранить размеры равными), то эти методы ослабят (нарушат) постусловия для сеттеров Rectangle, чтоутверждают, что размеры могут быть изменены независимо.Нарушения LSP, как этот, могут на практике быть или не быть проблемой, в зависимости от постусловий или инвариантов, которые фактически ожидаются кодом, который использует классы, нарушающие LSP.Изменчивость является ключевым вопросом здесь.Если бы Square и Rectangle имели только методы получения (то есть, они были неизменяемыми объектами), то никакого нарушения LSP не могло бы произойти.

3 голосов
/ 17 февраля 2012

натолкнулся на основную идею, что квадрат - это базовый тип, а прямоугольник - из квадрата.

Проблема в том, что это неверно -

Квадрат - это особый тип прямоугольника, но прямоугольник не является квадратом.

При этом создание прямоугольного наследующего прямоугольника также опасно, поскольку в итоге вы столкнулись с неожиданной ситуацией.Это распространенное нарушение принципа подстановки Лискова , поэтому часто лучше, чтобы Square и Rectangle реализовывали общий базовый класс, такой как Shape, который содержал бы только свойстваразделяемый обоими классами, такими как Area или Bounds и т. д.

2 голосов
/ 17 февраля 2012

Я думаю, что проблема геометрическая: -)

Прямоугольник НЕ является квадратом. Квадрат - это прямоугольник.

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

Bye, Marco

0 голосов
/ 17 февраля 2012

Обращаясь к вашему вопросу о сокрытии унаследованных членов, вы можете использовать модификатор new:

public class Rectangle : Square {
     private new int Size { get; set; }
     ...
}
0 голосов
/ 17 февраля 2012

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

Например:

public class Rectangle
{
     public int width {get;set;}
     public int height {get;set;}
     public int Area() { return width * height; }
}

public class Square : Rectangle
{
    public override int width
    {
        get { return base.width; }
        set { base.height = value; base.width = value; }
    }

    //... etc ...
}

[Test]
public void Rectangles_area_should_equal_length_times_width()
{
    Rectangle r = new Square();
    r.height = 10;
    r.width  = 15;

    Assert.That(r.Area() == 150); // Fails
}

Вы видите проблему?При ссылке на квадрат из его базового класса поведение меняется неожиданным образом.Правильное решение для Square и Rectangle - братья и сестры (дети Shape), а не родитель / ребенок.

0 голосов
/ 17 февраля 2012

Геометрия 101: квадрат - это прямоугольник, а прямоугольник - это не квадрат.

0 голосов
/ 17 февраля 2012

Квадрат всегда является прямоугольником, но прямоугольник не всегда является квадратом - следовательно, Rectangle должен быть родителем Square, а не наоборот.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...