Здравствуйте, last_hardcoder, Вы писали:
_>Это решение соответствует минимуму кода и максимуму памяти, занимаемой объектом. Оно не является идеальным для 100% случаев применения.
Конечно нет, но оно возможно и корректно. Вопреки распространенному мнению, что квадрат якобы вообще никогда нельзя производить от прямоугольника.
А почему рассматриваете только простейшие случаи наследования? Если мне не изменяет память (из школьного курса математики), то квадрат это правильный многогранник (терминологию могу забыть уже), у которого число граней равно 4. А прямоугольник это более общий случай многогранника. И иерархия наследования для квадрата будет "Фигура <- Многогранник <- правильный многогранник <- квадрат (хотя его можно не выделять)". Плюс кваррат является четырехугольником (то есть множественное наследование). А для прямоугольника — "Фигура <- Многогранник <- четырехугольник <- прямоугольник".
Это естественно только одна из возможных иерархий наследования. Можно сделать посложнее, можно попроще. А степень сложности будет определяться требованиями к поддержке фигур, в случае, если изначально нужно поддерживать только прямоугольники и квадраты, то наследовать квадрат от прямоугольника допустимо. Также степень сложности будет определяться методами классов, в случае, если от этих фигур требуется только рисоваться, то лучше вообще определить, что и то, и другое является фигурами (абстрактный класс) и не мучиться с наследованием. А если потребуется там площади, периметры, диагонали считать — то иерархию придется пересмотреть. Главное в иерархиях наследования, чтобы они упрощали все, а не усложняли плюс убирали дублирующийся код. XP рулит, заранее все усложнять не очень хорошо . Все равно заранее всего не предусмотреть.
Здравствуйте, igna, Вы писали:
I>Конечно нет, но оно возможно и корректно. Вопреки распространенному мнению, что квадрат якобы вообще никогда нельзя производить от прямоугольника.
Если отвлечся от конкретики фигур, то получается, что частные случаи нельзя выражать через общие? Ну это бред какой-то. И частные через общие можно выражать, и развивать частные до общих, и независимо реализовывать и то и другое. Вопрос лишь в ресурсах, которые тратятся в каждом случае.
Здравствуйте, Tilir, Вы писали:
T>Ох уж мне эти java-developer'ы... Ясное дело, Мейерса вы не читали. А между прочим, в книге "Эффективное использование C++" есть целый раздел, посвящённый тому, почему (в рамках философии C++) квадрат нельзя наследовать от прямоугольника. Коротко говоря, для него не выполняется LSP. Очень советую ознакомится. Если лень читать всю книжку, то подскажу: см. правило №35.
Там сказано, что нельзя публично наследовать. А приватно то что мешает?
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, Big_Urri, Вы писали:
B_U>>А еще, как квадрат, так и прямоугольник можно задавать длиной диагонали и углом между ними. В этом случае проблем с наследованием и изменяемостью не возникает, поведение фигур полностью идентично. Height и Width будут вычисляемые. Но есть еще ромб.
PD>Только не ромб! Не надо ромбов! Никаких ромбов не надо! Дискуссию с участием еще и ромба сервер не выдержит!
Предлагаю наследовать вместо ромба токосъёмник от электропоездов
PD>P.S. Как много у программистов свободного времени
Мда... только вот потом все почему-то жалуются на то, что заставляют работать сверхурочно
Tilir wrote:
> // private: const Rectangle GetCopyX(double n){}; // чтобы её не дай Б-же не вызвали
Ну вот и меня соблазнили в флейм ввязаться!
Почему не бы вызвать? Пусть вызывают, вернётся нормальный прямоугольник, полученный растягиванием стороны квадрата.
По моему главное при наследовании down-casting. Т.е. если мы имеем
Здравствуйте, last_hardcoder, Вы писали:
_>Если отвлечся от конкретики фигур, то получается, что частные случаи нельзя выражать через общие? Ну это бред какой-то. И частные через общие можно выражать, и развивать частные до общих, и независимо реализовывать и то и другое. Вопрос лишь в ресурсах, которые тратятся в каждом случае.
Ну открой тему с названием вроде "Можно без неприятностей открыто наследовать прямоугольник от квадрата" и одевай каску.
Здравствуйте, igna, Вы писали:
I>Но ведь это форум "Философия программирования", а не "C/C++". Я думаю, в этом форуме "наследование" означает "открытое наследование".
Бедные не "C/C++" программисты :'( Ну тогда всё правильно, Либо SetWidth должен отсутствовать (что существенно сужает область применения и крайне нежелательно, т.к может стать препядствием расширения системы в будущем, даже если на текущем этапе это не нужно). Либо наследовать нельзя и нужно создавать обёртку, прячущую SetWidth, отличным от наследования способом.
Она в данном случае не даст бабаха, поскольку виртуальная GetWidth (её там напрямую нет, но легко доопределить) для прямоугольника будет возвращать m_Width, а для квадрата m_Len.
Вообще по здравому размышлению, мне до сих пор очень нравится этот код — я так и не смог придумать ситуации, когда бы он привёл к откровенно плохим результатам. Не нужны даже const-модификаторы, а LSP выполняется, поскольку "Все стороны равны" у моего квадрата вырождается в "m_Len==m_Len", что выполняется и для прямоугольника. И побочных эффектов http://www.rsdn.ru/Forum/Message.aspx?mid=2396945&only=1
он не вызывает. И по памяти оптимален — зачем квадрату два закрытых члена для длины и ширины? Ему и одного хватит.
А что? Прямоугольник — это на самом деле квадрат, только у него стороны разные.
Tilir wrote:
> А что? Прямоугольник — это на самом деле квадрат, только у него стороны > разные.
Нет, представь себе, что у тебя есть функция, на вход берущая квадраты.
Она вполне имеет право ожидать, что квадраты квадратные и ничего не знать о существовании прямоугольников.
Так как у тебя от квадрата наследуется прямоугольник, ты можешь получить косвенно вызов "functionForSquaresOnly(new
Rectangle(3,4))"
И функция будет удивлена, обнаружив очень странные свойства у переданного ей квадарата, как то что вдруг периметр стал
каким-то неправильным, например не выполняется "square.getWidth()*4==square.getPerimeter()" и т.п.
Posted via RSDN NNTP Server 2.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Кажется, пример про квадрат и прямоугольник совершенно идиотский, однако это не так.
В том или ином виде эта задача регулярно всплывает.
Например:
"файл" — "бинарный файл" — "текстовый файл",
"файл" — "файл только для чтения"
"контрол" — "контрол-контейнер" — "табконтрол"
"коллекция" — "список" — "типизированный список"
"маршалер" — "конкретный маршалер"
и уже упоминаемые здесь "матрица" — "квадратная матрица".
Мое решение такое:
class Rectangle
{
public Rectangle(int width, int height)
{
this.width = width;
this.height = height;
}
public Rectangle(int squareSide)
: this(squareSide, squareSide)
{
}
public bool IsSquare
{
get { return this.width == this.height; }
}
private int width;
public int Width
{
get { return this.width; }
set { this.width = value; }
}
private int height;
public int Height
{
get { return this.height; }
set { this.height = value; }
}
}
Оно нехорошо только тем, что нельзя задать определение функции, которая принимает квадрат.
Вообще это тоже серьезный недочет, однако мы как-то живем с функциями, которые принимают int, но он должен быть > 0, и с функциями, которые принимают файл, который должен быть доступен на запись. И понятно почему!
Открытое наследование, как наследование и интерфейса, и реализации, практически всегда используется на практике как средство уточнения типа. конкретизации. А реализация интерфейса — как обобщение. Щас я имею в виду C#. Т.е. если я вижу, что человек отнаследовался от Control, я понимаю — он хочет конкретизировать Control, заюзать функциональность базового класса. И я не должен ждать от него соблюдения LSP. Если же я вижу реализацию интерфейса IComponent, я понимаю — человек хочет встроить свой класс в инфраструктуру компонентов и жду от него соблюдения LSP. Потому как если он не соблюдет, уже готовый код инфраструктуры завалится с исключением и класс этот никуда не встроится. Вот почему на практике программист обычно понимает что к чему.
IMHO, объектные системы должны быть написаны именно так. Т.е. чтобы а) не плодить интерфейсы ISquare, которые все равно бесполезны при изменяемых объектах, б) не плодить ISquare, когда вариантов "фигур" очень много (например, они задаются пересечением 3х енумов по 10 записей в каждом) и в) позволять как-то узнать о том, можно ли вызывать ThisFunction(Rectangle), не обрамляя это в try/catch, нужны свойства по типу IsRectangle. Когда-то давно в таком же обсуждении я заморочился и написал большой пример
Poudy,
P>IMHO, объектные системы должны быть написаны именно так. Т.е. чтобы а) не плодить интерфейсы ISquare, которые все равно бесполезны при изменяемых объектах, б) не плодить ISquare, когда вариантов "фигур" очень много (например, они задаются пересечением 3х енумов по 10 записей в каждом) и в) позволять как-то узнать о том, можно ли вызывать ThisFunction(Rectangle), не обрамляя это в try/catch, нужны свойства по типу IsRectangle. Когда-то давно в таком же обсуждении я заморочился и написал большой пример
Здравствуйте, Poudy, Вы писали:
P>Кажется, пример про квадрат и прямоугольник совершенно идиотский, однако это не так. P>В том или ином виде эта задача регулярно всплывает. P>Например: P> "файл" — "бинарный файл" — "текстовый файл",
Тут мне кажется, всё нормально. Где закавыка?
P> "файл" — "файл только для чтения"
Здравствуйте, Lazy Cjow Rhrr, Вы писали:
LCR>Poudy,
P>>IMHO, объектные системы должны быть написаны именно так. Т.е. чтобы а) не плодить интерфейсы ISquare, которые все равно бесполезны при изменяемых объектах, б) не плодить ISquare, когда вариантов "фигур" очень много (например, они задаются пересечением 3х енумов по 10 записей в каждом) и в) позволять как-то узнать о том, можно ли вызывать ThisFunction(Rectangle), не обрамляя это в try/catch, нужны свойства по типу IsRectangle. Когда-то давно в таком же обсуждении я заморочился и написал большой пример
Ну я этот же пост и привел. Собственно, взглядам совсем не обязательно меняться за два года.
Кстати, если уж о взглядах ... за 2 года вот что поменялось:
1. LazyLoading — теперь сичтаю, что это вредная идея вообще;
2. Сущности — раньше думал, что должны быть "бесшовными" в плане общения с сервером, чтобы все было автоматом и незаметно. Сейчас думаю, что сервер и клиент должны всегда рассматриваться как совершенно разные приложения (ну примерно как сервер — ERP, а клиент — софт на КПК) и, соответственно, у сервера и клиента должны быть свои уникальные версии модели.
3. Про средства разработки в ERP — теперь считаю, все должно работать в VisualStudio.
Здравствуйте, FDSC, Вы писали:
FDS>Здравствуйте, Poudy, Вы писали:
P>>Кажется, пример про квадрат и прямоугольник совершенно идиотский, однако это не так. P>>В том или ином виде эта задача регулярно всплывает. P>>Например: P>> "файл" — "бинарный файл" — "текстовый файл",
FDS>Тут мне кажется, всё нормально. Где закавыка?
Закавыка в дереве наследования. Как наследовать?
Вообще говоря, текстовый поток UTF-8 или UTF-16 "испортится", если из него взять и прочитать байт как из бинарного. По-другому должны работать откат на одну позицию и вообще seek. Какой-нибудь светлой голове может прийти в голову наследовать текстовый от бинарного (или наоборот . Я думаю в данном примере текстовый файл — это ваще отдельно. Т.е. не должно быть понятия "текстовый файл". Должно быть так: "бинарный файл" — "текстовый ридер".
Здравствуйте, igna, Вы писали:
I>Все-таки математика права, квадрат это прямоугольник,
Кстати, насчёт математики тоже не всё так просто, есть два подхода.
В обычной, хорошо нам знакомой, статической геометрии можно считать, что квадрат — это прямоугольник с равными сторонами.
Но есть ещё такой раздел математики как топология. Так вот с точки зрения топологии, наоборот, прямоугольник — это растянутый(или сжатый) квадрат. То есть, первичен именно квадрат.