Pull to refresh

Comments 9

предпочитаю дедовские способы — если человек является камином — то может быть подклассом, иначе нет.

Напишите кстати еще про проблему наследования квардата от класса прямоугольник ( ну там квадрату не нужны две переменные, хватит и одной и тд ). И как решение — агрегация
>предпочитаю дедовские способы — если человек является камином — то может быть подклассом, иначе нет.
Предположим, что вместо названия класса «Человек» было бы другое: «Пластилиновый камин».
Всё остальное в программе оставим прежним. В этом случае Вы бы могли посчитать его подклассом класса «Камин».
Хотя «пластилиновые камины» не ведут себя поведенчески также, как и обычные камины(они плавятся при 45 градусах). Следовательно, по LSP, они не могут быть взаимозаменяемыми сущностями и не подходят под отношение тип-подтип.
Ну это смотря что нас интересует в камине. Если в задаче мы его греть не собираемся, то «Пластилиновый камин» вполне может быть подклассом «Камина».
Суть занятна. Только без поддержки на уровне языка — довольно нудно реализуется в больших проектах.
Согласен. Однако поддержка на С++ есть в виде сторонних библиотек. Достаточно погуглить по «С++ Design by Contract».
Например эта: sourceforge.net/projects/dbcpp/

Основная проблема — это то, что (насколько я понял) предусловия/постусловия и их ослабления/усиления не контролируются компилятором, следовательно программисты должны сами следить за контрактами, что неизбежно приведёт к их(контрактов) непониманию и несоблюдению. Хотя методология эта хороша уже тем, что заставляет задуматься о границах применимости класса, его контрактах с другими классами и т.д.
«не контролируются компилятором»
Просто потому, что это всего лишь одно из принимаемых соглашений. А в реальной жизни может быть как угодно ;-)
не только в компиляторе, но и в инструментальных средствах (прежде всего — IDE). Сложность добавления функционала в них — вот, ИМХО, главный ступор при внедрении разных полезных фич для разработки тех или иных проектов, потому что, как вы верно заметили, программер может следить за всем этим зоопарком (ведь таких плюшек, как контракты, вагон и тележка, да и в зависимости от проекта их состав классно бы менять), но не будет этим заниматься — и так голова пухнет.
Получается, что нельзя, к примеру, определить для человека и камина общий базовый класс «Штука с температурой», который позволяет просто хранить и менять температуру, т.к. у него совсем нет никакого предусловия на изменение температуры…

И ещё. Если попробовать сделать наоборот, «Камин» подтипом «Человека», то в принципе можно использовать камины вместо людей, т.к. предусловие у каминов слабее. Но не должен ли тогда камин соблюдать предусловия человека? Если он IS-A человек, то его нельзя нагревать выше 45.
>Получается, что нельзя, к примеру, определить для человека и камина общий базовый класс «Штука с температурой»...
Нельзя. Для базового класса «Штука с температурой» не определено поведение, на которое будут рассчитывать его пользователи. Можно было бы определить поведение(предусловие) класса «Штука с температурой» несколькими способами:
1) принимаем любую температуру
Но тогда, т.к. Firepace и Human задают более жесткие предусловия, они не могут быть его подтипами.

2) принимает температуру < 800 градусов
В этом случае только класс Fireplace мог бы быть подтипом, т.к. предусловия Fireplace не являются более жесткими, чем у «Штуки»

3) принимает температуру < 45 градусов
только Human может быть подтипом

>. Если попробовать сделать наоборот… не должен ли тогда камин соблюдать предусловия человека?
В моём исходнике я не снабдил контрактом функцию setTemperature. Если снабдить, то станет видно, что «наоборот» наследовать классы нельзя.
(см. комментарии внутри функции Fireplace::setTemperature)
#include <iostream>
#include <cassert>

class Human {
public:
Human() { setTemperature(36); }

// функция-гетер
virtual int getTemperature() const {
 return temperature;
}
// функция-сетер
virtual void setTemperature(int N) {
  // preconditions
  int old_temperature = getTemperature(); 
  // возможный диапазон значений температуры для человека
  assert(N >= 36 && N < 45);
 
  temperature = N; 

  // postconditions
  assert(getTemperature() == N);
  // гарантируем, что температура человека будет лежать в этих пределах
  assert(getTemperature() >= 36 && getTemperature() < 45);
}

virtual void heat(int N) {
 // preconditions
 int old_temperature = getTemperature(); 
 // N должно быть такое, чтобы человек не перегрелся
 assert(getTemperature() + N < 45);
 
 std::cout << "Выпил водки";
 setTemperature(getTemperature() + N);

 // postconditions
 assert(getTemperature() == old_temperature + N);
 // гарантируем, что температура человека находится в допустимых пределах
 assert(getTemperature() >= 36 && getTemperature() < 45);
}

protected:
int temperature;
};
//-----------------------------------------------------------------------------
class Fireplace : public Human {
public:
// средняя температура камина без дров (т.е. кирпича внутри него) 18 градусов
 Fireplace() { setTemperature(18); } 

 
 // функция-сетер
virtual void setTemperature(int N) {
  // preconditions
  int old_temperature = getTemperature(); 
  // возможный диапазон значений температуры для человека был такой:
  // assert(N >= 36 && N < 45); 
  //новый assert для камина примет вид:
  assert(N > 0 && N < 800);
 
  temperature = N;

  // postconditions
  assert(getTemperature() == N);
 
  // для человека было такое постусловие:
  // assert(getTemperature() >= 36 && getTemperature() < 45);
 
  // но для камина у нас должно быть другое:
  // assert(getTemperature() > 0 && getTemperature() < 800);
  // однако мы не имеем право его использовать, т.к.
  // getTemperature() > 0 - ОСЛАБЛЯЕТ постусловие базового класса
  // следовательно, или необходимо изменить постусловие базового класса
  // или, на данный момент, класс Fireplace не может быть подтипом Human
}

// подогреть на N градусов
virtual void heat(int N) {
 // preconditions
 int old_temperature = getTemperature();
 // кирпич в камине выдерживает максимум 800 градусов Цельсия
 assert(getTemperature() + N < 800);

 std::cout << "Подбросали дровишек";
 setTemperature(getTemperature() + N);

 // postconditions
 // новая температура должна быть равна старой + N
 assert(getTemperature() == old_temperature + N);
}
};

int main()
{
Human *h = new Fireplace;
h->heat(500);

return 0;
}


* This source code was highlighted with Source Code Highlighter.

Sign up to leave a comment.

Articles