Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Инкапсуляция

87
27 января 2011 года
Kogrom
2.7K / / 02.02.2008
Уже обсуждали циклы, шаблоны. Возможно, кому-то будет интересна тема инкапсуляции. Тут я буду говорить именно о той, которая определяется синтаксисом, ключевыми словами типа private, protected и т.п. Кто что про неё думает? Нужна ли она вообще?

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

Но и с данными должна быть некая условная инкапсуляция. То есть должен быть не полный запрет, а генерирование предупреждений компилятором (интерпретатором) в случае их использования. Такой подход может пригодиться при рефакторингах.
Страницы:
87
28 января 2011 года
Kogrom
2.7K / / 02.02.2008
Цитата: koodeer
Но всё-таки создаётся впечатление, что весь сыр-бор с инкапсуляцией и тестированием вызван как раз динамической природой используемого тобой языка.



Не знаю, почему создаётся такое впечатление. На самом деле пытаюсь в разговоре осознать некоторые мысли, которые пришли мне в голову, когда я читал вот эту книгу:
http://www.williamspublishing.com/Books/978-5-8459-1530-6.html

В ней не написано прямо так, как я говорю, но её текст навёл меня на такие мысли про инкапсуляцию. При том, что в книге все примеры на языках со статической типизацией.

А далее я вспомнил, что в Smalltalk все данные являются частными, а все методы публичными. Ну и понесло...

3
28 января 2011 года
Green
4.8K / / 20.01.2000
Цитата: Kogrom
Согласен. Вполне достаточно данных для того, чтобы сказать, что пример неудачен.

Зачем городить класс там, где можно обойтись методом, тем более что у него большой потенциал повторного использования? Велика вероятность, что и другим классам могут потребоваться функции calcArithmeticMean, calcSum. Более того, sum (accumulate) есть в стандартных библиотеках известных мне языков, что подтверждает мысль о повторном использовании.



Нормальный пример, только ты не пытаешься рассмотреть его локально. И не поняв сути примера, говоришь удачный он или нет.

Ты зачем то пытаешься представить, что вокруг этого класса. Зачем такой класс существует - в данном случае не важно. Вопрос был: Класс стал "выполнять не свои обязанности"?
Изменились обязанности этого класса между примерами, если его реализация осталась неизменной?

Ок я изменю пример.
Так было и так надо, не надо пытаться понять место этого класса во Вселенной:

 
Код:
void Class::Method()
{
   some_do;
   some_do;
   some_do;
   some_do;
   some_do;
}

а теперь чисто для своих внутренних целей, я сделал так:
Код:
void Class::Method()
{
   some_do;
   some_do;
   private_method();
}

void Class::private_method()
{
   some_do;
   some_do;
   some_do;
}

а теперь вопрос: Класс стал "выполнять не свои обязанности"? Если поведение и реализация не изменились?
63
28 января 2011 года
Zorkus
2.6K / / 04.11.2006
Если честно, ваш холивар необыкновенно скучен и уныл (как и почти все холивары на тему TDD).

А причина в том, что кто-то уделяет этому чересчур много внимания и сил.

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

Будьте гибче и проще (это я ни к кому конкретно не обращаюсь).
Смыслов от тестов на практике два явных (есть и еще).

1) Убеждение, что код - bug-free.
2) Убеждение, что интерфейс кода юзабелен настолько, чтобы его собственный автор мог написать простейшего клиента (а тест есть не что иное, как простейший клиент метода, класса или модуля) быстро и без проблем.

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

- Время на написание тестов всегда ограничено. Очевидно, если вы тратите два дня на написание кода, и еще два дня на написание юнит-теста, вы пишете свои тесты неправильно. Когда приблизиться дедлайн, вы придете к тому, что на тесты все будут забивать и писать функциональность.
- Покрытие разумно-простыми базовыми тестами всей системы лучше, чем идеальное покрытие одного куска и отсутствия тестов для трех четвертей системы вовсе.
- Тесты должны быть компактными по размеру, быстрыми в написании и модификации. В частности, тесты по принципу белого ящика по определению сложны в поддержке. Если требования меняются часто, вы будете разбираться в падениях тестов половину своего времени, пока либо не начнете писать более простые и общие тесты, либо не начнете комментировать их с пометкой "TO DO".
- Тесты должны выполнять быстро. Если полный набор тестов работает около часа локально (+ 20 минут на clean build кода), никто не будет прогонять их все перед каждым коммитом. Отсюда вывод, что надо писать больше простых тестов, и с настороженностью относиться к идее неумеренно плодить функциональные тесты, которые требует для работы развертывания тестовой схемы базы данных, или чего-то подобного. Имеет смысл использовать моки / DI для упрощения и ускорения тестов.
Или же, функциональные тесты, если их много, должны быть вынесены в какую-то отдельную процедуру функционального тестирования, которая работает скажем каждую ночь на билд-сервере.
- Тесты мало что стоят без работающей и поддерживаемой в исправном состоянии системы непрерывной интеграции.
- Есть места, которые тестировать юнит-тестами сложно. Пример - UI, код связывающий между собой слои бизнес-логики и БД. Там часто нужны или специальные тесты написанные с помощью TestComplete, Selenium или чего-то подобного для UI, или спец фреймворки для моделирования "тестовой сцены" в базе данных.

А насчет динамических языков. Я внимательно слежу за развитием Groovy (и подумываю стать контрибьютором), так вот, в последнее время в комьюнити разработчиков его наметилась тенденция, что пишутся инструменты для эвристического статического (времени компиляции) анализа кода, написанного на динамическом языке :) Примеры -
http://codenarc.sourceforge.net/
http://www.groovylint.com/

Наводит на мысль, что в динамических языках проблема тестов вызывает больше фрустрации :)
87
28 января 2011 года
Kogrom
2.7K / / 02.02.2008
Цитата: Green
Ок я изменю пример.
Так было и так надо, не надо пытаться понять место этого класса во Вселенной


Если я не буду пытаться понять "место класса во вселенной", то получу обыкновенное процедурное программирование, замаскированное под объектно-ориентированное. Вот такое у меня странное понимание ООП.

87
28 января 2011 года
Kogrom
2.7K / / 02.02.2008
Zorkus, спасибо, что пересказал кратко суть юнит-тестирования.
Цитата: Zorkus
А насчет динамических языков. Я внимательно слежу за развитием Groovy (и подумываю стать контрибьютором), так вот, в последнее время в комьюнити разработчиков его наметилась тенденция, что пишутся инструменты для эвристического статического (времени компиляции) анализа кода, написанного на динамическом языке :) Примеры -
http://codenarc.sourceforge.net/
http://www.groovylint.com/

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


И в Python есть подобные средства. Но есть подозрения, что эти инструменты разрабатывают люди, которые до этого программировали на языках со статической типизацией :)
С другой стороны, лишняя проверка не мешает.

А если говорить на счёт тестов, то в языках с динамической типизацией обычно проще использовать mock-объекты и fake-объекты, то есть меньше препятствий для реализации тестирования.

3
28 января 2011 года
Green
4.8K / / 20.01.2000
Цитата: Kogrom

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


Ого... все ещё хуже... :) Т.е. такая матрешка. А где будут храниться данные? В каком классе?
И ещё не понятно, где заканчиваются открытая реализация и начинается закрытая?
Я уже приводил пример: если я вместо макарон в открытом методе сделаю несколько вызовов приватных методов, уже надо разносить такой код по классам? Зачем?

Цитата: Kogrom

Не совсем так. Класс-помощник будет независим от главного.


Тогда возвращаемся к уже заданному вопросу, а как этот клас "помощник" будет оперировать с приватными данными главного класса?
Они будет передаваться в конструкторе? Кем будут передаваться? Конструктором главного класса? А где будут храниться в вспомогательном классе в его личных полях? Да хоть пусть и по ссылкам.
Т.е. вспомогательный класс жестко зависит от основного, т.к. оперирует его данными. А основной класс зависит от вспомогательного, т.к. тот оперирует его данными. Думаю, не надо объяснять, к чему ведет такое перекрестное связывание?

А при тестах что будем тестировать? Основной класс или вспомогательный?
И тот и другой? Причем в отрыве?
Тогда получается, что класс помощник - самостоятельный класс, только зависит от внешних ресурсов, содержит методы манипуляции не своими данными. А как же тогда инкапсуляция? Или она действительна только для белых, а не для негров-помощников?

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

Или при изменении основного класса я должен изменить помощьника и его тесты? А как это зафиксировать? Видимо, только документации.
И как же "Класс-помощник будет независим от главного"?

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

Юнит-тесты ради которых все и затевалось потеряли смысл, т.к. они уже тестируют не юниты, а какие-то связки.

Цитата: Kogrom

У него будет потенциал использования в другом классе.


А нафига? Вот когда понадобиться в другом классе, тогда и вынесем. Только вынесем с учетом инкапсуляуции, т.е. соберем воедино поля и методы, а не разнесем их по системе.
Но при чем тут тестирование?

Цитата: Kogrom

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


Да такой код будет совершенно невозможно поддерживать.
Допустим возьмем этих 100 классов представим, что они бьются ещё класса на 4, те ещё на парочку... м-да...
А для чего все это? Для тестов? А нет... " Это устранит дублирование"

Да и при чем тут "дублирование"? Какое отношение имеет дублирование к тестированию приватных методов?

Цитата: Kogrom

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


Погоди, погоди... мы начали с юнит-тестирования, а не с декомпозиции для повторного использования. И тут опять встает впечатление, что:

Цитата: Green

У меня есть предположение, что у тебя была ситуация с некоторой плохой архитектурой класса. Где, действительно, часть реализации было правильным вынести в отдельную сущность. И у тебя сложилось мнение, что это и есть "универсальная таблЭтка", "серебряная пуля" для приватных членов.
Только в этом случае вынесение реализации было скорее всего делом логичной архитектура, а не необходимостью тестирования.



Т.е. твои выводы не имеют никакого отношения к тестированию привата.
Они имеют отношение к повторному использованию кода. И здесь америки нет - одинаковый код лучше вынести в отдельную сущность.

Но и здесь хочу предостеречь: не надо наворачивать систему основываясь на тотальном переиспользовании кода. Когда это потребуется, тогда и зарефакториться.
Реюз - один из видов оптимизации, а преждевременная реализация, как известно...
Но опять же, это не имеет никакого отношения к тестированию, о котором в начале говорилось.

3
28 января 2011 года
Green
4.8K / / 20.01.2000
Цитата: Kogrom
Если я не буду пытаться понять "место класса во вселенной", то получу обыкновенное процедурное программирование, замаскированное под объектно-ориентированное. Вот такое у меня странное понимание ООП.


Не получишь. Как раз наоборот ООП предполагает разбиение вселенной на отдельные законченные фрагменты. :)

3
28 января 2011 года
Green
4.8K / / 20.01.2000
Цитата: Zorkus
Если честно, ваш холивар необыкновенно скучен и уныл (как и почти все холивары на тему TDD).


Да нет тут никакого холивара на тему TDD. :)

63
28 января 2011 года
Zorkus
2.6K / / 04.11.2006
Цитата: Kogrom
Zorkus, спасибо, что пересказал кратко суть юнит-тестирования.


Именно. Но ты не поверишь, сколько людей понимает юнит-тестирование как попытки окружить систему какими то тяжелыми, неуклюжими, ломающимися лесами, за которыми самой системы не видно.

Цитата: Kogrom

И в Python есть подобные средства. Но есть подозрения, что эти инструменты разрабатывают люди, которые до этого программировали на языках со статической типизацией :)
С другой стороны, лишняя проверка не мешает.


Логично, это попытка преодолеть объективный недостаток динамической типизации.

Цитата: Kogrom

А если говорить на счёт тестов, то в языках с динамической типизацией обычно проще использовать mock-объекты и fake-объекты, то есть меньше препятствий для реализации тестирования.


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

276
28 января 2011 года
Rebbit
1.1K / / 01.08.2005
А чего ви собственно так тестов ухватились то?
Осмелюсь напомнить что програма пишется не для того чтоб под нее тесты сделать. а для того чтоб ее продать.
Чтоб ее ктото захотел купить она должна быть дешевой и бисто разрабатываться.
Тести нужни для того чтоб ее не поломать если придется ее долго сапортить и изменять.
Если ее придется долго сапортить и изменять то она и без того сложная, мелденно реализируемая и дорогая.

Зачем ее тогда еще больше усложнять тисячами служебних класов? Чтоб было красиво? Вам не кажется что ето не красиво, а смешно?
87
28 января 2011 года
Kogrom
2.7K / / 02.02.2008
Цитата: Green
Ого... все ещё хуже... :) Т.е. такая матрешка. А где будут храниться данные? В каком классе?



Какая матрешка? Если уж нужна метафора, то тут будет некое дерево: главный объект оперирует объектами поменьше, те - ещё меньшими и т.д. до элементарных объектов. Аналогично ствол дерева разделяется на ветви. Данные главного объекта - это те самые объекты-"помощники" и есть. Зачем же главному классу хранить какие-то другие данные?

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

87
28 января 2011 года
Kogrom
2.7K / / 02.02.2008
Цитата: Zorkus
А вот тестировать код написанный на динамических языках не проще. Потому что надо проверят то, что в статических проверяет компилятор.


Зачем? В языках со строгой динамической типизацией будет достаточно просто запустить код на выполнение. Язык сам проверит.

Цитата: Rebbit
Зачем ее тогда еще больше усложнять тисячами служебних класов? Чтоб было красиво? Вам не кажется что ето не красиво, а смешно?


Смешно, конечно. Если воспринимать классы как модули-мутанты, то все эти разговоры смешны.

297
28 января 2011 года
koodeer
1.2K / / 02.05.2009
Kogrom, тебя невозможно понять, пока не приведёшь пример кода своей концепции.
Выкладывай кратенький код, в котором будет главный класс с публичными методами и частными полями, и класс-помощник.
276
29 января 2011 года
Rebbit
1.1K / / 01.08.2005
Цитата: Kogrom

Смешно, конечно. Если воспринимать классы как модули-мутанты, то все эти разговоры смешны.


Чтото я не до конца понял что такое модули-мутанты?

Как по мне то основная задача класа - отображение реальных сущостей в логике програмы. Скажем, это некие роботники сотрудники фирмы "програма".

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

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

63
29 января 2011 года
Zorkus
2.6K / / 04.11.2006
Цитата: Kogrom
Зачем? В языках со строгой динамической типизацией будет достаточно просто запустить код на выполнение. Язык сам проверит.


Отнюдь. Язык не сможет найти такие мелкие ошибки, как плюс вместо минуса в формуле. И еще миллион других.

Цитата: Kogrom

Смешно, конечно. Если воспринимать классы как модули-мутанты, то все эти разговоры смешны.


Мне смешны они по другой причине. У меня ощущение (субъективное, разумеется), что ты воспринимаешь некоторые вещи чересчур академично, что ли. Т.е. слишком буквально воспринимаешь некоторые положения популярных концепций и не рассматриваешь целесообразность компромиссов тут и там.

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

63
29 января 2011 года
Zorkus
2.6K / / 04.11.2006
Уточню свою мысль в отдельном посте. Я рекомендую посмотреть на концепции IoC и Dependency Injection и их реализации в популярных языках. Это улучшит тестируемость кода больше, чем рассуждения о том, где надо и не надо создавать вспомогательные классы для хранения чего-то там.
3
29 января 2011 года
Green
4.8K / / 20.01.2000
Цитата: Kogrom
Какая матрешка? Если уж нужна метафора, то тут будет некое дерево: главный объект оперирует объектами поменьше, те - ещё меньшими и т.д. до элементарных объектов. Аналогично ствол дерева разделяется на ветви. Данные главного объекта - это те самые объекты-"помощники" и есть. Зачем же главному классу хранить какие-то другие данные?


Матрешка - частный случай дерева.
Я так и не понимаю ДЛЯ ЧЕГО УСЛОЖНЯТЬ простые вещи?

Цитата: Kogrom

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


Что ж, будем ждать... :)

Только ты не ответил на мои вопросы про декомпозицию и её связи с юнит-тестированием.
Возможно, ответив на них (в первую очередь себе, а не мне) момент понимания мудрости приблизится... :)

276
29 января 2011 года
Rebbit
1.1K / / 01.08.2005
Цитата: Zorkus
Уточню свою мысль в отдельном посте. Я рекомендую посмотреть на концепции IoC и Dependency Injection и их реализации в популярных языках. Это улучшит тестируемость кода больше, чем рассуждения о том, где надо и не надо создавать вспомогательные классы для хранения чего-то там.


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

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

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

3
29 января 2011 года
Green
4.8K / / 20.01.2000
Цитата: koodeer
Kogrom, тебя невозможно понять, пока не приведёшь пример кода своей концепции.
Выкладывай кратенький код, в котором будет главный класс с публичными методами и частными полями, и класс-помощник.


+1
а ещё лучше взять уже существующий общеизвестный класс с приватными членами и переделать его в соот-вии с предлагаемой концепцией.

5
29 января 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: Kogrom
Зачем? В языках со строгой динамической типизацией будет достаточно просто запустить код на выполнение. Язык сам проверит.

Все пути исполнения программы? ;)

5
29 января 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: Green
+1
а ещё лучше взять уже существующий общеизвестный класс с приватными членами и переделать его в соот-вии с предлагаемой концепцией.


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

241
29 января 2011 года
Sanila_san
1.6K / / 07.06.2005
Ага. Мне, в свою очередь, просто не совсем понятно, зачем городить матрёшку вида
 
Код:
void class Wrapper()
{
    class Insider()
        public int DoSomethig()
        {
        //do something
        };
}

когда можно написать сразу
 
Код:
void class Wrapper()
{
        private int DoSomethig()
        {
        //do something
        };
}


Какое реальное премущество первого кода перед вторым?
3
29 января 2011 года
Green
4.8K / / 20.01.2000
Цитата: hardcase
Есть мысль, что отднократно используемые приватные методы в классах возникают в языках, которые не поддерживают локальных функций. В хорошо написанном коде на Nemerle однократно используемых приватных методов просто нет.



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

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

63
29 января 2011 года
Zorkus
2.6K / / 04.11.2006
Цитата: hardcase
Все пути исполнения программы? ;)


Ты лучше меня знаком с концепциями языков программирования и компиляторов :) Скажи - в языках типа Nemerle в простой программе, можно построить такую систему типов, которая делает так, что если код компилируется, то он корректен? Я почему-то думаю что нет.

5
29 января 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: Zorkus
Ты лучше меня знаком с концепциями языков программирования и компиляторов :) Скажи - в языках типа Nemerle в простой программе, можно построить такую систему типов, которая делает так, что если код компилируется, то он корректен? Я почему-то думаю что нет.

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

87
29 января 2011 года
Kogrom
2.7K / / 02.02.2008
Цитата: koodeer
Kogrom, тебя невозможно понять, пока не приведёшь пример кода своей концепции.
Выкладывай кратенький код, в котором будет главный класс с публичными методами и частными полями, и класс-помощник.


Цитата: Green
+1
а ещё лучше взять уже существующий общеизвестный класс с приватными членами и переделать его в соот-вии с предлагаемой концепцией.


Это не будет чистый эксперимент - я могу подобрать примеры подтверждающие мою идею. Будет вернее, если я попытаюсь проделать такой фокус с классами, которые вы предложите. Думаю, это вам тоже ничего не докажет, но для меня будет очередным упражнением :)

Цитата: Zorkus
Отнюдь. Язык не сможет найти такие мелкие ошибки, как плюс вместо минуса в формуле.


Ну так разве это не относится и к языкам со статической типизацией?

Цитата: hardcase
Все пути исполнения программы? ;)


Вот это уже правильный вопрос. Могу рассмотреть подробнее.

Пример 1.

 
Код:
if a != 99:
    do_something()
else:
    do_armageddon()

Тут нет разницы.

Пример 2.
 
Код:
if a != 99:
    do_something()
else:
    do_something_with_int(my_string)

Пока a != 99 такую ситуацию пропустят все интерпретируемые языки (пусть даже и со статической типизацией), а также компилируемые языки с нестрогой статической типизацией (те пропустят и когда a == 99). Такую ситуацию выловит только язык со строгой статической типизацией. Я знаю hardcase, что ты это знаешь, но разбирал для остальных.

Однако, разве это говорит о том, что в языках со строгой статической типизацией не надо проверять случай, когда a == 99? А вдруг функция do_something_with_int() запустит какой-нибудь нехороший процесс?

Цитата: Sanila_san
Ага. Мне, в свою очередь, просто не совсем понятно, зачем городить матрёшку вида...


Мне тоже непонятно, потому что не знаю язык на котором твои примеры. Ещё хуже того, я не понимаю, почему некоторые путают объекты и классы.

3
30 января 2011 года
Green
4.8K / / 20.01.2000
Цитата: Kogrom
Это не будет чистый эксперимент - я могу подобрать примеры подтверждающие мою идею. Будет вернее, если я попытаюсь проделать такой фокус с классами, которые вы предложите. Думаю, это вам тоже ничего не докажет, но для меня будет очередным упражнением :)


Странно, что ты не можешь ответить на конкретно поставленные вопросы. Создается впечатление, что они идут в разрез с твоей идеей, поэтому ты их и игнорируешь.
Я повторю вопросы:

1) как этот клас "помощник" будет оперировать с приватными данными главного класса? где будут храниться во вспомогательном классе данные основного класса?
2) Они будет передаваться в конструкторе? Кем будут передаваться? Конструктором главного класса?
3) при тестах что будем тестировать? Основной класс или вспомогательный?
4) при изменении основного класса я должен изменить помощника и его тесты? А как это зафиксировать?
5) Какое отношение имеет дублирование к тестированию приватных методов?

Желательно представить любой законченный пример с тестами (пусть примитивный), чтоб понять об одном мы говорим или нет. Пофиг до чистоты эксперимента. Только пример не должен представлять логичную и явную декомпозицию, очевидную ещё на этапе проектирования. Хотелось бы увидеть вполне логичный класс с приватными методами, а потом преобразование этого класса в "матрешку" и появление соотв. тестов.
Пример должен показывать в т.ч. где будут содержаться приватные данные и как обрабатываться.
Думаю, пример лучше привести тебе, т.к. ты же представляешь о чем ведешь речь, а значит сможешь и показать.

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

87
30 января 2011 года
Kogrom
2.7K / / 02.02.2008
Цитата: Green
1) как этот клас "помощник" будет оперировать с приватными данными главного класса? где будут храниться во вспомогательном классе данные основного класса?


Во первых, не класс, а объект. Поэтому одним из приватных данных будет этот объект. Получается, что он сам себя хранит. Но это только разновидность хранения.

Цитата: Green
2) Они будет передаваться в конструкторе? Кем будут передаваться? Конструктором главного класса?


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

Цитата: Green
3) при тестах что будем тестировать? Основной класс или вспомогательный?


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

Цитата: Green
4) при изменении основного класса я должен изменить помощника и его тесты? А как это зафиксировать?


Это зависит от изменений. Вопрос из ряда "если я поменяю код в одном месте программы, то надо ли мне менять в другом". Слишком абстрактно.

Цитата: Green
5) Какое отношение имеет дублирование к тестированию приватных методов?


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

Цитата: Green
Желательно представить любой законченный пример с тестами (пусть примитивный), чтоб понять об одном мы говорим или нет.


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

Было:

Код:
class SomeClass
{
private:
    vector<double> _items;
    float _calcSum()
    {
        double sum = 0;
        for (size_t i=0; i < _items.size(); ++i)
        {
            sum += _items;
        }
        return sum;
    }
public:
    void push(double d)
    {
        _items.push_back(d);
    }

    float calcArithmeticMean()
    {
        return _calcSum() / _items.size();
    }
};

Стало:
Код:
class Summator
{
public:
    float calcSum(vector<double> &items)
    {
        double sum = 0;
        for (size_t i=0; i < items.size(); ++i)
        {
            sum += items;
        }
        return sum;
    }
};

class SomeClass
{
private:
    vector<double> _items;
public:
    void push(double d)
    {
        _items.push_back(d);
    }

    float calcArithmeticMean()
    {
        return Summator().calcSum(_items) / _items.size();
    }
};

Казалось бы, я внёс лишнюю сущность. Но теперь я смогу напрямую протестировать функцию calcSum на то, что она корректно считает сумму (а не предполагать, что она работает через тестирование calcArithmeticMean) и могу использовать объект класса Summator в другом классе. Тестов тут не привожу, но подразумевается, что прямо тестируются функции calcSum, calcArithmeticMean, косвенно push.
Цитата: Green
Свой вариант классов для эксперимента предложу позднее, если необходимость этого ещё останется.


Будет интересно.

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

6
31 января 2011 года
George
4.1K / / 05.01.2007
Kogrom, а скажи, ты пишешь код для того, чтобы получить программу или для того, чтобы его тестировать? Такое впечатление, что второе.
11
31 января 2011 года
oxotnik333
2.9K / / 03.08.2007
Kogrom, приведенный пример имхо, малость не о том, что говорилось, а именно "вложенный класс", это не отдельно написанный, а класс, находящийся в private секции родительского:
 
Код:
class SomeClass
{
private:
    class ChildSomeClass
    {
        ........
    };
   .............
};
3
31 января 2011 года
Green
4.8K / / 20.01.2000
Цитата: Kogrom

Во первых, не класс, а объект. Поэтому одним из приватных данных будет этот объект. Получается, что он сам себя хранит. Но это только разновидность хранения.

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


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

Т.е. ты предлагаешь отказаться от инкапсулирования?
Данные будут храниться в одном месте, а обрабатываться в другом.
Т.е. возвращаемся к процедурному программированию?

Цитата: Kogrom

Это зависит от изменений. Вопрос из ряда "если я поменяю код в одном месте программы, то надо ли мне менять в другом". Слишком абстрактно.


Вовсе не абстрактно.
Модульные тесты фиксируют интерфейс и поведение объекта, давая т.о. возможность изменять реализацию. Введя матрешечность тесты начинают мешать изменению реализации. Как пример, можно создать циклическую очередь на основе связанного списка, а можно на основе пула (вектора или массива). Скорее всего у такого класса будут приватные методы по добавлению/удалению элементов из контейнера. Если эти методы будут вынесены в отдельный класс, то как уже говорил, изменение в одном классе будут тянуть за собой изменения в другом, а сл-но потребуются тесты, которые охватывают оба класса.

Цитата: Kogrom

Приватные методы являются индикатором того, что класс выполняет более одной обязанности (возможно, не всегда, но об этом ниже).


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

Цитата: Kogrom

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


А зачем? Ещё раз: зачем делать то, что скорее всего никогда не понадобиться?

Цитата: Kogrom

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


Да не идиотский пример, а примитивный. Идиотским, наверное, выглядит и пример строкового класса или т.п., т.к. такие классы уже существуют "в библиотеке"?
Да и "функций вне классов" нет, т.к. мы разговариваем об инкапсуляции.

Цитата: Kogrom

Стало:
Код:
class Summator
{
public:
    float calcSum(vector<double> &items)
    {
        double sum = 0;
        for (size_t i=0; i < items.size(); ++i)
        {
            sum += items;
        }
        return sum;
    }
};

Казалось бы, я внёс лишнюю сущность. Но теперь я смогу напрямую протестировать функцию calcSum на то, что она корректно считает сумму (а не предполагать, что она работает через тестирование calcArithmeticMean) и могу использовать объект класса Summator в другом классе. Тестов тут не привожу, но подразумевается, что прямо тестируются функции calcSum, calcArithmeticMean, косвенно push.


Ну, во-первых, ты изменил мой пример, нагло подогнав его под свои цели. :D
В моем примере было иначе:

 
Код:
sum += _item.value;

а ведь могло быть и ещё чуть сложнее
 
Код:
if(_item.type == type)
    sum += _item.value;


Во-вторых, а если бы ф-ция calcSum использовала не один а с десяток приватных членов класса?
Передавал бы остальные так же? И надеялся бы, что сможешь использовать его в другом месте?

А если _items - это не вектор, а массив или список (заметь, при этом интерфейс основного класса не изменится), сколько придется изменить классов, тестов и т.п.?
87
31 января 2011 года
Kogrom
2.7K / / 02.02.2008
Цитата: George
Kogrom, а скажи, ты пишешь код для того, чтобы получить программу или для того, чтобы его тестировать?



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

Проверять всё не обязательно, да и невозможно. Но желательно иметь возможность протестировать любой конкретный метод, не меняя системы.

Цитата: Green
Т.е. ты предлагаешь отказаться от инкапсулирования?


Нет. Предлагаю изменить характер инкапсуляции.

Цитата: Green
Данные будут храниться в одном месте, а обрабатываться в другом.
Т.е. возвращаемся к процедурному программированию?


Опять нет. Данные будут обрабатываться тем, у кого обязанность обрабатывать, и храниться в том, у кого обязанность хранить. Это может быть и один объект.

Цитата: Green
Модульные тесты фиксируют интерфейс и поведение объекта, давая т.о. возможность изменять реализацию.


Есть такое мнение: "Тестирование черного ящика (когда мы намеренно игнорируем реализацию) обладает рядом преимуществ. Если мы игнорируем код, мы наблюдаем иную систему ценностей: тесты сами по себе предоставляют для нас ценность. В некоторых случаях это вполне оправданный подход, однако он отличается от TDD". Кент Бек (TDD, Питер, 2003, стр 199).

Цитата: Green
Ну вот откуда такая аксиома? ИМХО вся загвоздка именно в этом, что ты принял это утверждение за аксиому и доказываешь далее, что сторонние обязанности можно вынести в отдельную сущность.


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

Цитата: Green
А зачем? Ещё раз: зачем делать то, что скорее всего никогда не понадобиться?


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

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

Цитата: Green
Ну, во-первых, ты изменил мой пример, нагло подогнав его под свои цели. :D


Ок. Не буду трогать святое. Однако не вижу, почему я подогнал "нагло". Сделал код конкретным, вот и всё :)

Цитата: Green
Во-вторых, а если бы ф-ция calcSum использовала не один а с десяток приватных членов класса?
Передавал бы остальные так же? И надеялся бы, что сможешь использовать его в другом месте?


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

Цитата: Green
А если _items - это не вектор, а массив или список (заметь, при этом интерфейс основного класса не изменится), сколько придется изменить классов, тестов и т.п.?


А в чём тут преимущество скрытого метода? В том, что его можно не проверять? Авось сработает?

Цитата: oxotnik333
Kogrom, приведенный пример имхо, малость не о том, что говорилось, а именно "вложенный класс", это не отдельно написанный, а класс, находящийся в private секции родительского


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

5
31 января 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: Kogrom
Тут есть один вопрос - как проверить скрытые методы?

А зачем их проверять? Я одного не могу понять, зачем тестировать "кишки"? Нужно тестировать интерфейс класса - работать-то приходится именно через него.

87
31 января 2011 года
Kogrom
2.7K / / 02.02.2008
Цитата: hardcase
А зачем их проверять? Я одного не могу понять, зачем тестировать "кишки"? Нужно тестировать интерфейс класса - работать-то приходится именно через него.



В общем, я на этот вопрос отвечал в предыдущем сообщении, цитаты даже приводил... Но могу ещё раз.

Протестировать всё невозможно - об этом у Макконнелла доказывается. Поэтому есть вероятность того, что при прохождении всех тестов интерфейса останутся ошибки, которые будут выявлены при использовании программы. Как найти код, который вызывает эти ошибки? Может у тебя на выходе то, что требуется, а внутренний код вызывает какой-нибудь левый разрушительный метод? Думаю, тут поможет возможность потестировать "кишки".

Кроме того, подход "чёрного ящика" противоречит классическому TDD (смотри цитату, смотри книгу). Но можно конечно использовать и другую систему.

5
31 января 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: Kogrom
Кроме того, подход "чёрного ящика" противоречит классическому TDD (смотри цитату, смотри книгу). Но можно конечно использовать и другую систему.

А что классический TDD говорит о локальных функциях? Их както тестировать надо?

33K
31 января 2011 года
hivewarrior
205 / / 16.11.2010
Цитата: Kogrom
Тут есть один вопрос - как проверить скрытые методы? Например, мы предполагаем, что в скрытых методах есть ошибка. Можно их на время открыть, например, и проверить временными тестами (а потом благополучно забыть закрыть). Можно скопировать в отдельный маленький проект и там протестировать (и при каждом сомнительном изменении там проверять). А можно использовать предложенный мною принцип (точнее не мною, а создателями Smalltalk).

Проверять всё не обязательно, да и невозможно. Но желательно иметь возможность протестировать любой конкретный метод, не меняя системы.


Ты рассуждаешь, что тестирование проще с паблик методами у агрегированного объекта, но покажи, как в готовом проекте тебе легче будет тестировать кишки классов.

Объект все равно приват и дотянуться до него тоже не так то просто, я бы даже сказал, так же трудно, как и до приватного метода. Будешь создавать отдельную песочницу для имитаций, или сборки, где появятся тесты отдельного агрегированного объекта? Тогда чем этот подход отличается от сборок, которые тестируют приватные методы самого класса? Можно банально для каждого класса ввести паблик функцию "test", а там уже устраивать операции над внутренностями чего и кого угодно. Потом одну функцию убираем/комментируем/делаем приватной и все работает как часы.

Чем отличается такой подход от твоего? Один избыточный (ли?) элемент в классе, против твоих конструкторов/деструкторов, сжирающих 70% процессора (цифра среднепотолочная, сильно не ругаться, используется для устрашения :rolleyes:).

Цитата: Kogrom

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


Лучше попытайся понять помыслы создателей BrainFuck'а. Просто люди создают то, что удобно им, и им кажется правильным. Это не истина в последней инстанции. Если у авторов есть свои доводы, то лучше спросить непосредственно авторов, а не пытаться понять их через книжки, там есть другое мнение, писателя книжки.

Страус Труп не хотел того, что получилось сейчас. Он, когда создавал классы и ООП пытался упростить программирование именно мат.моделей, где обычных структур явно не хватало. То, как все его идеи были извращены и доведены до абсурда можно легко посмотреть в C#, когда на все есть своя сущность, даже если логикой это не продиктовано.

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

Цитата: Kogrom

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

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


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

Пример:
Есть агрегированный объект копалка для класса лопата. Ты задал ее работу, показал в какое место надо давить и так далее, все работает.

Назови вероятность повторного использования этого объекта? Для экскаватора подойдет? Или для бура? Или для еще чего? Так что это не бонус, а туфта.Если не согласен, приведи пример, когда есть вероятность повторного использования.

Цитата: Kogrom

А в чём тут преимущество скрытого метода? В том, что его можно не проверять? Авось сработает?


Я сторонник того, что приватные методы уже давно проверены и ошибки в них исправлены. Трижды. И только потом они попадают в класс.

5
31 января 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: Kogrom
есть вероятность того, что при прохождении всех тестов интерфейса останутся ошибки, которые будут выявлены при использовании программы.


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


Цитата: Kogrom
Как найти код, который вызывает эти ошибки?


Взять тесткейз из багрепорта и прогнать софту под отладчиком.

297
31 января 2011 года
koodeer
1.2K / / 02.05.2009
Цитата: Kogrom
Я пытаюсь понять помыслы создателей языка SmallTalk, которые сделали все методы открытыми, а данные скрытыми.


Ну мало ли глупостей сделано в разных языках? Лопухнулись создатели SmallTalk'а, сделали все методы открытыми. Увы, до сих пор нет идеального языка.

Цитата: Kogrom
Тут есть один вопрос - как проверить скрытые методы?


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

87
31 января 2011 года
Kogrom
2.7K / / 02.02.2008
Цитата: hardcase
А что классический TDD говорит о локальных функциях? Их както тестировать надо?



На этот случай у меня нет цитаты :)

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

6
31 января 2011 года
George
4.1K / / 05.01.2007
А я живу проще и согласен с Hardcase'ом и с собой. Код пишется не ради тестов, а ради получения результата - программы. Если паблик интерфейс протестирован и работает, то зачем лезть в кишки? Выходит, что тестирование ради тестирования.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог