Я не согласен с вашим примером. Хотя, конечно, у прямоугольника есть площадь и периметр, не имеет смысла хранить их отдельно от длины и ширины, когда вы можете просто вычислить их на лету в геттерах. Зачем заставлять людей вызывать эти функции «Set» для получения точных результатов, когда вы можете предоставить гораздо более приятный интерфейс?
Я бы сделал Shape
чистым интерфейсом:
struct Shape
{
virtual ~Shape(); // probably need this
virtual double getArea () = 0 ;
virtual double getPerimeter () = 0 ;
};
class Rectangle : public Shape
{
double length ;
double breadth ;
public:
Rectangle ():length(0.0),breadth(0.0){}
Rectangle (int _length, int _breadth):length(_length),breadth(_breadth){}
double getArea ()
{
return length * breadth ;
}
double getPerimeter ()
{
return 2 * (length + breadth) ;
}
};
Мне кажется, что любая форма имеет следующие атрибуты 'Площадь' и 'Периметр', и, следовательно, если мы не предоставим эти атрибуты в классе, класс Shape не будет подходящим представлением 'Форма реального мира'
Я тоже не согласен с этим. Класс Shape должен иметь открытый API , который обеспечивает доступ к его области и периметру. Имеет ли он поля, содержащие эти значения, является деталью реализации, и не имеет никакого отношения к тому, представляет ли он фигуру для внешнего мира.
Обеспечение внешнего мира красивым интерфейсом - это, скажем, 85% дизайна класса. Остальные 15% следят за тем, чтобы интерфейс, который подходит вызывающей стороне, был реально осуществим. Заставить детали внутренней реализации следовать тому же шаблону, что и внешний интерфейс, в той мере, в которой каждое свойство объекта, возвращаемого функцией, обязательно сохраняется в поле, а не просто с низким приоритетом, это бессмысленно.
Если бы у вас был круг, вы, возможно, могли бы иметь функции getRadius
и getDiameter
, но вы бы не хранили их в разных полях, несмотря на то, что, конечно, круги имеют радиус и также диаметр.
Теперь, если это был не прямоугольник, а какая-то очень сложная фигура, где вычисление площади и периметра идет медленно, то вы можете кэшировать область и периметр в объекте (и либо пересчитать их, либо отметить устаревшие значения кэша). всякий раз, когда определяющие параметры изменяются). Но это специфическое свойство сложных форм, не имеющее ничего общего с простыми формами, такими как Rectangles, и поэтому не относится к базовому классу Shape. Базовые классы должны содержать только те вещи, которые являются общими для всех производных классов или, по крайней мере, общими для достаточного количества из них, и вы готовы действовать так, как если бы они были общими для всех (и переопределять или игнорировать эти вещи в подклассах, к которым они не применяются. к). В вашем примере, способность сообщать площадь и периметр являются общими для всех фигур. Факт сохранения площади и периметра в элементах данных не обязательно должен быть общим для всех фигур, а в случае Rectangle занимает дополнительную память и требует дополнительного кода, что практически бесполезно.
вы никогда не должны иметь атрибутов в базовом классе
Я тоже не согласен с этим, хотя, думаю, я ценю, куда они могут пойти с этим.
Поля, которые может иметь базовый класс Shape
, включают координаты положения и ориентацию (если мы говорим о реальных прямоугольниках и т. Д., Которые должны отображаться на экране, а не прямоугольники в абстрактном виде), цвет, теги (если мы твердо привержены краудсорсинговым таксономиям и, вероятно, унаследованы от другого базового класса, а не определены в самой Shape), и тому подобное, что будет обрабатываться одинаково, независимо от того, какой производный класс задействован.
Тем не менее, наследование реализации используется чрезмерно (потому что это часто кажется обманчиво легким), но лично я думаю, что иногда это стоит. В случае фигур вы можете столкнуться с различными проблемами, пытаясь определить иерархию классов геометрических фигур.
Это не слишком плохо, если вы придерживаетесь неизменных объектов. Квадрат - это Прямоугольник (ширина и ширина которого равны), поэтому он может быть подклассом Прямоугольника. Но затем через месяц вы хотите ввести общие четырехугольники. Где вы должны добавить новый класс? Упс - в top иерархии классов, где это потенциально нарушает оба ваших существующих класса. Добавление четырехугольников заставило вас переосмыслить квадраты. При прочих равных вы хотели бы, чтобы такого рода широкомасштабные изменения были необязательными, а не навязывали их себе.
А как насчет изменчивых объектов? Изменяемый квадрат не является изменяемым прямоугольником, поскольку изменяемые прямоугольники должны обеспечивать возможность устанавливать ширину 2 и высоту 3. Квадраты не должны предоставлять эту способность. Изменяемый прямоугольник также не является изменяемым квадратом (поскольку изменяемые квадраты должны обладать свойством, что при изменении ширины высота изменяется. Изменяемые прямоугольники не должны иметь этого свойства). Таким образом, изменяемые прямоугольники, ширина и высота которых равны, совсем не то же самое, что изменяемые квадраты. Классы не могут быть связаны по наследству. Теперь Rectangle(2,2) == Square(2)
должно быть истинным или ложным? Что бы мы ни решили, мы кого-нибудь удивим. Таким образом, мы обнаруживаем, что должны оставить их как несвязанные, несравненные классы. То есть не используйте наследование.
Так что чистые интерфейсы хороши там, где это возможно, и некоторые люди решили, что такого рода проблемы настолько распространены, что другие виды наследования просто не стоят того. Язык программирования Go не имеет наследования . Кроме того, многие люди также решили избегать изменчивых объектов , и сделали это довольно хорошо, но это не вопрос ....
Я думаю, что есть много случаев, когда в качестве детали реализации удобно иметь общие функциональные возможности в базовых классах. Только не думайте, что это обычное поведение действительно присуще вашему базовому классу. Это реализация, а не интерфейс, и однажды вы можете определить производный класс, который не хочет использовать его, потому что какая-то другая реализация является более подходящей. Там, где вы можете удобно разместить общее поведение во встроенных объектах, а не в базовых классах, сделайте это.