Обновить
154
Павел Остапенко@mt_

Пользователь

2
Подписчики
Отправить сообщение
Посмотрите на мой класс Silo. Сам по себе он прост как гвоздь, это при том, что, он работает в сетях связи, управляет неким автоматическим объектом реального мира, выводит свои настройки в два интерфейса, синхронизирует себя двумя способами по сети, работает как веб-сервер и веб-клиент в зависимости от настроек, и прочее.
Если потенциально возможна смена ГУИ — значит надо так строить абстракции, чтобы код не «ломался». Это называется планирование функционала перед проектированием. В данном случае, требуются абстракции для работы с ГУИ. Это ни в коем случае не противоречит тому, о чём я говорил в статье.
Получается сложный класс? Делим функциональность, в класс добавляем объекты-помощники, либо (более осторожно) используем классы подмешивания.
Вот и вся мысль.
Не понимаю, где вы увидели говнокод, где конкретно в данном случае вы разглядели потенциал к полному перелопачиванию чего-либо вообще.
Наоборот, всё что я предлагаю в статье, сводится к изоляции классов, делению функционала до уровня «простых гвоздей».
Если речь идёт о синтетическом тестировании операторов взаимодействия между этими классами, то ничто не мешает сделать тесты функциями-членами (в т.ч. статическими), точно также, как и сами операторы вычитания и т.д. Либо — да, вспомогательными, отдельными дружественными функциями (что, по сути, другой вид записи закрытой статической функции-члена).

Если речь идёт о каком-то конкретном применении объектов этих классов в рамках другого класса, то тест вполне можно сделать внутри этого класса.

На мой взгляд, спор перешёл в чисто эстетическую плоскость. Предлагаемый подход, думаю, вам понятен. Применять или не применять — решать вам.
Будут конкретные вопросы по практическому применению подхода — пишите, готов обсуждать.
Не знаю таких классов, поэтому ничего не могу сказать об их взаимоотношениях в рамках используемой вами иерархии.
Первое. Данный код не имеет никакого отношения к геймдеву. Так что тут немного промахнулись: здесь другие абстракции. Никто не предлагает объекту сцены работать напрямую с джойстиком. Везде своя специфика, свои абстракции.

Второе. Что касается этого кода, то нет вообще ничего ужасного в привязке к конкретному контролу. При желании я этот контрол могу поменять на другой. При этом не изменится ни один внешний интерфейс. Понимаете?
На всякий случай, вот аргументы, почему делаю именно так:
habrahabr.ru/blogs/cpp/111120/#comment_3543213
habrahabr.ru/blogs/cpp/111120/#comment_3543799
Вы точно читали статью?
Каким именно боком, по-вашему, и откуда вылезет простота?
Взаимодействием объектов заведует другой класс. Класс более выского уровня в иерархии. В него можно точно также включать код для проверок взаимодействия дочерних объектов.
Или я что-то упускаю?
*и минимизирует число изменений…
Виртуалки приведены лишь «для красоты». Основная идея — не в них, а в инкапсуляции внутренней кухни.

Что касается смешивания в одну кучу. Я предлагаю такую связку:
класс {внутренние данные; проверка;}
только потому, что это короче, читабельнее, и не минимизирует число изменений по сравнению с этим:
класс {внешние данные;} + класс_проверки {указатель_на_объект_класса; вызов_внешних_данных_класса; проверка; }

При очень большом желании «не смешивать в одну кучу», определение функций-членов для тестирования класса можно вынести в отдельный файл. Это, на мой взгляд, всё равно лучше связки №2.
Почему бы тогда классу не заняться самотестированием? В конечном итоге, мы всё равно три раза перепишем функционал. Но по ходу, зачастую, не будем переписывать интерфейс обмена между классом и его «проверщиком» по ходу изменений внутри класса. По вашему примеру это будет примерно так:
class SelfTested
{
protected:
    virtual void SelfTest() = 0;
};
class DeepThought : SelfTested
{
protected:
    virtual void SelfTest()
    {
        int answer = Calculate();
        assertEquals(42, answer);
    }
private:
    int Calculate();
};


В этом случае, если даже логика изменится и внутри я Calculate поменяю на AskAnotherSupercomputer, либо буду ожидать строковый ответ, вовне объекта ничего не поменяется.
К сожалению, в данном случае конкретная реализация будет сильно заслонять собой идею. Выложил для примера код Бункера, возможно, это будет хотя бы немного полезнее общих слов из статьи:
habrahabr.ru/blogs/cpp/111120/#comment_3548011
Не проблема — скопировать код сюда. Вопрос в том, как просеять все эти килобайты, чтобы было понятно. Для примера, пожалуйста:
class Silo : public UserLevel::Notify, public SiloParamsSourceNotify
{
public:
	Silo(SiloListNotify *notify, int role, const int &serverAddr);
	~Silo();
	const Silo &operator= (const Silo &);
	
	void InitRemoteBehaviour();

	//settings window
	void UpdateUI(ArrayCtrl *params, ArrayCtrl *sensorParams, DropList *sensorType);

	double GetPercentage();

	//main window
	EdgedButtonOption * UpdateButton(int x, int y);
	EdgedButtonOption * GetButton();
	ButtonOption      * UpdateSiloDetails(Label *); //возвращает указатель на кнопку "Измерить" для добавления в окно

	bool IsError  ();
	bool IsWarning();
	
	bool UpdateStatus();

	void InitShutdown();
	void WaitFinish();

	virtual void OnUserLevel (int level, const String &user);
	void Xmlize(XmlIO &xml);

	virtual void OnSensorTypeChanged();

private:
	String GetPercentageString();
	String GetVolumeString();
	String GetHeightString();
	String GetMassString();

	One<SiloCore>   core; friend class SiloGroup;
	One<SiloParams> params;

	void ShutdownRemoteBehaviour();

	void ManualSensorMeasure();
	void UpdateNextMeasureTime();
	
	SiloListNotify   *notify;
	
	EdgedButtonOption button;
	ButtonOption      manual;
	
	SiloSensor       *sensor;
	
	bool              shutdown;
	
	void GetDHPic(double h, double *dp, double *hp);
	void GetDHPicBig(double h, double *dp, double *hp);

	int    userLevel;
	String user;
	bool   userLevelChanged;
};
Человек вроде чётко написал: «если у вас есть ошибки в проектировании, то ТДД ничем не поможет».
Конечно не против, давайте попробуем всё сказанное на практике — к этому я и призываю.
Готов обсудить на реальных примерах из вашей практики.
Спасибо за критику. Согласен с тем, что слишком упрощённо описал перенос объектов реального мира в С++. Чуть дополнил соответствующее место в статье. К сожалению, их взаимоотношения настолько разнообразны и разноплановы, что я бы не рискнул сказать что даже ваша статья описывает их достаточно полно.
Что касается езды по одному маршруту — не претендую на Истину, Светоча и т.п. ) Учусь также, как и вы, до сих пор. С моей точки зрения, мне удалось более или менее свести наработки в некую законченную систему, пусть и сумбурно изложенную. В этом смысле я и вижу свою заслугу, а ни в коем случае не в особых, эксклюзивных знаниях.
С другой стороны, для начинающих требуется сформулировать более или менее законченное и простое правило. Что я и пытаюсь сделать. Если сможете сформулировать более простое и точное — напишите, возьму его на вооружение.
Спасибо за поддержку!
Буду рад любым отзывам попробовавших на практике. Предлагаю честно обсуждать все спорные вопросы, которые возникают при практическом их применении.
С моей точки зрения, хотя я возможно и не прав, для новичка оба подхода одинаково сложны. А вот для тех, кто привык к стандартному подходу, переключиться на другую концепцию непросто.
В этом смысле, третья часть, в которой я планирую затронуть многопоточность, будет такой же неуютно-непривычной.

Всё что я предлагаю — постараться, попробовать самим и оценить на практике, какой из подходов лучше. Конечно, непросто привыкнуть. Но если подход оправдан, то как знать — возможно он стоит затраченных усилий.
Не понимаю, в каком месте ваших мыслей идёт выброс исключения.)
Я предлагаю некий подход. Ссылаюсь на свой личный опыт, а также на автора книги, которого очень уважаю.
Если для вас достаточно мнение неких других уважаемых личностей, и вы, не попробовав лично, считаете подход нерабочим — просто не пользуйтесь им.

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

Информация

В рейтинге
Не участвует
Откуда
Москва и Московская обл., Россия
Зарегистрирован
Активность

Специализация

Технический директор
Оптимизация бизнес-процессов
Управление разработкой
Наставничество
Fullstack
Agile