Все-таки математика права, квадрат это прямоугольник, и в приведенном ниже коде (на Java) нет ничего неприличного:
class Rectangle {
int width, height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int Width() {
return width;
}
public int Height() {
return height;
}
}
class Square extends Rectangle {
public Square(int side) {
super(side, side);
}
}
А если захотелось добавить методы SetWidth и SetHeight, так это уже нечто за пределами математических понятий "квадрат" и "прямоугольник". В математике объекты (не в смысле ООП объекты, нет, в обычном, человеческом смысле объекты), так вот, в математике объекты как правило неизменные, то есть умножая одну матрицу на другую получаем третью, а вовсе не модифицируем первую. В программировании такие объекты тоже встречаются, например String в Java или .NET; и если Rectangle и Square определить неизменными, они вполне могут быть связаны отношением наследования.
Здравствуйте, igna, Вы писали:
I>Все-таки математика права, квадрат это прямоугольник, и в приведенном ниже коде (на Java) нет ничего неприличного:
I>А если захотелось добавить методы SetWidth и SetHeight, так это уже нечто за пределами математических понятий "квадрат" и "прямоугольник". В математике объекты (не в смысле ООП объекты, нет, в обычном, человеческом смысле объекты), так вот, в математике объекты как правило неизменные, то есть умножая одну матрицу на другую получаем третью, а вовсе не модифицируем первую. В программировании такие объекты тоже встречаются, например String в Java или .NET; и если Rectangle и Square определить неизменными, они вполне могут быть связаны отношением наследования.
И зачем такое нужно??? Это уже ближе к ФП...
Вот представь. Скажем, есть у меня задача разбить плоскость на несколько прямоугольников. Я как-нибудь итеративно это буду делать (ну, я "от балды" пример привёл), каждую итерацию изменяя размеры прямоугольника. И что, я буду ради этих мат. сущностей себе удобство программирования портить????
В этом вообще нет ничего страшного. Его можно и так наследовать, и по другому, а иногда — вообще никак. Всё зависит от конкретной задачи. Программы никогда не были идеальными с точки зрения логики... и не будут. Наследование вообще бывает чисто техническое — увидел, что кода половина копируется copy-past, сделал наследование. У меня так часто бывает, а как уж они там соотносятся в виде моделей реальной среды... кто их знает.
Потому что программный код — это модель ПРОГРАММЫ, а не реальных вещей, и там мы можем делать всё, что хотим на свой страх и риск... ну, а потом за это отвечать, может быть, придётся
Здравствуйте, FDSC, Вы писали:
FDS>И зачем такое нужно???
Да может быть практически и не нужно, только чтоб ясность была.
Нередко попадается мнение, что то "в программировании квадрат — не прямоугольник", то вообще "прямоугольник нужно наследовать от квадрата". И хотя объяснение кажущемуся парадоксу имеется, вот оно например: http://www.objectmentor.com/resources/articles/lsp.pdf, но очень уж там много, намного больше чем нужно.
На самом деле обьяснение может быть совсем простым: квадрат это прямоугольник (как и в математике), но изменяемый квадрат не является изменяемым прямоугольником. В программировании обычно объекты изменяемые, ну мы и привыкли называть изменяемые квадрат и прямоугольник просто квадратом и просто прямоугольником, а потом вдруг вспоминаем про школьные квадрат и прямоугольник, и все, вот они три сосны и блуждающие среди них программисты.
Здравствуйте, igna, Вы писали:
I>Все-таки математика права, квадрат это прямоугольник, и в приведенном ниже коде (на Java) нет ничего неприличного:
Все здесь неприлично. Наследование — это расширение понятия, когда все экзмепляры A являются также экзмеплярами B, но ни один экзмепляр B не является экземпляром A.
Тут же квадрат — это частный случай прямоугольника, да он им является, но из всего множества возможных прямоугольников некоторые являются квадратами.
Здравствуйте, igna, Вы писали:
I>Все-таки математика права, квадрат это прямоугольник, и в приведенном ниже коде (на Java) нет ничего неприличного:
Мне эти разговоры о наследовании квадрата от прямоугольника (или наоборот) всегда казались глупыми. На самом деле это два разных типа, но:
1) преобразование типа КВАДРАТ->ПРЯМОУГОЛЬНИК имеет смысл всегда;
2) преобразование типа ПРЯМОУГОЛЬНИК->КВАДРАТ имеет смысл только когда ширина равна высоте.
В информатике есть куча подобных примеров. Например типы short & long. Для них также верны утверждения (1) & (2), но почему-то никому в здравом уме не приходит в голову наследовать short от long (или наоборот)
Здравствуйте, igna, Вы писали:
I>Здравствуйте, FDSC, Вы писали:
FDS>>И зачем такое нужно???
I>Да может быть практически и не нужно, только чтоб ясность была.
Ну вот я и говорю: такие вещи только запутывают. Ясность — она в каждом конкретном приложении своя
Впрочем, никто не мешает пообсуждать это в десятый раз...
...утверждают, что парадигма наследования имеет хоть какую-то практическую пользу?! Гы-сына-лол.
Здравствуйте, igna, Вы писали:
I>Все-таки математика права, квадрат это прямоугольник
Раз уж на то пошло, отношение наследования, которое связывает квадрат и прямоугольник притянуто за уши и не имеет ни малейшего практического смысла. Правильно надо делать так: Квадрат — это Фигура, Прямоугольник — это Фигура, Треугольник — это Фигура. Ну и т.д. А то, что в геометрии квадрат является прямоугольником, то этот "гондурас" вообще не должен беспокоить, ибо этот факт является ортогональным по отношению к понятию наследования. Но для реализации такого соотношения как "Квадрат — это Фигура, Прямоугольник — это Фигура" наследование вообще не нужно. Это вообще не является наследованием. По сути это — имплементация интерфейса, или даже можно сказать, реализация контракта.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, McSeem2, Вы писали:
MS>...утверждают, что парадигма наследования имеет хоть какую-то практическую пользу?! Гы-сына-лол.
MS>Здравствуйте, igna, Вы писали:
I>>Все-таки математика права, квадрат это прямоугольник
MS>Раз уж на то пошло, отношение наследования, которое связывает квадрат и прямоугольник притянуто за уши и не имеет ни малейшего практического смысла. Правильно надо делать так: Квадрат — это Фигура, Прямоугольник — это Фигура, Треугольник — это Фигура. Ну и т.д. А то, что в геометрии квадрат является прямоугольником, то этот "гондурас" вообще не должен беспокоить, ибо этот факт является ортогональным по отношению к понятию наследования. Но для реализации такого соотношения как "Квадрат — это Фигура, Прямоугольник — это Фигура" наследование вообще не нужно. Это вообще не является наследованием. По сути это — имплементация интерфейса, или даже можно сказать, реализация контракта.
+1.
Вот надумалось:
Вариант 1:
interface IFigure
{}
interface IWidth
{
public int Width
{ get };
}
interface IHeight
{
public int Height
{ get };
}
interface IRectangle : IWidth, IHeight
{
}
class Rectangle : IRectangle
{
public int Width
{ get { return _width; } };
public int Height
{ get { return _height; } };
private int _width;
private int _height;
}
interface ISide
{
public int Side
{ get };
public int Angle
{ get };
}
interface IRhombus : ISide
{}
class Rhombus : IRhombus
{
public int Side
{ get { return _side; } };
public int Angle
{ get { return _angle; } };
int _side;
int _angle;
}
interface ISquare : IRectangle , IRhombus
class Square : ISquare
{
public int Width
{ get { return _width; } };
public int Height
{ get { return _height; } };
private int _width;
private int _height;
public int Side
{ get { return _side; } };
public int Angle
{ get { return _angle; } };
int _side;
int _angle;
// код для сохранения _width == _height == _side && _angle == 90 :maniac:
}
Что тут получили.
Квадрат он поддерживает интерфейс прямоугольника и ромба.
Однако появляется лишний код, который не связан с квадратом.
Решение через контракт мне нравится больше:
Вариант 2:
Таким образом квадрат мы получаем из прямоугольника и ромба при сопоставлении определенных условий и избавляемся от бесмысленного кода Rectangle, где есть 2 переменные на сторону. При чем также избавляемся от ненужного угла в ромбе.
С другой стороны нужно как-то красиво организовать поддержку контрактом иначе можно сходу не понять в чем проблема:
void f(ISquare) {...}
Rectangle r1 = new Rectange(1,1); // r1 : ISquare
Rectangle r2 = new Rectange(1,2); // r2 : IRectangle
f(r1); // OK
f(r2); // Ошибка ??
interface IRectangle
{
int Width {get;}
int Height {get;}
}
interface ISquare : IRectangle
{
int Size {get;}
}
class Rectangle : IRectangle
{
int _width;
int _height;
...
}
class Square : ISquare
{
int _size;
...
}
Предельно простой код, никаких нарушений LSP и никаких парадоксов.
... << RSDN@Home 1.2.0 alpha rev. 675 on Windows Vista 6.0.6000.0>>
Здравствуйте, igna, Вы писали:
I>А если захотелось добавить методы SetWidth и SetHeight, так это уже нечто за пределами математических понятий "квадрат" и "прямоугольник".
В математическом смысле вы правы — можно ввести такую бинарную операцию над элементами множества, что результат её выполнения не будет принадлежать исходному множеству. Это свойство имеет название, но я его забыл. Пример: операция деления натуральных чисел, результат не всегда является натуральным числом (1/2=0.5).
I>В математике объекты (не в смысле ООП объекты, нет, в обычном, человеческом смысле объекты), так вот, в математике объекты как правило неизменные, то есть умножая одну матрицу на другую получаем третью, а вовсе не модифицируем первую.
Это мысленные абстракции — очень дешёвый материал, можно транжирить.
I>В программировании такие объекты тоже встречаются, например String в Java или .NET; и если Rectangle и Square определить неизменными, они вполне могут быть связаны отношением наследования.
Это всего лишь один из способов представления отношения между абстракциями, выбор которого иногда осуществляется при помощи здравого смысла и с учётом практической необходимости.
Что нового?
Джентльмены не матерятся, они просто говорят: "Your bunny wrote!"
Здравствуйте, VoidEx, Вы писали:
VE>Все здесь неприлично. Наследование — это расширение понятия, когда все экзмепляры A являются также экзмеплярами B, но ни один экзмепляр B не является экземпляром A.
Предположим, что A наследует B, а x является экземпляром A. Поскольку "все экзмепляры A являются также экзмеплярами B", x является также экземпляром B. Поскольку "ни один экзмепляр B не является экземпляром A", то x не является экземпляром A. Получили противоречие, так как x не может одновременно являться экземпляром A и не являться экземпляром A. Следовательно, из предположения, что A наследует B, следует, что не существует ни одного экземпляра A. Хм...
Вышеприведенное рассуждение верно естественно только если "наследование — это расширение понятия, когда все экзмепляры A являются также экзмеплярами B, но ни один экзмепляр B не является экземпляром A".
Здравствуйте, igna, Вы писали:
I>Здравствуйте, VoidEx, Вы писали:
I>Предположим, что A наследует B, а x является экземпляром A. Поскольку "все экзмепляры A являются также экзмеплярами B", x является также экземпляром B. Поскольку "ни один экзмепляр B не является экземпляром A", то x не является экземпляром A. Получили противоречие, так как x не может одновременно являться экземпляром A и не являться экземпляром A. Следовательно, из предположения, что A наследует B, следует, что не существует ни одного экземпляра A. Хм...
Вы прекрасно поняли, о чем я говорил, придираться к формулировке можете перед зеркалом.
Здравствуйте, VoidEx, Вы писали:
VE>Здравствуйте, igna, Вы писали:
I>>Здравствуйте, VoidEx, Вы писали:
I>>Предположим, что A наследует B, а x является экземпляром A. Поскольку "все экзмепляры A являются также экзмеплярами B", x является также экземпляром B. Поскольку "ни один экзмепляр B не является экземпляром A", то x не является экземпляром A. Получили противоречие, так как x не может одновременно являться экземпляром A и не являться экземпляром A. Следовательно, из предположения, что A наследует B, следует, что не существует ни одного экземпляра A. Хм...
VE>Вы прекрасно поняли, о чем я говорил, придираться к формулировке можете перед зеркалом.
Вы сказали чушь откровенную. Настолько, что я так и не смог написать вам ответ с опровержением. Так что спасибо igna за то, что выразил мои мысли.
Здравствуйте, Владек, Вы писали:
В>В математическом смысле вы правы — можно ввести такую бинарную операцию над элементами множества, что результат её выполнения не будет принадлежать исходному множеству. Это свойство имеет название, но я его забыл. Пример: операция деления натуральных чисел, результат не всегда является натуральным числом (1/2=0.5).
В общем-то бинарная операция по определению замкнутая (т.е. является отображением M x M -> M). Другими словами, деление на множестве натуральных чисел не бинарная операция
Здравствуйте, VoidEx, Вы писали:
VE>Все здесь неприлично. Наследование — это расширение понятия, когда все экзмепляры A являются также экзмеплярами B, но ни один экзмепляр B не является экземпляром A. VE>Тут же квадрат — это частный случай прямоугольника, да он им является, но из всего множества возможных прямоугольников некоторые являются квадратами.
Можно ли сконструировать пример, демонстрирующий "неприличность" кода, который я привел в первом сообщении
Здравствуйте, igna, Вы писали:
MS>>...утверждают, что парадигма наследования имеет хоть какую-то практическую пользу?!
I>Ась? Какие люди "утверждают, что парадигма наследования имеет хоть какую-то практическую пользу"?
Здравствуйте, igna, Вы писали:
I>Здравствуйте, _nn_, Вы писали:
I>
__>>interface ISquare : IRectangle , IRhombus
I>
I>Вот эта возможность унаследовать ISquare от IRectangle обусловлена отсутствием в последнем методов вроде SetWidth и SetHeight.
I>Так что классы ли, интерфейсы ли, наследовать квадрат от прямоугольника можно только если оба неизменны (как в математике).
А чем так плохи неизменяемые объекты ?
Можно изменять введя дополнительные интерфейсы IChangeRectange, IChangeSquare