LINUX.ORG.RU

Вопрос по устройству программного продукта целиком

 , ,


0

2

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

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

Перемещено mono из talks



Последнее исправление: mono (всего исправлений: 1)

Начни с чего-нибудь по-проще.

Посмотри более-менее крупные репы на github.

Набивай руку)

Y ★★
()

Храни в корне README/README.md (если на github.com репозиторий), LICENSE/COPYING, информацию для сборки. Всё остальное - на твоё усмотрение.

К примеру, вот репозиторий GNOME Clocks: https://git.gnome.org/browse/gnome-clocks/tree/?h=gnome-3-6 (с 3.8 он уже не на Python).

drake
()

Выделяешь предметную область, например высокоуровневые методы изменения своего дерева и получения оттуда данных. Это модель, она ниже работает с источником — бд/памятью/сетью — и собирает ответы на предметные вопросы. Если модель непростая, стоит ее разбить на несколько файлов/классов, например один чисто враппер источника данных, другой для сложной аналитики кто чей родственник. Можно положить в /src/database, а можно в корень /src, как удобнее. Получаешь четкую предметную область в виде объекта класса Database, ниже сам больше не опускаешься.

Теперь очередь за контроллером(-ами) — они прибивают (binding) элементы форм к данным модели, к командам изменения модели, а также следят за своевременными перезапросами для обновления таблиц/полей, т.к. база тупая и не говорит что где менялось каскадом от первого изменения. Ну и собственно работа с формами, фокусы, переходы скрыть/показать окно/элемент, драгодроп. То есть контроллер это «делегат» для формы, и по совместительству бизнес-логика, т.к. имеет ключевые точки входа из интерфейса, чтобы реагировать и обновлять казалось бы несвязанные элементы UI.

Можно практиковать многослойность, то есть вместо баззворда MVC применять MMMCCCV, чем оно чаще всего и является. Пример MM: модель дерева подразумевает работу с примитивами, не отраженными в движке-источнике данных, вроде как человек это один уровень абстракции, группа SQL-таблиц с человеками и связями это другой, более нижний, а вот само дерево выше строится уже на человеках и в таблицы не заглядывает. Получается SQL < Person < Tree, два уровня. То же самое с «контроллером», можно для форм понастроить моделей поведения, которые будет жить только в рантайме, и сами пользоваться примитивами Tree и Person, а их, в свою очередь, будет использовать собирающий контроллер. Пример CC: midnight commander имеет две панели, то есть две инстанции одинаковых контроллеров, но есть еще метаконтроллер, который их содержит, он ловит F5 и т.п. и пробрасывает это все в/из предметной области, посылая в контроллеры запросы на перерисовку. Или например он может реализовать мастер-деталь, когда в левой панели ты бегаешь вверх-вниз, она шлет события, он их ловит и передает правой панели путь тек.файла, а она просто отображает quickview по этому пути. Т.о. оба контроллера панелей друг о друге не знают, и все их взаимодействие происходит через главный. А главный не заботится о локальных деталях панелей: где у них курсоры, прокрутки, какие поля чем заполнены, какие фильтры на выборки и т.п. — все варится внутри.

// псевдокод
class MidnightController {
    PanelController *leftPanel;
    PanelController *rightPanel;
    ...
}

class PanelController {
    View *view;
    ...
}

По папкам разноси как удобнее, а для файлов есть правило не более N строк на файл. Кроме этого еще есть ограничение самого языка, например в питоне емнип нельзя разнести класс на несколько файлов (кроме как через костыли), а вот на сях[++?] разноси сколько влезет — там все файлы собираются в объектники, и линкер их потом связывает, а если чего не хватает, то ругнется. В край можно #include «part2.c».

arturpub ★★
()

как тебе подсказывает тёмная сторона твоего подсознания, так и делай

Harald ★★★★★
()
Ответ на: комментарий от arturpub

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

evilmanul
() автор топика
Ответ на: комментарий от arturpub

а вот на сях[++?] разноси сколько влезет

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

в питоне емнип нельзя разнести класс на несколько файлов

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

anonymous
()
Ответ на: комментарий от evilmanul

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

arturpub ★★
()
Ответ на: комментарий от anonymous

И сопровождать потом намного удобнее, особенно если ещё добавить

Парсер распознал сарказм, если так, то я говорю не о размазывании кода по модулям, а о разделении логических частей одного объекта. Например можно выделить делегацию датасета для таблицы в mycontroller-persons-table-dataset.c, а остальную логику оставить в mycontroller.c. Размазать всегда успеешь :)

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

arturpub ★★
()
Ответ на: комментарий от arturpub

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

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

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Как эталонный хеллоуворлдщик-быдлокодер, давно хотел спросить - а что есть «тесты» (нет, представляю, конечно, что это, но чем они являются с технической стороны?) и как они пишутся?

evilmanul
() автор топика
Ответ на: комментарий от i-rinat

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

Так они уже написаны давно, осталось отрефакторить вместе с остальным кодом :D

anonymous
()
Ответ на: комментарий от evilmanul

Нашёл у кого спрашивать, я ж ничего крупного не пишу :)

«Тесты» — это такое волшебное понятие, в которое все вкладывают разный смысл. Очень удобно использовать в дискуссиях.

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

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

Тут надо осознавать, что никакое конечное количество тестов не гарантирует корректность. Считай это своего рода проверкой на опечатки.

i-rinat ★★★★★
()
Ответ на: комментарий от evilmanul

Как эталонный хеллоуворлдщик-быдлокодер, давно хотел спросить - а что есть «тесты» (нет, представляю, конечно, что это, но чем они являются с технической стороны?) и как они пишутся?

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

Например, любимый начальник повелел тебе высечь в байтах функцию, которая считает сумму двух своих параметров. Так и написал в ТЗ: «функция Ф принимает параметры А и Б и возвращает их сумму». Ну, разумеется под проект выбивается финансирование, оборудование и выделяется разработчик - ты. Как любой разработчик, ты отлично разбираешься во ввереном тебе языке программирования, но ни ухом ни рылом в прикладной области. Поэтому в твоей реализации функции все операнды складываются как обычно, а для некоторых результат ещё дополнительно инкрементируется. Ну просто ты что-то упустил на уроке математики. Параллельно ты же сам пишешь тестовую программу, которая будет закидывать в твою функцию разные наборы параметров и и убеждаться, что она глючит именно так, как ты хотел. После этого ты демонстрируешь начальнику, что твоя функция успешно проходит тесты, её принимают, вставляют в самолёт, тестируют самолёт уже сам по себе, те редкие комбинации параметров при которызх твоя функция сглючила бы, разумеется во время тестов не случаются, а случаются уже потом, во время эксплуатации, где-нибудь в Казани. Только не надо меня спрашивать какой вообще смысл в этом юнит-тестировании.

anonymous
()
Ответ на: комментарий от i-rinat

Понятно, спасибо. Примерно так и представлялось.

evilmanul
() автор топика

по какому принцпипу код выносят в отдельные файлы?

либо по принципу логической связанности кода внутри файла, либо следуя требованиям OS/IDE/фреймфорка/яп.

Куда кладутся разные побочные ресурсные файлы, вроде картинок (если есть)?

аналогично — либо туда, где удобно, либо туда, куда их требует класть фреймфорк или IDE.

p.s. в некоторых языках принято 1 class per file, так что твой вопрос будет звучать «по какому принципу код разносят по разным классам», и тут тебе уже надо читать книжки и статьи по ООП.

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

waker ★★★★★
()
Последнее исправление: waker (всего исправлений: 1)
Ответ на: комментарий от evilmanul

Я в явном виде их никогда не пишу (в смысле что make test и все такое). Но если есть отдельные блоки, сами по себе сложные, то для них всегда существует тестовый проект, где я просто жму compile+run и смотрю где валится. Обычно этот проект покрывает основное множество веток кода, а ошибки и другой неликвид я проверяю путем временного изменения условия на противоположное. Например if (res == 0) превращается в if (res != 0) и наблюдается адекватность происходящего. После этого код импортируется в основной проект, можно ссылкой, чаще копированием, т.к. часто хочется что-то допроверить в песочнице, не ломая фронт. Может показаться, что так все плавно разваливается, но на этом уровне несложно все обратно собирать, особенно если есть формализация.

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

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

arturpub ★★
()
Ответ на: комментарий от arturpub

в питоне емнип нельзя разнести класс на несколько файлов (кроме как через костыли)

Можно. Объявляешь функцию и заносишь ее в класс, это не костыль.

AIv ★★★★★
()
Ответ на: комментарий от arturpub

Получаешь четкую предметную область в виде объекта класса Database

генеалогического древа
Database

wut?

Laz ★★★★★
()
Ответ на: комментарий от Laz

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

arturpub ★★
()
Ответ на: комментарий от arturpub

Я чаще всего за этим термином встречаю набор сущностей и отношений между ними, отражающих ключевые характеристики предметной области, с чем и было связано моё возмущение. Буква 'M' в «MVC», на который я увидел намёк, к базе данных тоже никакого отношения не имеет. Впрочем, прочитав ваше сообщение полностью, я понял, о чём идёт речь, да.

база данных (по-молодежному «модель»)

В этом ракурсе интересно было бы узнать мнение молодёжи о таких вещах как «сетевая модель OSI», «модель ДВС», «планетарная модель атома».

Laz ★★★★★
()
Ответ на: комментарий от Laz

Да, я достаточно вольно использовал термины, потому что когда плаваешь по разным уровням абстракции, понимаешь что вот это для того — то же самое, что вон то для вот этого. И смысл названий исчезает, т.к. название это мое отношение к объекту, а оно априори неоднозначно. В каждом проекте вырабатывается своя терминология, в зависимости от основных ролей [групп] объектов, тоже тот еще зоопарк. Здесь уж кто что чаще встречал, то и воспринимает на слух, приходится такие вот портянки писать/рассказывать, чтобы полностью мысль донести.

arturpub ★★
()
Последнее исправление: arturpub (всего исправлений: 1)
Ответ на: комментарий от arturpub

Стек хранит, получает и отдает данные, но Это не БД.

A под моделью в разных местах понимают настолько разные вещи, что я бы вообще это слово изъял из оборота:-)

AIv ★★★★★
()
Ответ на: комментарий от AIv

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

Indaril_Shpritz
()
Ответ на: комментарий от Indaril_Shpritz

y геологов модель это внутреннее строение земли. y тех кто численным моделированием занимается это система уравнений. A еще есть модели которые по подиуму вышагивают Или на обложку журнала Снимаются. A еще есть модели которые из Набора циан акрилатOM Клеют. И это далеко не полный список.

AIv ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.