TDD и вообще юнит-тесты in the wild

87 views
Skip to first unread message

Юрий Мироненко

unread,
Nov 26, 2014, 8:29:43 AM11/26/14
to su...@googlegroups.com
Я уже поднимал этот вопрос раньше, но ясность у меня так и не появилась.
Вопрос вот какой: как использовать юнит-тестирование, и тем более TDD - в реальной жизни, а не на абстрактных примерах?

Я умею работать с SUnit, более того - я весьма успешно использовал даже и TDD. Но только в очень специальных случаях. Например, мне нужно было написать калькулятор судебной пошлины с множеством довольно дурацких правил и со странным округлением. Там так и просился TDD - и очень помог. Nile, система для "безопасной арифметики", очень хорошо и удобно покрывается тестами.

Но такой работы, может, 5%-10% от общего вала.
Как применить TDD или хотя бы вообще тесты к остальным 90% ?

Чтобы быть более конкретным - возьмём тот самый Tabular.
Движок для импорта-экспорта таблиц из/в распространённые форматы.
Как его разрабатывать с помощью TDD?

Этот движок состоит, очевидно, из внутренней модели представления табличных данных.
Эту модель, конечно, легко покрыть тестами и легко разрабатывать с помощью TDD.
Понятно даже, что применение TDD поможет выделить и держать работоспособными используемые интерфейсы.

Но понятно также, что основная польза, основной полезный код - это именно импортеры-экспортеры.
Как применить TDD к ним?
Я не знаю.

Конечно, под TDD замечательно легла задача "пересчитать номер колонки в её буквенный код". A, B, C, ..., Z, AA, AB, .... и так далее.
Но дальше?

Vladimir Musulainen

unread,
Nov 26, 2014, 8:48:17 AM11/26/14
to su...@googlegroups.com
Я до начала имплементации пишу тест, в котором я описываю свои ожидания, что-то типа

CSVExporter export: data to: writeStream.
self assert: writeStream contents = ‘value1;value2;value3<n>value4;value5;value6<n>’ expandMacros.

А потом добиваюсь, чтобы export:to: отработал как надо.


26 нояб. 2014 г., в 16:29, Юрий Мироненко <assar...@gmail.com> написал(а):

--
--
http://groups.google.ru/group/sugr
---
Вы получили это сообщение, поскольку подписаны на группу "Russian Smalltalk User Group".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес sugr+uns...@googlegroups.com.
Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.

Юрий Мироненко

unread,
Nov 26, 2014, 9:13:11 AM11/26/14
to su...@googlegroups.com
Ну вот смотри: сейчас там реально генерируется XLSX.
Который представляет из себя набор зазипованных XML'ек.

XMLWriter смысла тестировать нет - он и так покрыт тестами очень хорошо.
ZIPArchiver тоже. Они свою работу сделают.

А "свои ожидания" описываются словами "должно нормально открыться в Excel".
При этом соответствие текста XMLек образцу "с точностью до символа" не требуется.
Если у тега будут идти те же параметры, но в другом порядке - это нормально и приемлемо.

И что делать?

26 ноября 2014 г., 16:48 пользователь Vladimir Musulainen <vmusu...@mail.ru> написал:

Alexander Kogan

unread,
Nov 26, 2014, 2:28:49 PM11/26/14
to su...@googlegroups.com
Ну надо полагать, что некие известные тебе стандартные входные данные
будут всегда производить стандарную форму XML на выходе. Вот это и
тестируется. Создаешь ХМL содержащий информацию априори тебе
известную. Архивируешь. Потом в обратно порядке: разархивируешь,
читаешь XML Reader дату ожидая увидеть правильную последовательность,
которая насколько тебе это известно должна отрываться в Excel.

Формат исходных данных надо разнообразить в тех пределах, какие ты
можешь ожидать в реальной работе.

Dennis Schetinin

unread,
Nov 27, 2014, 12:38:16 AM11/27/14
to Russian Smalltalk User Group
Еще вчера начал писать длинное письмо и даже с какими-то первыми шагами, но получилось очень долго и занудливо. Поэтому постараюсь быть краток. (Перечитав то, что получилось, добавлю: насколько смогу:)

Первое, и самое важное: "тестировать"/"покрывать тестами" и TDD — совершенно разные вещи. Соответственно, надо понять, что мы хотим: тестировать или разрабатывать. 

За первый вариант мне сказать нечего. Если же мы хотим разрабатывать (именно TDD), то надо решить, с какого момента мы хотим TDD. "Классический" TDD как его описал Кент Бек предполагает некую стадию предварительного проектирования вне рамок TDD. Это примерно то, что предложил Владимир: мы уже где-то как-то придумали какие должны быть объекты, какая к ним приписана функциональность и используем TDD чтобы эту функциональность уточнить и зафиксировать. Не могу сказать, что это не работает — очень даже хорошо работает, но именно в тех рамках, в которых применяется. То есть, если впоследствии оказывается, что предварительный дизайн был не слишком удачным, то придется переделывать и переделывать все — не только основной код, но и тесты. У меня в реальной жизни часто так и получается… слишком часто. Поэтому я стараюсь придерживаться "чистого" TDD, где стадия проектирования не выносится "за скобки". Для меня это работает гораздо лучше — именно в плане дизайна. Но и дизайн получается несколько… специфический… В общем, для тех, кто привык руководствоваться априорными правилами хорошего дизайна, результат выглядит "не очень" (обычно речь идет об избыточной сложности)… Но работает! И сложность оказывается либо не настолько уж избыточной, либо (даже чаще) очень уместной. В общем, если есть интерес, можем попробовать данную задачу с этой точки зрения решить. Мне самому интересно, получится что-то вменяемое или нет. Но это будет не быстро, учитывая необходимость объясняться и наличие свободного времени.

И по поводу ожиданий… Здесь, похоже, как раз сказывается отношение к TDD как к способу тестирования. Да, Excel сможет "скушать" разные варианты файлов, но ваш конвертер для конкретного входа будет выдавать какой-то один вариант (если вы не хотите использовать генератор случайных чисел). И вам нужно не описать все правильные варианты, а просто реализовать какой-то один из них. Поэтому для начала надо взять какой-то один случай ("отщипнуть" от задачи маленький кусочек), специфицировать (вплоть до символа)— именно в первую очередь! — что ваш конвертер должен выдать, записать это в виде теста и реализовать. На всякий случай уточню: это если "по классике" — с середины начинать :)


--

Best regards,


Dennis Schetinin


26 ноября 2014 г., 18:13 пользователь Юрий Мироненко <assar...@gmail.com> написал:

Юрий Мироненко

unread,
Nov 28, 2014, 3:54:18 AM11/28/14
to su...@googlegroups.com
Судя по описанию, в любом случае, это будут приёмочные, а не юнит-тесты.
Но идея об использовании "XML-читалки" в любом случае полезная, спасибо.

26 ноября 2014 г., 22:28 пользователь Alexander Kogan <abk...@gmail.com> написал:
Настройки подписки и доставки писем: https://groups.google.com/d/optout.

Dennis Schetinin

unread,
Nov 28, 2014, 4:16:18 AM11/28/14
to Russian Smalltalk User Group
Это будут именно юнит-тесты.


--

Best regards,


Dennis Schetinin


28 ноября 2014 г., 12:54 пользователь Юрий Мироненко <assar...@gmail.com> написал:

Юрий Мироненко

unread,
Nov 28, 2014, 4:26:48 AM11/28/14
to su...@googlegroups.com
Денис, это был ответ на письмо Когана.
Ответ тебе ещё надо сочинить :)

28 ноября 2014 г., 12:15 пользователь Dennis Schetinin <cha...@gmail.com> написал:

Dennis Schetinin

unread,
Nov 28, 2014, 5:24:31 AM11/28/14
to Russian Smalltalk User Group
:) Пардон


--

Best regards,


Dennis Schetinin


28 ноября 2014 г., 13:26 пользователь Юрий Мироненко <assar...@gmail.com> написал:

Nikolay Kleptsov

unread,
Nov 28, 2014, 6:03:52 AM11/28/14
to Russian Smalltalk User Group
У TDD есть один "довесок" напрямую не относящийся к нему. TDD содержит тесты, в которых используются готовые решения (примеры). Например, осваивал MongoTalk именно по тестам.

28 ноября 2014 г., 16:23 пользователь Dennis Schetinin <cha...@gmail.com> написал:

Юрий Мироненко

unread,
Nov 28, 2014, 6:33:47 AM11/28/14
to su...@googlegroups.com
Это да, тесты - весомый довесок к "самокомментирующемуся коду".

28 ноября 2014 г., 14:03 пользователь Nikolay Kleptsov <kleptsov...@gmail.com> написал:

Юрий Мироненко

unread,
Nov 30, 2014, 5:32:58 AM11/30/14
to su...@googlegroups.com
Еще вчера начал писать длинное письмо и даже с какими-то первыми шагами, но получилось очень долго и занудливо.

Вообще, я всегда не прочь прочитать нечто длинное и якобы занудливое, если там подробно раскрывается тема. Я люблю читать, разбираться и разгребать. Долой клиповое сознание! :)

Первое, и самое важное: "тестировать"/"покрывать тестами" и TDD — совершенно разные вещи.
 
Поправь меня, если я ошибаюсь, но ведь в итоге мы приходим к коду, покрытому тестами, в котором тесты играют ту же роль, что юнит-тесты при обычном подходе? То есть, помогают контролировать целостность реализации при последующих модификациях.

Или такие тесты потом нужно ещё писать отдельно?

 
Соответственно, надо понять, что мы хотим: тестировать или разрабатывать. 

Лично я для начала хочу понять, как _вообще_ можно писать тесты к основной массе встречающихся на практике задач, да ещё так, чтобы от этого была какая-то польза. Пока я этого не понимаю.

Не понимая этого я, конечно, не могу понять и TDD. То есть я его понимаю в той части, где тесты хорошо пишутся - и в таком разе я могу воспользоваться как TDD, так и написать тесты к уже готовому коду. Но для основной массы кода это просто не работает.


Поэтому для начала надо взять какой-то один случай ("отщипнуть" от задачи маленький кусочек), специфицировать (вплоть до символа)— именно в первую очередь! — что ваш конвертер должен выдать, записать это в виде теста и реализовать.

Ну смотри. Допустим, я "специфицировал вплоть до символа", что параметры какого-то тега должны идти в таком-то порядке. А XML-фреймворк решил, что он выдаст мне параметры тега в алфавитном порядке. Меня это устроит, а написанные тесты - будут ругаться.


В общем, если есть интерес, можем попробовать данную задачу с этой точки зрения решить. Мне самому интересно, получится что-то вменяемое или нет. Но это будет не быстро, учитывая необходимость объясняться и наличие свободного времени.

Интерес - и крайне сильный - есть. Недаром же я периодически поднимаю эту тему.

Предлагаю сделать так:

Я начал делать XLSX-экспортёр. За ODS-экспортёр я ещё даже не брался.

Поскольку эти задачи очень и очень похожи друг на друга, имеет смысл XLSX-экспортёр продолжать делать "по старинке", а ODS-экспортёр начать делать с помощью TDD. Тогда будет наглядно видна разница между подходами.

Если такой план тебе нравится - с чего начать?

27 ноября 2014 г., 8:37 пользователь Dennis Schetinin <cha...@gmail.com> написал:

Dennis Schetinin

unread,
Dec 1, 2014, 1:04:34 AM12/1/14
to Russian Smalltalk User Group

>> Первое, и самое важное: "тестировать"/"покрывать тестами" и TDD — совершенно разные вещи.
Поправь меня, если я ошибаюсь, но ведь в итоге мы приходим к коду, покрытому тестами, в котором 
> тесты играют ту же роль, что юнит-тесты при обычном подходе? То есть, помогают контролировать 
> целостность реализации при последующих модификациях.
> Или такие тесты потом нужно ещё писать отдельно?

Тесты, которые будут созданы в процессе TDD, разумеется, можно использовать и для тестирования. Но в процессе разработки мы пишем такие тесты, таким образом и ровно столько, чтобы разработать нужную функциональность. "Покрытие" тестами с целью всесторонней проверки ПО — совсем другая задача. Соответственно, там и тесты с методами их создания чуть-чуть другие, и "чем больше, тем лучше"… Впрочем, я не специалист в области тестрирования.

>> Соответственно, надо понять, что мы хотим: тестировать или разрабатывать. 
Лично я для начала хочу понять, как _вообще_ можно писать тесты к основной массе встречающихся 
> на практике задач, да ещё так, чтобы от этого была какая-то польза. Пока я этого не понимаю.

Тогда, возможно, нет смысла лезть в дебри всяческих моков, подходов "сверху-вниз" и т.п. — достаточно ограничиться классическим вариантом. Книжка Бека про TDD прочитана? Там примеры вполне из жизни…

> Ну смотри. Допустим, я "специфицировал вплоть до символа", что параметры какого-то тега должны идти 
> в таком-то порядке. А XML-фреймворк решил, что он выдаст мне параметры тега в алфавитном порядке. 
> Меня это устроит, анаписанные тесты - будут ругаться.

Ну, измени тест. Если поумничать, то можно сказать так: TDD — это способ разработки через исследование. С целью создания нужного софта исследуются требования, понимание правильности, имеющийся код и т.д. В данном случае речь как раз о требованиях и правильности. 

Я начал делать XLSX-экспортёр. За ODS-экспортёр я ещё даже не брался.
> Поскольку эти задачи очень и очень похожи друг на друга, имеет смысл XLSX-экспортёр продолжать делать
> "по старинке", а ODS-экспортёр начать делать с помощью TDD. Тогда будет наглядно видна разница между
> подходами.
> Если такой план тебе нравится - с чего начать?

Раз, как я понимаю, "архитектура" уже выработана на случае XLSX, то этот вопрос не поднимаем. В этом случае я бы записал в виде теста самый простой случай: пустой файл. Я бы сгенерировал этот самый пустой файл, чтобы достать строку, которую он содержит. Она, возможно, будет не очень короткая, так что я бы ее в отдельный метод вытащил (expectedEmptyFileString). Еще предполагаю, что экспортер работает с каким-то потоком, соответственно проверка условия идет через буфер. Тогда получится так (распишу прямо по шагам — так логику легче отследить):

1)
OdsExportTests >> testEmpty

2)
OdsExportTests >> testEmpty

    actualString should equal: self expectedEmptyFileString  

3)
OdsExportTests >> testEmpty
    actualString := String new.
    
    actualString should equal: self expectedEmptyFileString  

4)
OdsExportTests >> testEmpty
    actualString := String new.
    exporter := OsdExporter newOn: (WriteStream on: actualString).
    exporter export.
    actualString should equal: self expectedEmptyFileString

Примерно так выходит…


--

Best regards,


Dennis Schetinin


30 ноября 2014 г., 14:32 пользователь Юрий Мироненко <assar...@gmail.com> написал:

Юрий Мироненко

unread,
Dec 1, 2014, 4:18:19 PM12/1/14
to su...@googlegroups.com
Книжка Бека про TDD прочитана? Там примеры вполне из жизни…

Нет, книжка не прочитана. Раньше я эту книжку не видел и о ней не знал. Скачал, прочитаю.

По результатам беглого просмотра: как сделать на TDD то, что там приводится в качестве примера (там единственный пример - или имелась в виду не та книжка? Я говорю про "Tesd-driven development by example" - и там в качестве примера приводится то ли мультивалютный кошелек, то ли что-то в таком роде), я знаю и сам. И даже имею соответствующий и вполне успешный опыт.

Но это, как я и жаловался выше, от силы 10% в обычном объёме работы.

Типичная задача, например: "хочу, чтобы в справочнике клиентов было место рождения, и чтоб оно отражалось в печатных формах А и Б". Вот как такое затестить - я не знаю.

Точнее не так: я знаю, как теоретически можно было бы написать соответствующие тесты. Однако это будет что-то абсолютно, совершенно уродское и, по всей видимости, бесполезное. Код будет существенно сложнее реализации, и будет содержать ту же логику, что и сама реализация, только в вывернутом виде. Если верить http://habrahabr.ru/post/112851/ - это серьёзные ошибки.

Это, не знаю, как тестировать аксессоры. Никто не тестирует аксессоры.

Вот здесь у человека те же проблемы, что и у меня: http://habrahabr.ru/post/113487/#comment_3646619
Что интересно, ответы, которые давали ему на хабре, очень похожи на те, которые я получаю здесь.
Некоторые прямо текстуально совпадают.

P.S. Про "рекомендации о первых шагах" я напишу отдельно.

1 декабря 2014 г., 9:03 пользователь Dennis Schetinin <cha...@gmail.com> написал:

Юрий Мироненко

unread,
Dec 1, 2014, 4:32:14 PM12/1/14
to su...@googlegroups.com
Раз, как я понимаю, "архитектура" уже выработана на случае XLSX, то этот вопрос не поднимаем.

Не очень-то она выработана, там proof-of-concept. Показывает возможность и основные приёмы работы, архитектурой там и не пахнет. Большая часть файлов вообще как строчки захардкожены.

В этом случае я бы записал в виде теста самый простой случай: пустой файл.

Не вполне понятно, что такое этот "пустой файл".
Это workbook без worsheet'ов?
Или это workbook с одним пустым worsheet'ом?
Или это то, что создаётся в офисном пакете, когда нажимаешь "Файл-Новый"? Обычно содержит три пустых worksheet'а.
Или неважно, и я могу выбрать любой?

Я бы сгенерировал этот самый пустой файл, чтобы достать строку, которую он содержит. Она, возможно, будет не очень короткая, так что я бы ее в отдельный метод вытащил (expectedEmptyFileString).

О какой именно "строке" речь? Не совсем понял.

Рискну предположить, что речь идёт о XML-файле, и что ты считаешь, что в ODS-файле хранится зазипованный XML. Это не совсем так: в ODS несколько зазипованных XML-файлов, причём организованных в директории.

Причём структура этих директорий и названия файлов не заданы жестко. Есть один (кажется) файл,  в котором указаны имена и адреса остальных файлов, и что какой из остальных файлов значит. Но если я считаю этот файл, начну его парсить, а распарсив- скачаю другие файлы. То тогда я буду писать скорее импортер, чем тесты к экспортеру.

Разъяснишь поподробнее?

1 декабря 2014 г., 9:03 пользователь Dennis Schetinin <cha...@gmail.com> написал:

Vladimir Musulainen

unread,
Dec 1, 2014, 6:55:10 PM12/1/14
to su...@googlegroups.com
Причём структура этих директорий и названия файлов не заданы жестко. Есть один (кажется) файл,  в котором указаны имена и адреса остальных файлов, и что какой из остальных файлов значит. Но если я считаю этот файл, начну его парсить, а распарсив- скачаю другие файлы. То тогда я буду писать скорее импортер, чем тесты к экспортеру.


Здесь для самого себя надо отделить что "мы тестируем быстро и модульно” и что "мы тестируем долго и полностью”.

Я бы для быстрых тестов не стал бы создавать workbook с worksheet-ами, потому как тестирование даже экспорта пустого документа - это значит тестировать работу в кооперации нескольких классов. Штук пяти не меньше.
Это не модульные тесты. 

Я приведу процесс создания xlsx (по памяти, могу соврать)
1. Создание xml c данными
2. Создание файлов связей
3. Упаковка всего в архив

Вряд ли этим всем занимается один класс.

Например, архивирование просится в отдельный класс. На этот класс и пишутся тесты. На входе пачка файлов (или директория) - на выходе архив с заданным именем.
Создание XML тоже отдельная задача.
Пытаться тестировать сразу все - распространенная ошибка. Модульное тестирование проверяет работу маленьких кирпичиков. Приемочное тестирование уже работе механизма в целом или его частей, если механизм достаточно велик. А вот в приемных тестах выбирать разработчику будут ли он это автоматизировать и проводить тесты вручную. Если будет автоматизировать - не обойтись без соответствующего окружения для чтения xlsx. Можно и не парить  xlsx, а открывать через COM  файл EXCEL и запрашивать значения ячеек сверяя с эталонным. Работа с COM медленная и запускать такие тесты накладно каждый десять минут. Поэтому их можно отделить в пачку тестов которые запускается перед мержем бренча разработки в основной бранч (master).

Стоит ли тестировать asset. Не знаю, когда-то я тестировал из любви к искусству. Теперь нет. Тесты идут только на более сложное поведение.

В целом скажу, что правильный подход к тестированию это такое же искусство, как и создавать красивые объектные структуры. Единых лекал нет и в каждом проекте все достаточно индивидуально




Vladimir Musulainen

unread,
Dec 1, 2014, 7:06:23 PM12/1/14
to su...@googlegroups.com

> Стоит ли тестировать asset. Не знаю, когда-то я тестировал из любви к искусству. Теперь нет. Тесты идут только на более сложное поведение.

аксессоры, конечно. Извините за многочисленные опечатки. Это дурацкое автоисправление ошибок в MAC OS. Заколебало уже

Dennis Schetinin

unread,
Dec 2, 2014, 3:25:22 AM12/2/14
to Russian Smalltalk User Group
У меня только один ответ есть на все прозвучавшие вопросы — и я его уже озвучивал. Попробую еще один вариант. Если мыслить в рамках "как затестить", когда уже есть (в коде или голове) готовое решение и вопрос в том, как к нему написать тест, то чаще всего бывает именно так — тест написать или невозможно, или он получается "уродским". Сама постановка вопроса к этому приводит. Код, который меня "учили" (не очень подходящее слово… скорее "стимулировали") писать редко получается тестируемым. Чтобы код был тестируемым надо приложить определенные усилия. Иногда это не тривиально и даже контр-интуитивно. TDD позволяет на это надеяться, хотя и НЕ это является сутью этой методологии.

TDD решает задачу перехода от системы A (в которой отсутствует некая функциональность) к системе B (в которой эта функциональность присутствует). Вся хитрость освоения TDD заключается в выработке навыка постепенно формализовать эту разницу и записывать ее в виде спецификации для последовательности примеров использования этой самой функциональности. 

И чтобы ответить на поставленный(-ые) вопрос(-ы) в рамках TDD, надо знать не только разрабатываемую функциональность, но и всю (имеющую отношение к этой ф-ти) A. Причем, может так оказаться, что написать направляющий тест для конкретной реализации A будет либо слишком сложно, либо невозможно — особенно, если об этом аспекте никто и не задумывался при создании A. И (по моему опыту) шансы получить такую ситуацию при "диком" проектировании гораздо выше, чем при последовательном TDD.

чтобы в справочнике клиентов было место рождения

Если не знать, что такое справочник клиентов, ничего сделать не получится. Если предположить, что это нечто простое типа коллекции объектов — не понятно затруднение при написании теста. По остальным вопросам ситуация такая же. Поэтому лучше брать более-менее законченный пример и идти последовательно. (Как мы и пытаемся начать делать в другой ветке.)

Очень похоже, что именно это является барьером для тех кто пытается по-быстренькому освоить TDD: на вопрос "как через TDD реализовать то-то?" — ответа нет! Надо идти — шагами, желательно маленькими (но при этом быстрими). Это относится и к реализации системы, и к освоению TDD :)

В этом же заключается и проблема "игрушечности" таких примеров: брать полномасштабную проблему слишком тяжело и ничего нового к пониманию, на самом деле, это не добавит. По крайней мере, я именно так "продирался": тренируясь на маленьких примерах, получал опыт; возникали идеи о том, как сделать более крупные вещи и т.д.; потом с трудом "вкурил" моки (как и очень многим, если не большинству, это казалось поначалу тавтологией) — но когда "ухватываешь" смысл, все складывается как мозаика. Как и практически в любом деле, попытка решить слишком сложную задачу ничего кроме разочарования не принесет. Начинать учиться играть в хоккей с вопроса "Ну, как мне чемпионат мира выиграть?" или изучать физику с попытки объяснить природу гравитации едва ли представляется разумным.


--

Best regards,


Dennis Schetinin


2 декабря 2014 г., 1:18 пользователь Юрий Мироненко <assar...@gmail.com> написал:

Dennis Schetinin

unread,
Dec 2, 2014, 3:37:31 AM12/2/14
to Russian Smalltalk User Group
Здесь для самого себя надо отделить что "мы тестируем быстро и модульно” и что "мы тестируем долго и полностью”.
Я бы для быстрых тестов не стал бы создавать workbook с worksheet-ами, потому как тестирование даже экспорта пустого документа - это значит тестировать работу в кооперации нескольких классов. Штук пяти не меньше.
Это не модульные тесты. 

Вот с этим я не совсем согласен. Да, здесь нужно тестировать "кооперацию" — точнее, взаимодействие несколько объектов. И это вполне могут быть модульные тесты. На этот случай и придуманы моки. Более того, именно начиная с этого, можно выстроить "архитектуру" (я не просто так беру это слово в кавычки) через TDD.

Прямо сейчас не могу как следует заняться этим примером (он выглядит гораздо интереснее, чем казалось сначала). Постараюсь в ближайшее время найти возможность для этого. Было бы замечательно, если бы устройство ODS кто-ниубудь описал точнее. (Я,действительно, думал там надо просто сформировать XML). Или можем взять за основу то, что написано (независимо от правильности) — для исследования TDD это не важно. Но на изучение формата ODS у меня точно не хватит ни времени, ни сил :)


--

Best regards,


Dennis Schetinin


2 декабря 2014 г., 3:55 пользователь Vladimir Musulainen <vmusu...@mail.ru> написал:

Юрий Мироненко

unread,
Dec 2, 2014, 3:54:08 AM12/2/14
to su...@googlegroups.com
Ну, чтобы было общее понимание: нужно сформировать не одну, а несколько XMLек.

По одной на каждый worksheet (этот worksheet по общим идеям будет напоминать HTMLную таблицу), одну на workbook, одну на таблицу стилей и ещё одну - на файл с описанием того, какая из остальных XMLек воркбук, какая - воркщит(ы), какая - таблица стилей и где они все лежат (потому что они не обязаны лежать в корне документа). И всё это зазиповать.

Это в простейшем случае. В более сложном там могут быть, например, ещё и картинки. Но мы пока не будем так извращаться.

Насколько я понимаю, этого описания, теоретически, должно быть достаточно для того, чтобы начать давать задачи по написанию тестов, которые я могу написать и сам.

2 декабря 2014 г., 11:36 пользователь Dennis Schetinin <cha...@gmail.com> написал:

Dennis Schetinin

unread,
Dec 2, 2014, 6:02:08 AM12/2/14
to Russian Smalltalk User Group
Я таки бегло глянул формат, и мне показалось, что worksheet-ы идут в один файл. Если это не так, изменения тривиальны, если вообще не сводятся к изменению названия одного символа…
В общем, получается примерно так:

OdsExporeterTests >> testGeneratesSubDocuments
  | exporter actualResult |  
  
  exporter := OdsExporter new.

  [ :contentGenerator :styleGenerator :rootGenerator :zipper |

    exporter
      contentGenerator: contentGenertor;
      styleGenerator: styleGenerator;
      rootGenerator: rootGenerator;
      zipper: zipper.

    [actualResult := exporter generate: #worksheets with: #style] 
        should strictly satisfy: 
    [ (contentGenerator generate: #worksheets) willReturn: #contentsFile.
      (styleGenerator generate: #style) willReturn: #styleFile.
      (rootGenerator generateContent: #contentsFile withStyle: #styleFile) willReturn: #rootFile.
      (zipper zip:(Set with: #rootFile with: #contentFile with: #styleFile)) willReturn: #odsFile ]
  
  ] runScenario.
  
  actualResult should be: #odsFile.



Название теста не очень удачное, но пока сойдет — по необходимости потом можно поменять.


--

Best regards,


Dennis Schetinin


2 декабря 2014 г., 12:54 пользователь Юрий Мироненко <assar...@gmail.com> написал:

Dennis Schetinin

unread,
Dec 4, 2014, 7:51:20 AM12/4/14
to Russian Smalltalk User Group
Все так плохо? :)


--

Best regards,


Dennis Schetinin


2 декабря 2014 г., 15:01 пользователь Dennis Schetinin <cha...@gmail.com> написал:

Юрий Мироненко

unread,
Dec 7, 2014, 8:09:09 AM12/7/14
to su...@googlegroups.com
Все так плохо? :)

А? Просто немного зашиваюсь что-то.
Не доходят руки. Если речь вообще о том, о чём я подумал.

4 декабря 2014 г., 15:50 пользователь Dennis Schetinin <cha...@gmail.com> написал:

Dennis Schetinin

unread,
Dec 8, 2014, 12:27:47 AM12/8/14
to Russian Smalltalk User Group
Я про наши экзерсисы с TDD :)
… Если к крайнему примеру нужны пояснения, я готов их дать. Может быть, это просто совпадение, но почему-то примерно на этой стадии нередко все заканчивается. Возникает ощущение, что тесты с моками отбивают всякое желание продолжать. Хотя, на самом деле все довольно просто.


--

Best regards,


Dennis Schetinin


7 декабря 2014 г., 17:09 пользователь Юрий Мироненко <assar...@gmail.com> написал:

Юрий Мироненко

unread,
Dec 8, 2014, 5:56:13 AM12/8/14
to su...@googlegroups.com
Ну на самом деле тут скорее приближающийся конец года...но, тем не менее, даже беглый взгляд подсказывает, что там используется какой-то пакет для этих самых моков.

#runScenario и т.п.

Что за пакет-то?

8 декабря 2014 г., 8:27 пользователь Dennis Schetinin <cha...@gmail.com> написал:

Dennis Schetinin

unread,
Dec 8, 2014, 1:19:21 PM12/8/14
to Russian Smalltalk User Group
Mocketry [http://www.smalltalkhub.com/#!/~dionisiy/Mocketry], конечно! :) Автор присутствует здесь, ежели чо… На мой взгляд, там все читается как обычный английский. Если это не так — готов подробно прокомментировать. Сейчас скажу, что параметры блока сценария (:contentGenerator :styleGenerator :rootGenerator :zipper) превращаются в моки.


--

Best regards,


Dennis Schetinin


8 декабря 2014 г., 14:56 пользователь Юрий Мироненко <assar...@gmail.com> написал:

Ремизов Александр

unread,
Dec 8, 2014, 2:04:12 PM12/8/14
to su...@googlegroups.com


понедельник, 8 декабря 2014 г., 22:19:21 UTC+4 пользователь chaetal написал:
 Если это не так — готов подробно прокомментировать.

Не хочу ловить на слове, наверняка занятого человека..., но мне лично было бы весьма интересно...
как то был грешок, пытался разобраться, но сходу не одолел...   (всё, что попадалось на английском.., я его и в школе-то не проходил, а чего нахватался видимо недостаточно, хочется на простом великом и могучем ;)

Dennis Schetinin

unread,
Dec 9, 2014, 3:52:46 AM12/9/14
to Russian Smalltalk User Group
Начинаем с чистого листа разработку систему экспорта таблиц в ODS формат. Что мы хотим получить? Некий объект, который умеет "генерить" ODS-файлы:

OdsExporeterTests >> testGenerates
  exporter generate should be: #someOdsFile

Я пока не знаю, что такое ODS-файл, так что использую самую простую заглушку (в виде символа). Но надо понять, откуда берется этот самый ODS-файл? В соответствии с документацией, это результат зипования нескольких файлов, а именно контента, стиля (могут быть еще, но эти два, как я понял, обязательны) и файла с общим описанием — корневого файла. Следовательно в процессе генерации мне понадобится объект, который будет зиповать, и он должен получить соответствующее сообщение с перечнем зипуемых файлов; то, что он вернет в ответ на это сообщение и будет результирующим ODS-файлом:

OdsExporeterTests >> testGenerates
  
  [actualResult := exporter generate] should strictly satisfy: 
        [
        (zipper zip:(Set with: #rootFile with: #contentFile with: #styleFile)) 
            willReturn: #odsFile
        ]

  actualResult should be: #odsFile



===== Отступление =====

Добавленная структура имеет вид:

block1 should strictly satisfy: block2.

Возможность сказать системе, что в процессе выполнения некоторых действий (первый блок) должны быть выполнены следующие действия (второй блок) — как раз и обеспечивает нам Mocketry (или любой другой фреймворк, реализующий mock objects). Собственно, мок — это объект, который создается внутри теста и сначала сохраняет ("записывает") полученные сообщения как "ожидания", а затем — в процессе выполнения теста — проверяет, что ожидания действительно были выполнены:

[systemUnderTest exerciseMessage] should strictly satisfy: [mock someMessage].

Фреймворк сначала включит стадию "записи", в ходе которой выполнит второй блок, отследит получение объектом mock сообщения #someMessage и запомнит это как "ожидание". Затем фреймворк перейдет на стадию "воспроизведения" и выполнит первый блок. Если в процессе ее выполнения mock получить сообщение #someMessage, то ожидание будет помечено как выполненное. Если мок получает неожиданное сообщение, то тест проваливается. Если не все ожидания выполнены, то тест проваливается. 

Наряду с записью ожиданий моки записывают свою реакцию, которую затем воспроизводят в случае выполнения ожидвания, например:

[actualResult := systemUnderTest exerciseMessage] should strictly satisfy: 
    [mock someMessage willReturn: #expectedResult].

Когда объект mock получит сообщение #someMessage, будет возвращено #expectedResult в качестве ответа. И, к примеру, проверка типа

actualResult should be: #expectedResult

будет успешно выполнена.

Mocketry использует концепцию "сценария" чтобы… создать что-то вроде среды для выполнения теста с моками… (Денис Кудряшов, наверное, лучше объяснит этот момент, но она нужна. :)  Эта самая среда создается "внутри" блока, если ему послать сообщение #runScenario. То есть, типичный тест с моками выглядит так:

[ :mock1 :mock2 |
  exerciseBlock should satisfy: expectationsBlock
] runScenario

Превращение параметров блока-сценария в моки фреймворк берет на себя.

===== Конец отступления =====



Откуда берется zipper? О нем должен знать наш exporter. Моки заставляют нас делать такие зависимости явными (то есть, использовать Dependency Injection в чистом виде, как я понимаю?). Соответствующим образом дописываем:

OdsExporeterTests >> testGenerates
  [ :zipper |
    exporter zipper: zipper.

    [actualResult := exporter generate] should strictly satisfy: 
          [
          (zipper zip:(Set with: #rootFile with: #contentFile with: #styleFile)) 
              willReturn: #odsFile
          ]
  ] runScenario.
  actualResult should be: #odsFile

Теперь задаем себе вопрос: а откуда взялись #rootFile, #contentFile, #styleFile? Очевидный ответ напрашивается: они должны быть тоже сгенерированы. Кем? Соответствующими компонентами, которые тоже придется добавить (так как у нас нет других подходящих объектов, которым можно было бы приписать соответствующую функциональность). Добавив их, мы столкнемся с вопросом "а из чего они генерят файлы?" Тогда мы поймем, что генерация ODS-файла должна выполняться для определенного контента (worksheet-ов) и с заданным стилем… Ну, вроде, и все — приходим к этом тесту, попутно лишь изменив его название:


OdsExporeterTests >> testGeneratesSubDocuments
  | exporter actualResult |  
  
  exporter := OdsExporter new.

  [ :contentGenerator :styleGenerator :rootGenerator :zipper |

    exporter
      contentGenerator: contentGenertor;
      styleGenerator: styleGenerator;
      rootGenerator: rootGenerator;
      zipper: zipper.

    [actualResult := exporter generate: #worksheets with: #style] 
        should strictly satisfy: 
    [ (contentGenerator generate: #worksheets) 
          willReturn: #contentsFile.
      (styleGenerator generate: #style) 
          willReturn: #styleFile.
      (rootGenerator generateContent: #contentsFile withStyle: #styleFile) 
          willReturn: #rootFile.
      (zipper zip:(Set with: #rootFile with: #contentFile with: #styleFile)) 
          willReturn: #odsFile ]
  
  ] runScenario.
  
  actualResult should be: #odsFile.


Как-то так…


--

Best regards,


Dennis Schetinin


8 декабря 2014 г., 23:04 пользователь Ремизов Александр <gen...@live.ru> написал:

--

Юрий Мироненко

unread,
Dec 9, 2014, 12:30:52 PM12/9/14
to su...@googlegroups.com
Проблема с этим примером в том, что "зиппер" уже существует.
И у него есть свой, вполне определенный API.
И лепить какой-нибудь адаптер туда было бы совершенно излишне.



9 декабря 2014 г., 11:52 пользователь Dennis Schetinin <cha...@gmail.com> написал:

Dennis Schetinin

unread,
Dec 10, 2014, 12:38:18 AM12/10/14
to Russian Smalltalk User Group
Разумеется, я в состоянии писать примеры только с позиции своих знаний. Про уже существующий "зиппер" мне не известно. Если расскажите про его API, пример можно будет адаптировать соответствующим образом. 

Единственная трудность, с которой можно столкнуться — вопрос о том, надо ли "мочить" (ударение на первом слоге) уже существующий объект. Этот вопрос в общем случая для меня пока открыт. Каких-то существенных противопоказаний этому я обычно не вижу. 

Впрочем, открыт и вопрос об "обертывании" уже существующих объектов — в определенных случаях это оказывается вполне оправданной практикой. Но общего правила я для себя пока не обнаружил — принимаю решение каждый раз с учетом конкретных условий.

Вместе с тем, есть более-менее устоявшаяся схема размышлений на подобные ситуации: 
  • Если API существующего объекта хорошо ложится в формирующуюся схему взаимодействия разрабатываемой системы (в данном случае — экспортера) с сотрудниками (генераторы контента, стиля, рута и зиппера), то спокойно мОчим с учетом этого API и не паримся
  • Если использование уже существующего объекта ломает схему, то это, скорее всего, означает, что этот API сложноват для наших целей — его нужно адаптировать. Соответственно, не обращаем пока внимание на существование этого объекта и пишем так, как нам удобно. В результате получится обертка/адаптер. 
  • Но бывает, что возиться с этим лениво или некогда — кажется, что "игра не стоит свеч". Тогда можно попробовать классический вариант — описывать не взаимодействие, а результат. Но это, на мой взгляд, самый плохой вариант. На первый взгляд может показаться, что он более короткий и быстрый, но на практике так бывает очень редко. При этом он запутывает систему и делает ее гораздо сложнее. По сути, он является индикатором беспомощности: я просто не знаю как быть. В общем, последнее время я всячески стараюсь его избегать. И наш пример, вроде бы, не выглядит как пример такого поворота событий.

То есть, решение принимается не исходя из каких-то априорных (с точки зрения текущей задачи) предпочтений, а из чисто практических побуждений.


--

Best regards,


Dennis Schetinin


9 декабря 2014 г., 21:30 пользователь Юрий Мироненко <assar...@gmail.com> написал:

Сергей Глушенко

unread,
Jan 6, 2015, 10:58:21 AM1/6/15
to su...@googlegroups.com
 Я слежу  за этой веткой с самого ее создания. В свое время вопрос с TDD и вообще тестированием был очень болезненным. Доходило до шумных скандалов с руководителем (кстати присутствующем здесь) при его попытках ввести тестирование на проекте. На основании опыта работы в том проекте, и работы с текущим собственным проектом в котором я на первых порах то же пытался ввести тестирование я сделал такие выводы:

1. Для того что бы использовать TDD надо обладать очень специфичным складом ума. Думать от обратного надо уметь, все таки большинство людей привыкли к последовательному а не обратному мышлению. TDD как я его понял подразумевает изначальное написание теста охватывающего максимальный функционал разрабатываемого объекта. Вместо объекта подсовывается некий волшебный ящичек  который выполняет требуемую функцию. Затем пишется более детальный тест, дописыватся обвязка ящичка, возможно (а в большинстве случаев именно так и происходит) переписывается вся предыдущая обвязка, и так пока ящичек не перестанет быть нужным. Наверное так можно писать когда четко знаеш что у тебя должно получится в конце.В реальной жизни же так почти никогда не бывает. В начале работы обычно не знаеш что получится на выходе и с чем столкнешся. Есть общее понимание что хотим получить, и в процессе разработки вырисовывается четкая картина реализации. При этом очень часто бывает что она сильно отличается от первоначальной. Тут и подводные камни которые невозможно обойти без изменения изначальной концепции, и какие - то озарения или мысли пришедшие в процессе, да и много еще причин. В случае с TDD это подразумевает переписывание кучи тестов, на и практически всего кода, поскольку как мы помним мы начали с общего случая,и при изменении общей идеи (ну или конечного результата) ломается все уже сделанное. Так что TDD неверное хорош, но в сказке.

2. Автоматические тесты - зло (а вот за это меня побьют). Попробую обосновать.

2.1 100% покрытие тестами невозможно. Так же невозможно помнить (в случае с большим проектом) что покрыто тестами а что не покрыто. Но при разработке помня о том что есть тесты полноценное ручное тестирование не производится. Соответственно не покрытый тестами функционал не тестируется. Отдута и лезет максимальное количество ошибок. С моей точки зрения правильнее четко осознавать свои деиствия и понимать с какими классами и объектами ты работаеш. Не работать сразу с большим количеством классов. Чаше тестировать ручками последствия своих деийствий и стараться понимать к чему они могут привести. Ну и действия с существующим кодом производить только в случае необходимости. Не надо если вам почему- то не понравилось имя класса его менять, то же касается названий методов и переменных. Ну и конечно (но это опять таки мое личное мнение) обязательная проверка входных данных в методах. Например проверка на nil не отнимет много времени, но часто спасает. Даже если по первоначальной задумке там его быть не может.

2.2 Наиболее частые ошибки возникают в UI. Ни и тестирование UI исамое сложное если не невозможное дело. К тому же действия пользователя практически невозможно предсказать. как пример приведу один багрепорт:

LAD, Контроллер любой
Обнаружил случайно. Ставлю блок "Математика". Нажимаю (например) на конец вывод "А" мышкой. Одновременно левую и правую кнопку быстро и несколько раз.
Интерфейс вылетает.

Вот и скажите реально ли такое протестировать.

2.3. Написание тестов неоправданно увеличивают время разработки. Опять таки на личном опыте могу сказать что написание тестов на метод по времени превышает написание самого метода в 3-5 раз в зависимости от сложности метода. Во первых надо создать инстанс объекта, поставить необходимые заглушки и врапперы, а затем надо покрыть все возможные комбинации входных данных. В отдельных случаях на один метод пишется до десятка тестов. В сложном случае при попытке 100% покрытия конечный код просто никогда не будет написан. Ну и естественно при необходимости изменения поведения объекта всю эту кучу тестов необходимо переписывать. В случае развивающегося динамичного проекта использование тестирования ставит на этом проекте крест.

Я предпочитаю использовать разработку через дебаг -с моей точки зрения наиболее быстрый способ разработки. Но вот только он никак не сочетается с тестированием и тем более с TDD.


Ну а теперь можете приступать к избиванию)))))

 

среда, 26 ноября 2014 г., 18:29:43 UTC+5 пользователь Assargadon написал:

Dennis Schetinin

unread,
Jan 6, 2015, 1:00:14 PM1/6/15
to Russian Smalltalk User Group

6 января 2015 г., 19:58 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

Для того что бы использовать TDD надо обладать очень специфичным складом ума. Думать от обратного надо уметь <…>  Наверное так можно писать когда четко знаеш что у тебя должно получится в конце.В реальной жизни же так почти никогда не бывает. В начале работы обычно не знаеш что получится на выходе и с чем столкнешся. 

Алиса: – Куда мне отсюда идти?
Ч.Кот: — А куда ты хочешь попасть?
Алиса: — А мне все равно, только бы попасть куда-нибудь.
Ч.Кот: — Тогда все равно куда идти. Куда-нибудь ты обязательно попадешь.

Надо обладать специфичным складом ума, чтобы представлять, куда ты (скажем, впервые) направляешься, не имея возможности представить себе все нюансы маршрута?

TDD не предполагает какого-либо особого склада ума. Этот подход всего лишь заставляет в каждый момент времени понимать конечную цель своих действий — еще до начала этих самых действий.


2. Автоматические тесты - зло <…> 1… 2… 3…

Автомобили зло, потому что:

1. На них можно доехать не до любой точки

2. Чаще всего мы хотим прибыть в какое-либо помещение, а на автомобиле туда заехать невозможно

3. Автомобиль требует массу времени: его надо заправлять, заводить, ремонтировать.

Теперь, внимание, вопрос: а какое все это отношение имеет к автоматизации?

ввести тестирование на проекте

Не устану повторять: TDD и тестирование — принципиально разные вещи. Как автоматизация и автомобиль — даже несмотря на то, что у них общий корень "авто".

Сергей Глушенко

unread,
Jan 7, 2015, 12:08:03 AM1/7/15
to su...@googlegroups.com
TDD не предполагает какого-либо особого склада ума. Этот подход всего лишь заставляет в каждый момент времени понимать конечную цель своих действий — еще до начала этих самых действий.
 
Возможно в небольших проектах и можно четко представлять себе конечную цель. Заранее продумать всю архитектуру, состав классов и взаимодействие между ними. Но в этом случае проект получится статическим. Его нельзя будет малой кровью модернизировать или где ни будь в середине пути свернуть в сторону. Я год назад начиная свой проект не представлял во что он выльется, имел только смутное представление что я хочу. И теперь я не представляю чем он будет через год. Общая архитектура за это время менялась уже много раз и от базовой архитектуры практически ничего не осталось. Многие связи, и решения как раз возникают в процессе решения задач, а не перед началом работы над задачей. В случае с TTD сначала надо четко себе представить что будет в конце и идти выбранным путем несмотря ни на что. То есть сначала надо очень долго продумывать будущую реализацию. В процессе продумывания можно просто потерять интерес к задаче. Ну или процесс построения будущей архитектуры растянется на непомерно большое время. и не факт что результат будет удачным. Но свернуть то нельзя, потому что изменения тянут за собой глобальные переделки тех же тестов написанных в процессе разработки через TDD.

Ну есть и еще один минус TDD который объеденяет его с тестированием. Глобальное замедление процесса разработки. Посмотрим на реальном примере из моего проекта (полько вчера пробовал).

Есть обект - блок генератора импульсов. Он может параметрироваться. То есть он может быть одновибратором, симметричным генератором, несимметричным генератором. в казачестве параметров в первом и втором случаях  выступает  длительность включенного состояния, в третем случае параметров  два - длительность включенного и длительность выключенного состояния. Длительность может задаваться либо в милисекундах либо в микросекундах. каждый из параметров может быть либо константой, либо читаться со входа.

Для создания скетча (результата работы компилятора) мне необходимо знать какую из функций добавить в скетчь - окончание выдержки в милисекундах или окончание задержки в микросекундах. Соответственно у объекта генератор реализуем метод


isNeededMicrosTimerUtilityFunction



в соответствии с логикой TDD  пишем первый тест (я  свои эксперементы со злости удалил, так что описываю словами)

Создаем Project.
Создаем блок FBDGeneratorBlock.
Добавляем его в проект

 Эта часть подымается родительскому классу что бы в последствии получать блок одним методом

блоку говорим что он одновибратор
говорим ему что параметр  время включенного состояния будет константой.
ставим ему параметр время включенного состояния 1000 милисекунд.
проверяем что метод isNeededMicrosTimerUtilityFunction возвращает false


реализация метода что бы тест стал зеленый

isNeededMicrosTimerUtilityFunction

^self onConstantIsMicros.


кстати реализация метода
onConstantIsMicros
то же обвязывается тестами.

Второй тест

Создаем блок
блоку говорим что он одновибратор
говорим ему что параметр  время включенного состояния будет константой.
ставим ему параметр время включенного состояния 1000 микросекунд.
проверяем что метод isNeededMicrosTimerUtilityFunction возвращает true


Второй  тест

Создаем блок
блоку говорим что он одновибратор
говорим ему что параметр  время включенного состояния будет константой.
ставим ему параметр время включенного состояния 1000 микросекунд.
проверяем что метод isNeededMicrosTimerUtilityFunction возвращает true


Реализация метода не меняется  поскольку метод
onConstantIsMicros
 уже работает правильно

Третий   тест

Создаем блок
блоку говорим что он одновибратор
говорим ему что параметр  время включенного состояния будет вход.
ставим ему параметр время включенного состояния 1000 милисекунд.
проверяем что метод isNeededMicrosTimerUtilityFunction возвращает false


Реализация метода не меняется  поскольку метод
onConstantIsMicros
 уже работает правильно

.....

в результате получилось более двадцати тестов на один метод конечный код:

isNeededMicrosTimerUtilityFunction

 
(self isSingle or: [self isSinhroMulty])
 ifTrue
: [^self onConstantIsMicros].
 
^self onConstantIsMicros or: [self offConstantIsMicros]

С той работой с которой я обычно справляюсь за час я провозился день и то до конца не сделал. Ближе к шести вечера (а работаю я с восьми до восьми) я разозлился снес к чертям пакет SUnit вместе со всеми тестами и быстренько все доделал. Я честно пытался понять прелесть TDD но либо я дурак, либо что то в TDD не то. Неприятно считать себя дураком, поэтому я и написал здесь. Может я что то не понимаю, и кто то сможет мне объяснить в чем прелесть использования TDD. По результатам моего небольшого эксперимента он жестко ограничивает свободу разработчика, при этом являясь огромным тормозом в скорости разработки.




вторник, 6 января 2015 г., 23:00:14 UTC+5 пользователь chaetal написал:

Юрий Мироненко

unread,
Jan 7, 2015, 1:23:36 AM1/7/15
to su...@googlegroups.com
Прежде чем продолжить, Сергей, объясните пару моментов.

Во-первых, почему вы используете логику с побочным эффектом вместо нормальных & и | ? Возможно, здесь скрывается что-то, чего я не понимаю, и потому мои дальнейшие рассуждения тоже будут неверными.

Во-вторых, если я правильно понял, isNeededMicrosTimerUtilityFunction есть логическая функция от четырёх аргументов:
  1. isSingle
  2. isSinhroMulty
  3. onConstantIsMicros
  4. offConstantIsMicros

Поскольку, судя по названиям элементов, там Boolean, всевозможных комбинаций - 16, а вы говорите о 20+ тестах. Откуда берутся дополнительные тесты?

7 января 2015 г., 8:08 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

--

Администрация сайта FLProg.ru

unread,
Jan 7, 2015, 1:41:02 AM1/7/15
to su...@googlegroups.com
Просто я не стал разворачивать методы onConstantIsMicros и offConstantIsMicros. Их результат зависит от  типа параметра. Это вход или константа. Соответствено эти варианты необходимо проверять.
И второй вопрос - я не понял а что такое логика с побочными эффектами. Если Вы имеете в в виду что при использовании  self a or:[self b] если а возвращает - true блок or выполняться и метод b просто не вызовется , то это и является основной прелестью такого подхода. Я стараюсь по возможности везде использовать такую конструкцию.  Например если если а возвращает - true то не важно что возвращает метод b,  он вызываться не будет
-- 
С уважением,
Сергей Глушенко
 
 
 
07.01.2015, 11:23, "Юрий Мироненко" <assar...@gmail.com>:
Вы получили это сообщение, поскольку подписаны на одну из тем в группе "Russian Smalltalk User Group".
Чтобы отменить подписку на эту тему, перейдите по ссылке https://groups.google.com/d/topic/sugr/4ibrvXynivs/unsubscribe.
Чтобы отменить подписку на эту группу и все ее темы, отправьте письмо на электронный адрес sugr+uns...@googlegroups.com.

Vladimir Musulainen

unread,
Jan 7, 2015, 2:30:02 AM1/7/15
to su...@googlegroups.com


среда, 7 января 2015 г., 8:08:03 UTC+3 пользователь Сергей Глушенко написал:

isNeededMicrosTimerUtilityFunction

 
(self isSingle or: [self isSinhroMulty])
 ifTrue
: [^self onConstantIsMicros].
 
^self onConstantIsMicros or: [self offConstantIsMicros]



Когда я вижу такое, то сразу приходит на ум, что объект слишком  заумный и надо , либо сделать несколько классов, каждый из которых имплементирует один вид генератора, либо использовать декомпозицию. 

Отмечу несколько моментов относительно TDD и тестирования:
1. Если разработчик проекта один и он все держит под контролем, то нужды в автоматическом тестировании нет. Как и во многих других практиках программирования как-то: code style, version control system и тому подобное. Единственный разработчик может обзывать классы/методы хоть на китайском. Не пользоваться CVS/GIT/по вкусу и версии держать в виде папок с архивами. Не иметь тестов вообще. И это никак особо не повлияет на скорость его работы. Но вот, как только туда придет второй программист, вот тут начнется попа. Ибо они будут разговаривать на разных языках, обмениваться папками с исходниками для передачи изменений в коде и нервничать на тем: 'Я внес изменение в программу в метод, который часто используется - как мне проверить, что ничего не сломалось? Ответ - руками запустить программу и вызывать много сценариев работы. Уп-с, выпустили релиз с багой. Петя, а почему ты не проверил такой сценарий? Вася, а что и такой мог быть?' И тут начнется создание чек-листов с сценариями для ручной проверки и тому подобное. 
2. Не надо тестировать, то что тестируется плохо. Плохо тестируется UI - не тестируйте. Не надо писать  тестов на каждый метод, включая ассессоры. Это я говорю касательно unit-тестирования. Рассматривайте класс, как черный ящик. Вы дергаете за рычаги на нем и он должен вам выдать золотой слиток. Это и описывает тест. 

Сергей Глушенко

unread,
Jan 7, 2015, 3:06:26 AM1/7/15
to su...@googlegroups.com
По поводу Васи тут двоякая ситуация.  В той же ситуации с релизом и багой. Петя а почему ты не написал тест?. Как возможно не проверить ситуацию при ручной проверке, то точ но так же и возможно просто не написать тест на эту ситуацию. Только без тестов Вася с Петей занимаются выпуском версий выполняя полезную работу, а в случае качественного тестирования и поддержания тестов в рабочем состоянии занимаются только этим. И релиза от них не дождешся. Если на каждый чих надо переписать тонну кода то и чих то делать расхочется. Да и не согласен я с тем что написание тестов помогает Пете с пониманием проекта написанного Васей. Как бы не усложняет. Для тестов в классах приходится делать дополнительные вспомогательные методы, что то закрывается моками и врапперами. Да и общее количество кода необходимого для просмотра и понимания возрастает в разы. В базовом образе SmallTalk нет тестов, но между тем мы довольно таки легко находим нужные нам классы и методы. Я то же не помню что я делал год назад, так что при доработке старых классов могу сравнить себя с тем Петей. Вопрос в написании адекватных названий классов методов. Возможно не всегда правильных с точки зрения орфографии, но понятных с первого взгляда. Ну и при необходимости в особо сложных случаев написание комментов. Думаю это более простое и доступное решение чем документирование с помощью тестов.

я думаю из названий этих классов любому знакомому с предметной областью программистом будет все понятно
AbstractBlock
   
AbstractLADElement
       
LADManyInputsBlock
           
LADBlocksWithoutVariables
               
LADAnalogSwith
               
LADRealTimeClockSetTimeBlock
               
LADScaleBlock
               
LADBounceBlock
               
LADCounterBlock
               
LADStepMotorBlock
               
LADCommPortBlock
               
LADDisplayOnChipHD44780Block
               
LADRealTimeClockAlarmBlock
               
LADRealTimeClockGetTime
               
LADArithmeticBlock
               
LADServoMotorBlock
               
LADStandartFunctionBlock
                   
LADABSBlock
                   
LADCosBlock
                   
LADMAXBlock
                   
LADMINBlock
                   
LADPOWBlock
                   
LADRANDOMBlock
                   
LADSinBlock
                   
LADSQBlock
                   
LADSQRTBlock
                   
LADTanBlock
               
LADUltrasonicBlock
               
LADIrRessiveBlock
               
LADDisplayOnChipHD4480I2CBacklightControlBlock
               
LADAbstractSDBlocks
                   
LADSaveToSDVariableBlock
                   
LADUploadFileFromSDBlock
               
LADConvertFloatToIntegerBlock
               
LADBlocksWithTypeAndUpdateTime
                    LADBMP085Block
                   
LADDigitalTemperatureAndHumidityBlock
                    LADDS18x2xBlock
               
LADChip74HC595Block
               
LADSevenSegmentDecoderBlock
               
LADManyInputsSwithBlock
               
LADManyOutputsSwithBlock
               
LADBitCoderBlock
               
LADBitDecodeBlock
               
LADBitReadeBlock
               
LADBitWriteBlock
               
LADMatrixKeyboardBlock
               
LADPiezoSpeakerBlock
               
LADReadFromEepromBlock
               
LADSaveToEepromBlock
               
LADSendRessiveVariableFromCommunication
                   
LADRessiveVariableFromCommunication
                   
LADSendVariableFromCommunication
               
LADWebServises
                   
LADWebServerPage
                   
LADWebClient
           
LADSummStringBlock
           
LADSpecialCoil
               
LADCompareCoil
               
LADTriggerCoil
               
LADTimerCoil
               
LADGenCoil
           
LADConvertStringBlock
       
LADBasicElement
           
LADContact
           
LADCoil
               
LadRtrigCoil
       
LADAnalogInputOutputBlock
           
LADAnalogConductorInput
           
LADAnalogInput
           
LADAnalogConductoOutput
           
LADAnalogOutput
       
LADSendRessiveVariablesBlocks
           
LADRessiveVariablesBlocks
           
LADSendVariableBlock
       
LADSpeedCounterBlock
   
AbstractElement
       
BlocksWithType
           
BlocksWithTypeAndCollectionOutputs
               
BlocksWithTypeAndCollectionInputsOutputs
                   
FBDSwitchBlock
                   
FBDArithmeticBlock
                   
FBDBounceBlock
                   
FBDSummStringBlock
                   
FBDTimerBlock
                   
FBDServoMotorBlock
                   
FBDBasicLogikBlock
                   
FBDCommPortBlock
                   
FBDCompareBlock
                   
FBDTriggerBlock
                   
ALADAnalogConductoOutput
                   
FBDStandartFunctionBlock
                   
FBDConvertStringBlock
               
FBDGeneratorBlock
           
CounterBlock
           
FBDStepMotorBlock
           
FBDDisplayOnChipHD44780Block
           
FBDRealTimeClockAlarmBlock
           
FBDRealTimeClockSetTimeBlock
           
FBDRealTimeClockGetTimeBlock
           
FBDScaleBlock
           
FBDUltrasonicBlock
           
FBDIrRessiveBlock
           
FBDDisplayOnChipHD4480I2CBacklightControlBlock
           
FBDAbstractSDBlocks
               
FBDSaveToSDVariableBlock
               
FBDUploadFileFromSDBlock
           
FBDConvertFloatToIntegerBlock
           
FBDBlocksWithTypeAndUpdateTime
               
FBDDigitalTemperatureAndHumidityBlock
                FBDDS18x2xBlock
                FBDBMP085Block
           
FBDChip74HC595Block
           
FBDSevenSegmentDecoderBlock
           
FBDManyInputsSwithBlock
           
FBDManyOutputsSwithBlock
           
FBDBitCoderBlock
           
FBDBitDecodeBlock
           
FBDBitReadBlock
           
FBDBitWriteBlock
           
FBDMatrixKeyboardBlock
           
FBDPiezoSpeakerBlock
           
FBDSaveToEepromBlock
           
FBDReadFromEepromBlock
           
FBDWebServises
               
FBDWebServerPage
               
FBDWebClient
               
FBDNarodMonRuClient
       
TagBlock
       
SendRessiveVariablesBlocks
           
SendVariableBlock
           
RessiveVariablesBlocks
       
SpeedCounterBlock
       
FBDSendRessiveVariableFromCommunication
           
FBDRessiveVariableFromCommunication
           
FBDSendVariableFromCommunication




среда, 7 января 2015 г., 12:30:02 UTC+5 пользователь Vladimir Musulainen написал:

Юрий Мироненко

unread,
Jan 7, 2015, 3:55:19 AM1/7/15
to su...@googlegroups.com
Теперь, Денис, вопрос и тебе.

Я тоже попадал в такую ситуацию, даже писал расширение для SUnit для генерирования разных комбинаций входных параметров. Но у меня была задача проще - грубо говоря, надо было, чтобы пустая строчка и nil считались бы одним и тем же.

Очевидное решение здесь - написать таблицу истинности и по ней всё проверить. В одном тесте, не в двадцати :)

Но тут возникает очевидный вопрос: уж если есть таблица истинности, то проще считать её реализацией метода, а не его проверкой.

7 января 2015 г., 9:23 пользователь Юрий Мироненко <assar...@gmail.com> написал:

Сергей Глушенко

unread,
Jan 7, 2015, 4:12:21 AM1/7/15
to su...@googlegroups.com
Изначально у меня то же в этом методе было что то вроде таблицы истинности (пришол как раз через эксперимент с TDD) но затем знание о виде конкретного параметра мне понадобилось в компиляторе и было вынесенно в метод. Да и вообще реализация конкретного метода мне кажется расскажет о его работе быстрее и понятнее чем куча тестовых методов вокруг него. С моей точки зрения главное стараться делать методы максимально короткими. Сложные конструкции надо сразу же выносить в отдельные методы, тогда и работа конкретного метода будет сразу понятна с первого взгляда и никакие тесты не нужны будут

среда, 7 января 2015 г., 13:55:19 UTC+5 пользователь Assargadon написал:

Nikolay Kleptsov

unread,
Jan 7, 2015, 11:10:52 AM1/7/15
to Russian Smalltalk User Group
Добавлю небольшую ремарку.
Тесты не влияют на производительность приложения, они находятся как-бы в стороне.
При создании классов так или иначе приходится испытывать класс (Workspace, Debuger) TDD будет приятным дополнением.
Добавление тестов занимает меньше 5% разработки.
Понимание кода улучшается, анализируя методы тестов.
Отладчик показывает стек сообщений, большую часть ошибок можно исправить, а если ошибка не связана напрямую.
ТDD тоже не панацея от всего и все-таки

7 января 2015 г., 15:12 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

Dennis Schetinin

unread,
Jan 10, 2015, 5:51:59 AM1/10/15
to Russian Smalltalk User Group
Прошу прощения за запоздалый ответ — был в офлайне несколько дней.

Возможно в небольших проектах и можно четко представлять себе конечную цель. Заранее продумать всю архитектуру, состав классов и взаимодействие между ними.

Сергей, данная фраза, весь абзац, из которой я ее взял, да и все дальнейшие рассуждения показывают, что вы плохо представляете себе что такое TDD. Просто с точностью "до наоборот". Так что пытаться оспаривать детали здесь нет никакого смысла. Давайте сначала определимся с тем, что такое TDD? Выводы типа "либо я дурак, либо…" предлагаю сразу исключить.




--

Best regards,


Dennis Schetinin


7 января 2015 г., 9:08 пользователь Сергей Глушенко <sup...@flprog.ru> написал:
--

Dennis Schetinin

unread,
Jan 10, 2015, 6:00:11 AM1/10/15
to Russian Smalltalk User Group
Но тут возникает очевидный вопрос: уж если есть таблица истинности, то проще считать её реализацией метода, а не его проверкой.
Если я правильно все понимаю, опять же изложенное сводится к проблеме "тестирование против разработки". Ты спрашиваешь: "как тестировать", если реализация почти совпадает с условием теста? TDD на это ответа не даст — не в ее компетенции. TDD занимается вопросом как получить реализацию. Если это тебя интересует, напиши точнее исходное состояние системы и что надо в нее добавить — специфицируй задачу. (Кстати, тест в TDD — это как раз записанная в коде спецификация).

Мне вот теперь тоже захотелось задать вопрос. Я уже много раз повторял в рамках этой дискуссии: давайте не путать TDD и тестирование. Вроде бы никто ни разу не пытался этот тезис оспаривать. Но каждый раз упорно эта тема возвращается. Почему?



--

Best regards,


Dennis Schetinin


7 января 2015 г., 12:55 пользователь Юрий Мироненко <assar...@gmail.com> написал:

Dennis Schetinin

unread,
Jan 10, 2015, 6:13:50 AM1/10/15
to Russian Smalltalk User Group

7 января 2015 г., 9:08 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

По результатам моего небольшого эксперимента он жестко ограничивает свободу разработчика, при этом являясь огромным тормозом в скорости разработки.

А по результатам моего многолетнего эксперимента TDD либо повышает (при программировании в Smalltalk), либо не влияет в среднесрочной и повышает в долгосрочной перспективе производительность (в C# и Java). 

Первое означает, что даже на 15-минутной задаче я через TDD чаще сделаю быстрее, чем "напрямую", если учитывать все время на обдумывание задачи, ее понимание и проектирование. 

Второе означает, что из-за неудобства (мое личное ощущение) сред разработки и ужасной статической типизации "накладные расходы" на создание теста в соответствующих средах гораздо выше и на начальных этапах относительно простой, скажем, недельной задачи они могут показаться неоправданными. Но опыт показывает, что недельных задач не бывает — к ним приходится возвращаться, до- и даже пере-делывать. И в такой перспективе TDD себя оправдывает.

Насчет "жесткого ограничения свободы разработчика" — вопрос интересный. Можно обсудить — это может привести нас к взаимопониманию по вопросу что такое TDD. Если возникнет желание, то предлагаю начать с вопроса о том, что вы понимаете под свободой разработчика и как TDD ее ограничивает (желательно с примерами).

Юрий Мироненко

unread,
Jan 12, 2015, 5:54:54 PM1/12/15
to su...@googlegroups.com
Не могу удержаться от небольшого ёрничанья.
Выдержка из новогодней новости на сайте проекта (курсив мой):

...При  сохранении проекта программа падает. Правильно говорят что торопится нельзя и надо полнее производить проверку программы.

Как бы намекает...

P.S. Конечно, я понимаю, что TDD и "просто тестирование" - вещи разные. Но мне казалось, что критика Сергея относилась не только к TDD, но и к тестам вообще. Хотя бы потому, что в конце повествования он снёс SUnit :)

10 января 2015 г., 14:13 пользователь Dennis Schetinin <cha...@gmail.com> написал:

--

Dennis Schetinin

unread,
Jan 12, 2015, 9:48:45 PM1/12/15
to Russian Smalltalk User Group

13 января 2015 г., 2:54 пользователь Юрий Мироненко <assar...@gmail.com> написал:

P.S. Конечно, я понимаю, что TDD и "просто тестирование" - вещи разные. Но мне казалось, что критика Сергея относилась не только к TDD, но и к тестам вообще. Хотя бы потому, что в конце повествования он снёс SUnit :)

Но это совершенно другая тема, я бы ее обозначил как "Управление тестами". Хотя у Сергея я ее особо не увидел — он до нее попросту не добрался. Но я могу ошибаться: посты были длинными, мог упустить этот мотив.

Это, кстати, одна из тех, которыми я пытаюсь заниматься в свободное время… Если есть интерес, можно открыть соответствующую ветку…

Сергей Глушенко

unread,
Jan 12, 2015, 9:51:12 PM1/12/15
to su...@googlegroups.com
Да это была глупая ошибка)))))  Но если честно очень хотелось выпустить версию до нового года, просто прям горело в одном месте. как раз после неё я и решил провести эксперимент с тестированием и TDD. Неудачный. Хотя если честно, ошибки в существующем функционале у меня очень большая редкость.  В основной массе ошибки возникают в новых блоках  по достаточно ограниченному ряду причин. У менея даже есть небольшой список что надо обязательно сделать при создании нового блока, но все равно время от времени на этом прокалываюсь. Щсновная из причин - не полная реализация метода postCopy.  Где то 80% ошибок идут отдуда. Забываю реализовать либо копирование обекта в переменной , либо реализовать PostCopy у самого нового обекта.  И тут никакое тестирование не поможет. Если я вспомню о необходимости написать тест на копирование переменной, то мне быстрее написать одну строчку на копирование чем тест. И самое главное что это не будет менятся в будущем, поэтому проверять не имеет смысла. Ну и конечно спасибо моим тестерам. Их только зарегестрированных уже почти 400. А реально скачивают новую версию более полутора тысяч раз. Ну и очень помогает в тестировании версии написание видеоуроках. Во время видеоурока показывается весь новый функционал, поэтому и проверяется по полной. Еще по моему не было не одной записи во время которой не нашлось бы несколько ошибок. Так что пока обходимся без автоматического тестирования.
вторник, 13 января 2015 г., 3:54:54 UTC+5 пользователь Assargadon написал:

Dennis Schetinin

unread,
Jan 12, 2015, 10:13:10 PM1/12/15
to Russian Smalltalk User Group
Так и знал, что фраза Юрия спровоцирует очередной цикл разговора на тему "TDD — это не тестирование". Но я обещал не уставать про это писать — буду стараться. Так что очередная порция занудства.

В основной массе ошибки возникают в новых блоках  по достаточно ограниченному ряду причин. У менея даже есть небольшой список что надо обязательно сделать при создании нового блока, но все равно время от времени на этом прокалываюсь. Щсновная из причин - не полная реализация метода postCopy.  Где то 80% ошибок идут отдуда. Забываю реализовать либо копирование обекта в переменной , либо реализовать PostCopy у самого нового обекта.  И тут никакое тестирование не поможет. Если я вспомню о необходимости написать тест на копирование переменной, то мне быстрее написать одну строчку на копирование чем тест.

Сергей абсолютно точно определил исходный источник проблемы: организацию труда. Выполняется некая рутинная операция. К ней есть определенные требования. Эти требования нужно реализовать. Сама реализация — тривиальна. Пролема в том, что эти требования реализовать тупо забывают. TDD тут ни при чем вообще.

TDD может помочь тогда, когда есть конкретная задача и не ясно, как ее реализовать. TDD — это методология разработки!
 
И самое главное что это не будет менятся в будущем, поэтому проверять не имеет смысла.

TDD и тут ни при чем, но "поживем — увидим"?



Но вот что привлекло мое внимание… (Начну издалека) Если задуматься, для чего вообще пишутся программы? Я предлагаю такую мысль: программы пишутся для того, чтобы снять с человека бремя выполнения рутинных операций. Даже если это утверждение не верно в 100% случаев, часто (и на мой взгляд это очень правильно) дело обстоит именно так. Но разработчики часто оказываются первыми, кто про это забывает. Описанный случай — показательный пример. Слова "забываю реализовать" говорят о том, что здесь нужна автоматизация! 

Разумеется, я не имею ввиду, что надо сделать какой-нибудь виззард с кодогенератором или какой-то подобный монстрообразный наворот. Надо просто создать соответствующую инфраструктуру в коде, которая либо будет заставлять реализовывать этот метод, либо (предпочтительнее) реализует его один раз в общем виде. По второму варианту, не зная кода, ничего сказать не могу, кроме того, что здесь как раз может пригодиться рефлексия Smalltalk-а. А по первому варианту напрашивается как раз написать тест (не TDD-шный!), который будет проходить по всем блокам в системе и проверять правильность выполнения операций, которые должны поддерживаться всеми блоками. 






--

Best regards,


Dennis Schetinin


13 января 2015 г., 6:51 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

Сергей Глушенко

unread,
Jan 12, 2015, 11:06:26 PM1/12/15
to su...@googlegroups.com
Для "простых" блоков где входа и выхода - коллекции у меня реализовано  автоматическая операция PostCopy. Для более навороченных которые сейчас в основном разрабатываются где входы и выходы именные все сложнее. Я пока не придумал реализацию как производить проверку правильности копирования. Плюс в блоках чаще всего вложенны другие сложные объекты. Все блоки по этому критерию отличаются так что универсального теста не сделать. Большинство блоков после создания не модернизируются.соответственно правильно сделанные и проверенные не нуждаются в дальнейшем тестировании.
Для себя я решил проблемму старым проверенным способом. Бумажка на мониторе))) Цитирую:
PostCopy!!!!! (Там знаков большею Каждый раз когда матерюсь что забыл, ставлю один восклицательный знак.)
LAD - тип входов (при переносе в блока в LAD необходимо сменить тип входов. Все не решаюсь привести в FBD и LAD ихода и выхода к одному классу. Могут полететь старые проекты, хотя идея более менее безопасного перехода уже есть)
isCorrect- проверить (проверить правильность выполнения проверки корректности блока. Если упустить какое либо некорректное состояние блока, например не заполненное полк номера пинато пользователи обязательно на это состояние напорятся и при компиляции либо получат ошибку, либо скетч не будет работать  )
usedDigitalPin - проверить

В принципе это основные проверки которые могут привести к проблемам. Остальные общие свойства если необходимы в данном блоке, корректно реализованы наверху. В случае необходимости их перекрытия это явно вылазит в процессе разработки блока. как реализовать автоматическое тестирование показанных выше методов для абсолютно различных блоков я даже представить не могу.

Вот как то так выкручиваюсь пока без тестов.



вторник, 13 января 2015 г., 8:13:10 UTC+5 пользователь chaetal написал:

Vladimir Musulainen

unread,
Jan 13, 2015, 1:11:00 AM1/13/15
to su...@googlegroups.com
Все объекты, которым надо копироваться полностью, включая копии объектов в instance variable наследуем от одного класса, который называем CopyableObject.
У класса CopyableObject определяем метод postCopy что-то типа
postCopy

postCopy
super postCopy.
myInstanceVariablesNames do: [:each | self at: each name put: each copy]


Это не решит всех проблем с PostCopy?




13 янв. 2015 г., в 7:06, Сергей Глушенко <sup...@flprog.ru> написал(а):

Сергей Глушенко

unread,
Jan 13, 2015, 1:29:56 AM1/13/15
to su...@googlegroups.com
Я об этом думал, не поможет.
Вот смотри.
Реализация PostCopy  в разных блоках сильно отличается.
       Вода - выхода - вход может быть, или его может не быть. Если он есть - (не nil) то надо сделать его копию и привязать к новому блоку. Конечно ссылка на родительский объект в дочернем - это полхо - я понимаю, но поиск родителя через проект безумно тормозит отрисовку. Поэтому я пошол на этот не самый лучший шаг.  кроме входов в блоке куча других переменных. и работа с ними в разных блоках отличается. Например блок датчика DHT-11. При копировании блока необходимо в переменную содержащую модель датчика положить новый чистый инстанс модели. Блок дисплея. Если дисплей прямого подключения - его надо снести (засунуть nil), если дисплей по i2С даже копировать не надо. И так в каждом конкретном случае рассматривается, что надо скопировать. что надо снести, что надо оставить без изменения. Кое где необходимо просто поменять внутри данные, без копирования. 

Хотя сейчас пришла в голову идея для входов - выходов. Пробежаться по всем переменным, и если там лежит вход или выход (а это один класс), то произвести над ним стандартую операцию. Надо будет попробывать.
 


вторник, 13 января 2015 г., 11:11:00 UTC+5 пользователь Vladimir Musulainen написал:

Nikolay Kleptsov

unread,
Jan 13, 2015, 5:04:11 AM1/13/15
to Russian Smalltalk User Group
Если одним методом postCopy не получается, тогда сделать декомпозицию методов, может и классов. Попробовать выделить общий функционал блоков и дополнительные свойства. Количество реализаций блоков не бесконечно. Без кода трудно точнее что-то сказать

13 января 2015 г., 12:29 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

Сергей Глушенко

unread,
Jan 13, 2015, 5:28:55 AM1/13/15
to su...@googlegroups.com
Конечно композицию, наследование я использую. В основном наследование, поскольку композицию я не очень люблю. Она менее наглядна.  Когда сразу понятно что блоки будут иметь общие свойства, или когда набирается какое то количество классов с похожими характеричтиками. Да и при построении нового блока я внимательно продумываю от какого какого класса в иерархии блоков наследоваться. Но дело в том что часто в начале реализации класса не всегда знаеш что в конце получится. Тут и проблеммы при компиляции в скетч если разные функции блока конфликтуют между совой (например в скоростном счетчике они могут подраться за использование единственного аппаратного таймера - горячий пример,только позавчера воевал), и приходится переделывать уже готовый функционал.Второй вопрос, после окончания работы с блоком и обкатке его на пользователях я про него забываю. Он работает, и на другие блоки не влияет. Это основная концепция проекта - по возможности исключить влияние блоков друг на друга. Важно что бы при создании нового блока ничего не происходило с существующими. Этакие сфкрические кони в вакуме. Не всегда конечно это получается, все таки некоторые блоки дерутся между собой за прерывания например, за ины контроллера, за порты и шины данных, но в большинстве случаев получается обходится малой кровью. Так же это позволяет осуществлять прямую совместимост проекта. То ест в версии 1.6.2 (вышедшей сегодня ночью) можно открыть проект версии 1. Он заработает и скомпилится. Скетчь конечно получится совершенно другой, не тот какой бы получился в версии1, но работать он будет точно так же. Ну и самое главное я не знаю какой я буду делать блок завтра. Это зависит в основном от пожеланий пользователей, наличия у меня времени, наличия в коробочке очередного шилда, да и вообще с какой ноги я встану и в каком ухе у меня зажужжит. Так что планировать какую то иерархию заранее невозможно.  Ну а насчет небесконечности реализаций, зайдите на ебай и наберите arduino shiteld, потом arduino sensors. Ну а что бы окончательно все понять наберите просто Arduino. Так что проект FLProg не будет закончен никогда. К тому - же кроме ардуинок есть еще STM, PIC, да и в конце концов ко мне уже из бауманки едет Intel Galileo. Плюс впереди Scada Sistem.Правда в каком порядке это все будет реализовываться и как будет в итоге выглядеть я не имею ни малейшего представления. Как тут планировать архитектуру? Есть общая идеология, есть какие то базовые классы интерфейса которых я стараюсь придерживаться. Но не более.

вторник, 13 января 2015 г., 15:04:11 UTC+5 пользователь Kleptsov Nikolay написал:

Nikolay Kleptsov

unread,
Jan 13, 2015, 5:41:51 AM1/13/15
to Russian Smalltalk User Group
Может попробовать прототипы

13 января 2015 г., 16:28 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

Сергей Глушенко

unread,
Jan 13, 2015, 5:48:02 AM1/13/15
to su...@googlegroups.com
Честно говоря не знаком с понятием прототипы. Что то слышал, но в голове не отложилось. Я все таки не профессиональный программист  инженер электронщик и не имею фундаментального программистского образования. Любитель одним словом

вторник, 13 января 2015 г., 15:41:51 UTC+5 пользователь Kleptsov Nikolay написал:

Dennis Schetinin

unread,
Jan 13, 2015, 6:02:05 AM1/13/15
to Russian Smalltalk User Group
И вся эта логика на if-ах — я правильно понимаю?


--

Best regards,


Dennis Schetinin


13 января 2015 г., 10:29 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

Сергей Глушенко

unread,
Jan 13, 2015, 6:28:41 AM1/13/15
to su...@googlegroups.com
Не вся конечно. Классы имеют свое поведение. Умеют рисоваться, каждый тип данных (Boolean, Float, Integer ...) Это отдельный класс со своим поведением. Назначение блока задает его тим. Блок это просто хранитель данных. Его тип - его описатель. В библиотеке элементов лежат типы на основании которых создаются инстансы блоков. Переменные - это то же блок, внури которых лежит тэг. в теге лежит тип, ну и еще кое что необходимое. Поведение блока задается комбинацией содержимого. Например если значение какгого то параметра может быть константой или задаваться со входа  то в зависимости от выбора пользователя в переменной value лежит инстанс класса  InputValue или класса ConstantValue. и кода компилятор спросит у блока значение этой величины, один инстанс отдаст значение логической стоки для входа, а другой значение хранящейся в нем константы. Ну где то так только немного посложнее. Это сильное упрощение. Например константа может быть разной размерности Например: микросекунды, милисекунды, минуты-секунды-милисекунды. А в скетче нужны микросекунда. Соответственно инстанс все пересчитает. И так далее. Я знаю что это называется композиция, но конствнты находятся в иерархии с головой ParametrValue. И эти классы применяются при необходимости во всех классах. If-ы конечно есть, от них никуда не денешся. И я не считаю злом их использование. Если где то можно обойтись небольшими количеством If-ов не создавая лишний класс, я не стесняюсь их использовать. Главное что бы не было большого вложения. Если начинаются вложения, лучше сделать отдельный метод с выходами по условиям. Получается красиво и и наглядно.
Например метод проверки корректности модуля скоростного счетчика:

isCorrect

   
super isCorrect ifFalse: [^false].
    pinNumber ifNil
: [^false].
   
self isUseCountInTimeFunction ifTrue: [countTime ifNil: [^false]].
   
self isUsePulseDurationFunction
        ifTrue
:
           
[(self hasPositivDurationOutput or: [self hasNegativeDurationOutput])
                ifFalse
: [^false]].
   
self isUseDelayLineFunction
        ifTrue
:
           
[self hasDelayTimeInput ifFalse: [self delayTimeConstant ifNil: [^false]].
           
self hasDestinationOutput
                ifFalse
: [self controllerDestinationpinNumber ifNil: [^false]]].
   
^true
 P.S. Не ругайте за английский, я в нем не силен


вторник, 13 января 2015 г., 16:02:05 UTC+5 пользователь chaetal написал:

Dennis Schetinin

unread,
Jan 13, 2015, 7:00:22 AM1/13/15
to Russian Smalltalk User Group
А можно увидеть наиболее типичные реализации методов isUse…, has…?


--

Best regards,


Dennis Schetinin


13 января 2015 г., 15:28 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

Nikolay Kleptsov

unread,
Jan 13, 2015, 7:01:59 AM1/13/15
to Russian Smalltalk User Group
если смотреть только на один метод, могу и ошибаться,
isUseCountInTimeFunction и countTime выделить в отдельную сущность
также относиться и к
- (isUsePulseDurationFunction hasPositivDurationOutput hasNegativeDurationOutput)
-
(isUseDelayLineFunction hasDelayTimeInput delayTimeConstant hasDestinationOutput controllerDestinationpinNumber controllerDestinationpinNumber)

в этих объектах (сущностях) реализовать метод isCorrect или использовать callback методы (например, сложение разных типов чисел) где сначала идет общий метод сложения, далее у разных типов чисел через уточняющие методы происходит сложение

13 января 2015 г., 17:28 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

Сергей Глушенко

unread,
Jan 13, 2015, 7:04:07 AM1/13/15
to su...@googlegroups.com
Конечно

isUseCountInTimeFunction

    isUseCountInTimeFunction ifNotNil
: [^isUseCountInTimeFunction].
    isUseCountInTimeFunction
:= true.
   
^isUseCountInTimeFunction


isNeededPositiveInterruptFunction

   
self hasInputValueOutput ifTrue: [^true].
   
self isUseDelayLineFunction
        ifTrue
: [self hasDelayPositiveFront ifTrue: [^true]].
   
self isUsePulseDurationFunction ifTrue: [^true].
   
(self isUseCountPulseFunction and: [self isUsePositiveFrontForPulseCount])
        ifTrue
: [^true].
   
^false

isSpeedCounter

   
^true

hasDestinationOutput

   
^destinationOutput isNil not

canConnectOutput
: aArduinoFBDInputOutput to: aArduinoFBDConnection
aArduinoFBDInputOutput isBooleanType ifTrue
:[^aArduinoFBDConnection isBooleanType].
   
^aArduinoFBDConnection isNumberType


ну я так пробежался по тестовым методам. там их много

вторник, 13 января 2015 г., 17:00:22 UTC+5 пользователь chaetal написал:

Dennis Schetinin

unread,
Jan 13, 2015, 7:14:25 AM1/13/15
to Russian Smalltalk User Group
Вообще, напрашивается что-то вроде

^ elements anySatisfy: [:each | each isEffective and: [each isCorrect not]]) 


--

Best regards,


Dennis Schetinin


13 января 2015 г., 16:01 пользователь Nikolay Kleptsov <kleptsov...@gmail.com> написал:

Сергей Глушенко

unread,
Jan 13, 2015, 7:16:48 AM1/13/15
to su...@googlegroups.com
Вопрос на засыпку. А зачем?  я же и говорю, что не стесняюсь использовать if там где это оправданно. Такая связка в виде сущности кроме этого блока нигде не понадобится. Никогда. Зачем усложнять объект, создавая ненужную сущность. Почему Вы истинные программисты так боитесь тех же If -ов. Зачем на каждый чих создавать новый класс. Мой инженерный подход говорит мне что любую задачу можно решить милионом путей от безумно красивого но долгого до абсолютно костыльного но очень быстрого, а исскуство инженера - конструкторас найти золотую середину в решении каждой задачи, используя компромисы, и различные подходы. Вас же почему то тянет всегда на первый путь. Я в свое время был неплохим конструктором.
вторник, 13 января 2015 г., 17:01:59 UTC+5 пользователь Kleptsov Nikolay написал:

Dennis Schetinin

unread,
Jan 13, 2015, 7:17:42 AM1/13/15
to Russian Smalltalk User Group
В общем-то, здесь все на if-ах и сделано :)


--

Best regards,


Dennis Schetinin


13 января 2015 г., 16:04 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

Сергей Глушенко

unread,
Jan 13, 2015, 7:23:24 AM1/13/15
to su...@googlegroups.com

Попробую еще раз объяснить. Тут  нет необходимости создавать сущности. Нигде кроме данного класа они не потребуются. Я считаю класс нужно создавать только с целью его переиспользования, или если  его применеие действительно облегчит жизнь. Я не вижу облекчения жизни в создании коллекции элементов состоящих из двух - трех переменных и одного метода. Лично для меня  нагляднее примененный мной метод. Я сразу вижу все условия корректности модуля. Вложенности более одного уровня нет. Не надо плодить ненужные сущности. Они загромождают класс браузер, и мешаются по ногами эти мелкие твари.
вторник, 13 января 2015 г., 17:14:25 UTC+5 пользователь chaetal написал:

Dennis Schetinin

unread,
Jan 13, 2015, 7:24:44 AM1/13/15
to Russian Smalltalk User Group

13 января 2015 г., 16:16 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

Вопрос на засыпку. А зачем?

У меня есть штук 5 вариантов ответа на этот вопрос, но наверное будет некорректно влезать перед автором рац. предложения :)

Еще вопрос: метод isCorrect и подобные (я, так понимаю, они есть) с развитием системы уже никогда не будут больше меняться?

Dennis Schetinin

unread,
Jan 13, 2015, 7:27:09 AM1/13/15
to Russian Smalltalk User Group

13 января 2015 г., 16:23 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

Нигде кроме данного класа они не потребуются.

Ну, это странно. Если они нигде не требуются, кого волнует корректны они или нет?

Сергей Глушенко

unread,
Jan 13, 2015, 7:52:26 AM1/13/15
to su...@googlegroups.com
В данном блоке 80% нет. Точнее этот блок имеет законченную функциональность.  Соответственно саь блок меняться не будет. Максимум что может у него поменяться - добавятся интерфейсы ну например выдача значения константы какой ни будь с пересчетом например в шестнадцетиричную если это потребуется компилятору например под PIC/
Я понимаю что Вы хотели увидеть в тестовом методе

isCorrect

   
super isCorrect ifFalse: [^false].

    connectionDevice ifNil
: [^false].
    connectionDevice isCorrect ifFalse
: [^false].
   
^true



Это блок WebServer Здесь девайсом могут выстумать несколко различных шидлдов. Типа вайфай, W5100 и другие. На сегодня их три вида. Кроме этого блока данные классы являющиеся моделями устройства применяются еще в нескольких видах блоков, и количество блоков с применением этих моделей будет расти, как и количество самих моделей. Только WIFI модулей под ардуинку видов десять. И каждый работает по своему, Каждый имеет свой набор параметров. Блоки их использующие меняться больше не будут. Им глубоко плевать какую железяку в них засунули. Они хранят информацию о странице которую нуэно отправить на запрос к серверу. Каждая железяка знает о своем SubFrame  с набором параметров для себя. Соответственно описанная и отлаженная железяка то же менятся больше не будет. Она умеет дернуть свой кусок компилятора создающего код обеспечивающий ее работу. Соответственно кусок кода компилятора(метод или набор методов) отвечающий за ее программирование то же отладится один раз и забудется. При появлении у меня новой железяки просто будет написан новый класс модели железяки с тс общим интерфейсом. и таких железяк у меня уже много
AbstractExtendDevice
   
AbstractDisplay
       
DisplayOnChipHD44780
   
StepMotor
   
AbstractRealTimeClock
        DS1302RealTimeClock
        DS1307RealTimeClock
   
ServoMotor
   
UltrasonicHC_SR04
   
DigitalTemperatureAndHumidity
   
OneWire
    DS18x2x
   
IRRessiver
    BMP085
   
SevenSigmentIndicatorStatic
   
SevenSigmentIndicatorDinamic
   
Chip74HC595
   
EthernetDevice
       
EthernetShild
            ENC28J60EthernetShild
            W5100EthernetShild
        ESP8266WiFiModule
   
SDCardReader



Так что поверте что с композицией я то же умею работать. Просто нельзя зацикливаться на одном патерне, а в каждом конкретном случае надо находить компромис, приносящий оптимальный результат. Кстати как я и говорил раньще если классы тех же блоков отлаживаются один раз и забываются, то и тестировать их нет необходимости. То же касается и моделей железа





вторник, 13 января 2015 г., 17:24:44 UTC+5 пользователь chaetal написал:

Сергей Глушенко

unread,
Jan 13, 2015, 8:00:05 AM1/13/15
to su...@googlegroups.com

Пользователь добавив новый блок параметризует его, устанавливает константы, выбирает пины. Проверяется правильность и полноценность параметризации блока. Если блок не корректен то плата на которой он установлен будет отмечена, а так же сам некорректный блок будет отмечен. Кроме того при попытке компиляции проекта  будет выданно сообщение с номерами плат с некорректными блоками и количество некорректных модулей на каждой плате. Соответственно пока весь проект не будет корректен, компиляция производится не будет.
вторник, 13 января 2015 г., 17:27:09 UTC+5 пользователь chaetal написал:

Dennis Schetinin

unread,
Jan 13, 2015, 8:18:40 AM1/13/15
to Russian Smalltalk User Group
Я тону в этой информации :)  У меня логика простая: если объект проверяется на корректность, значит где-то еще он будет использоваться. Не говоря уже о том, что где-то он был создан и сконфигурирован пользователем. Соответственно, фраза про то, что этот объект больше нигде и никем не используется вызывает определенные сомнения.


--

Best regards,


Dennis Schetinin


13 января 2015 г., 17:00 пользователь Сергей Глушенко <sup...@flprog.ru> написал:
--

Сергей Глушенко

unread,
Jan 13, 2015, 8:33:16 AM1/13/15
to su...@googlegroups.com
Наверное я просто плохо бъяснил. Попробую так. Когда я говорю что класс не будет менятся, я имею в виду что не будет менятся его программный код. А isCorrect используется в жизни инстанся при его эксплуатации так сазать. ТО есть когда пользователь нажимает кнопку  "Проверить проект"  выполняется код:

checkProject

   
| errors stream |
    projectFrame ifNil
: [^self].
    errors
:= self currentProject errors.
    errors isEmpty ifTrue
: [^Dialog rusButtonWarn: 'Проект корректен'].
    stream
:= String new writeStream.
    errors
do: [:each | stream nextPutAll: each]
        separatedBy
: [stream nextPut: Character cr].
   
Dialog rusButtonWarn: stream contents



соответственно у проекта
errors

   
^programm errors

 у программы
errors

   
| result |
    result
:= OrderedCollection new.
    networks doWithIndex
:
           
[:each :idx |
            result
                addAll
: (each errors collect:
                           
[:eachError |
                            eachError
                                expandMacrosWithArguments
: (OrderedCollection with: idx printString)])].
   
^result



У платы
errors

   
| notCorrectBlocks result |
    notCorrectBlocks
:= blocks select: [:each | each isCorrect not].
    notCorrectBlocks isEmpty ifTrue
: [^#()].
    result
:= OrderedCollection new.
    result add
: 'На плате №<1s> -  ' , notCorrectBlocks size printString
               
, (notCorrectBlocks size = 1
                        ifTrue
: [' некорректный  блок']
                        ifFalse
:
                           
[notCorrectBlocks size > 4
                                ifTrue
: [' некорректных блоков']
                                ifFalse
: [' некорректных блока']]).
   
^result


вот мы и пришли к тому самому  isCorrect у блока.
конечно у блока то же можно спросить  errors, но мне честно говоря пока не хочется описывать текстом все ошибки параметризации блоков. Будет куча свободного времени займусь этим. Пока пользователи не просят - значит и не надо.





вторник, 13 января 2015 г., 18:18:40 UTC+5 пользователь chaetal написал:

Dennis Schetinin

unread,
Jan 13, 2015, 9:38:26 AM1/13/15
to Russian Smalltalk User Group
Есть блок. Он корректен. Пользователь установил isUseCountInTimeFunction в false. Где-то это значение будет использовано? Там будет код типа:

block isUseCountInTimeFunction ifTrue: [...] ifFalse: […]

Так?




--

Best regards,


Dennis Schetinin


13 января 2015 г., 17:33 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

Сергей Глушенко

unread,
Jan 13, 2015, 9:44:39 AM1/13/15
to su...@googlegroups.com
Совершенно верно
конкретно так

negativeFrontInterrupFunctionForCountInTimeFunctionWithBlock: aBlock

   
| stream |
    aBlock isUseCountInTimeFunction ifFalse
: [^''].

    stream
:= String new writeStream.

    stream
        nextPutAll
: aBlock declaringImpulseCountName;
        nextPutAll
: '++;';
        nextPut
: Character cr.
   
^stream contents

Это компилятор. ну и естественно на тот метод смотрин холдер  чекбокса на фрейме


вторник, 13 января 2015 г., 19:38:26 UTC+5 пользователь chaetal написал:

Сергей Глушенко

unread,
Jan 13, 2015, 10:00:51 AM1/13/15
to su...@googlegroups.com
Ну все для меня на сегодня дебаты закончены. Смена кончилась - поехал в общагу. Там интернет мобильный, то есть никакой. Почитать Ваши сообщения смогу, отвечу утром .

вторник, 13 января 2015 г., 19:44:39 UTC+5 пользователь Сергей Глушенко написал:

Dennis Schetinin

unread,
Jan 13, 2015, 10:16:14 AM1/13/15
to Russian Smalltalk User Group

13 января 2015 г., 19:00 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

Ну все для меня на сегодня дебаты закончены.

"Вот так всегда: на самом интересном месте!" (с)


Но мы, собственно, почти закончили — нашли сущность, имеющую следующую функциональность:
- показывает себя и позволяет пользователю задавать свое состояние
- проверяет корректность своего состояния
- "компилирует" себя

В общем-то, это все стандартно. Сейчас эта функциональность (опять же, стандартным образом) "размазана". Это приводит к многократному дублированию. О последствиях этого уже много написано в умных книгах умными людьми. …Но если есть желание испытать это на собственном опыте до/после/вместо прочтения этих самых книг — никто помешать не сможет :)

Сергей Глушенко

unread,
Jan 14, 2015, 1:34:58 AM1/14/15
to su...@googlegroups.com
Ну вы почти полностью описали функционал класса AbstractBlock являющегося родителем для всех остальных блоков. Каждый блок умеет правильно показываться пользователю для своей параметризации. То есть каждый блок знает свой фрейм параметризации и является для него моделью. Он сам рисуется на нужном месте (у него реализован метод displayOn:.  У некоторых блоков в иерархии он перекрыт если нужна нестандартная отрисовка). Каждый блок контролирует корректность своего заполнеия. Все совершенно академически. А где Вы увидали размазаность?
Единственное место где она возможна, это компиляция. Блок не умеет сам компилироваться. Это сделанно сознательно. Проект изначально создавался под возможность подключения различных компиляторов. Поэтому блоки ничего не знают о компиляторе, а только умеют правильно отвечать на его вопросы. Дело в том что в принципе все контроллеры на сегодняшний день программируются либо на С либо на его переделанных вариантах. Язык Wiring который используется в Ардуино - это тот же С с добавление синтаксического сахара и с некоторыми встроенными специализированными функциями которых нет в чистом С.  Соответственно базовый синтаксис у всех одинаковый. О вещах которые связаны с базовым синтаксисом блоки знают. Но с другой стороны структура программы, использование библиотек , специлизированные функциии отличаются не только для разных микроконтроллеров, но и даже для различных плат ардуино. О все этом знает компилятор. Поэтому при прибавлении поддержки нового контроллера мне необходимо только добавить новый класс компилятора и использовать его при компиляции. О необходимом классе компилятора знает модель - описатель контроллера которая хранится в проекте. При необходимости единственное что может потребоваться от блоков - дописать дополнительные интерфейсы для ответа новому компилятору если существующих интерфейсов будет не хватать. Как раз скоро мне предстоит опробовать данную систему в боевых условиях добавления поддержки принципиально новых контроллеров. Санкт-Петербургский Государственный университет Телекоммуникаций им. проф. М.А. Бонч-Бруевича предоставил мне для работы плату Intel Galileo, а один небольшой российский стартап попросил рассмотреть возможность применения FLProg в качестве основной среды для разрабатывания. Там вообще PIC. Ну наверное больше о компиляторе рассказывать ничего не буде.Все таки компилятор это мое ноу - хау
вторник, 13 января 2015 г., 20:16:14 UTC+5 пользователь chaetal написал:

Dennis Schetinin

unread,
Jan 14, 2015, 2:14:37 AM1/14/15
to Russian Smalltalk User Group

14 января 2015 г., 10:34 пользователь Сергей Глушенко <sup...@flprog.ru> написал:

А где Вы увидали размазаность?

Внутри блока я увидел компоненты с указанной функциональностью. (Если она совпадает с функциональностью всего блока — есть повод задуматься.) Только в отличие от блока эта функциональность реализована неявно и не в этих компонентах, а в методах других классов (блоков и м.б. чего-то еще?).

Из-за этой размазанности как раз возникают и проблемы с пониманием типа "а как и что тут тестировать?!". Очень знакомая ситуация, на самом деле.

Сергей Глушенко

unread,
Jan 14, 2015, 2:55:50 AM1/14/15
to su...@googlegroups.com
Естественно внутри блока есть другие сущности которые то же умеют все это делать. Например входы отрисовывают себя сами в указанном ими блоком  месте. Устройства лежащие внутри некоторых блоков имеют свои субфреймы позволяющие их парамерировать. В этом случае блок просто спрашивает у устройства какой фрейм отрисовать на соответвующей субканве создает данный фрейм и отдаее ему устройство в качестве модели. Так же устройство само тестирует правильность своей параметризации. Естественно одноименный функционал разных сущностей не дублирует друг друга. Выход рисуется не так как блок, ну а устройство имеет совершенно другой набор бараметров чем блок. Да и разные устройства которые могут присутствовать в одном блоке чаще всего отличаются набором параметров а иногда и поведением. Например EthernetShild и WifiShield абсолютно отличаются друг от друга, но при этом на базе каждого из них можно построить вэб сервер или вэб клиент. Получается что каждый класс имеет свою собственную законченную функциональность. Я опять не вижу размазанности. Каждый из них знает и умеет то что ему нужно и ничего лишнего. Например зачем блоку знать IP адрес карты на котором установлен сервер. Если его об этом спросит компилятор он спросит об этом устройство.

среда, 14 января 2015 г., 12:14:37 UTC+5 пользователь chaetal написал:

Сергей Глушенко

unread,
Jan 14, 2015, 3:06:59 AM1/14/15
to su...@googlegroups.com
Я тут немного оговорился и что бы избежать ненужных воросов сразу поправлюсь.

блок просто спрашивает у устройства какой фрейм отрисовать на соответвующей субканве создает данный фрейм и отдаее ему устройство в качестве модели

немного не так. Блок не занимается отрисовкой фрейма. Конечно же фрейм блока  знает о своих субфреймах и спрашивает у блока  устройство. И фрейм - же создает субфрейм кладет себе в переменную и передает взятое у блоке устройство субфрейму в качестве модели.

среда, 14 января 2015 г., 12:55:50 UTC+5 пользователь Сергей Глушенко написал:
Reply all
Reply to author
Forward
0 new messages