байка об эффективности
— у нас тут велосипед медленно едет, но у него квадратные колеса
— колеса смените
— некогда, надо быстрей педали крутить
если вы будете 12ч работать, а потом мотивировать себя на изучение новой высоко-требовательной-к-ресурсам-мозга информации — ничего у вас не выйдет
потому что не бывает так
как правильно сказали ниже — работайте меньше, ставьте приоритет — чтобы сейчас получать меньше, но в будущем начать заниматься профессионально (после учебы) тем, чем нравится
«20 тысяч в месяц» пфф, я работал первые годы вообще за 5к в месяц, или вообще бесплатно, лишь бы наработать опыт.
А когда опыт есть — никто вам 20к не заставит получать, берете и идете на новую работу, где не балду гонять за 20к нужно (или неквалифицированной работой заниматься), а тем умственным высокооплачиваемым трудом, ради которого собственно вы и учились и нарабатывали опыт
8 лет «хорошо учиться» можно по-разному:
— балду гонять, тогда считайте и не учились
— нарабатывать на будущее опыт, тогда вам достаточно еще несколько лет реально пообщаться с заказчиками (на фрилансе, если есть силы, но это для профи) или в фирме (для не-профи) и поразрабатывать с использованием современных инструментов\языков, когда база есть — это уже чтото
Имхо нет никакого смысла себя мотивировать, или вытаскивать из «Ж». Тебе либо нравится то что ты делаешь, и ты в этом уверен (что нравится), и тогда ты продолжаешь делать это дело, не важно с какой скорость, хоть с черепашьей, но ты знаешь что ты двигаешься в верном направлении.
Либо это не твое. Тут распознается имхо просто — не приносит удовольствия от процесса, нет результатов, хочется заняться чем-то другим. Особенно когда человек идет учиться не туда куда хотел, а куда направили (родители, общество) — это бесполезная трата времени, и человек сразу понимает это, потому что вообще ничего не идет, и нет желания того чтобы оно «шло», т.е. во время учебы это люди «лишь бы сдать», во время работы это «лишь бы быстрей домой с работы вернуться».
Человек сам знает, или по крайней мере может приложить усилия чтобы узнать, нравится оно ему или нет. Тогда вперед — делать дело, или выбирать другое.
Маловероятно программист может переквалифицировать в UI\UX, это скорее перпендикуллярное направление. Но если вам этим хочется заниматься — почему бы и нет, учитесь. В том числе и анализ требований, обсуждение с заказчиком, менеджерские качества, навыки проектирования, это все не написание кода и не знание алгоритмов, но это нужно в современной разработке.
Однако работодателю скорее в современных реалиях будет хотеться чтобы вы и код писать умели, и проектировать, и организовать себя могли, и UI сделали в прилоге, и т.п, особенно если это маленькая фирма, т.е. там — требуются люди-оркестры.
Если хотите немного что-то конкретное поизучать, а не распыляться, тогда предположу что вам в большую фирму нужно устраиваться, и нарабатывать там опыт
здесь я бросил искать нормальное решение и оставил этот костыль, потому что
«нормальное» это по каким критериям? если формально… вот есть требования (критерии), и адекватность решения — сложный вопрос.
для вас в том числе — приведу ССЫЛКУ
на свой коммент (и далее см ветку)
по 1му-примеру моему — это «нормальное» решение для задачи Интеграции
во 2му-примеру моему — это «нормальное» решение для задачи Оптимизации
… комменты тут помогают, а решение НЕ-костыль
p.s. если рассматривать «средний случай использования комментариев неопытным\незрелым программистом, не разбирающимся как правильно и делающим костыль потому что не-знает\нет-времени» то соглашусь, что коммент там хуже, чем сразу-нормальное-решение-вместо-костыля, однако не находите что такой случай не равен всем-возможным-применениям-комментов-для-аргументации-решения?
Я могу привести еще примеры:
* было сравнено несколько либ, и выбрана конкретная, и в комменте написано «почему» (критерии — скорость, простота, фичи, баги)
* была собственная разработка\исследование, и несколько вариантов перебрали, решили что «лучше так, а не иначе» (иначе — не то что надо из: производительность, артефакты\баги, сложность кода)
Да тут что с комментариями, что без — ничего не понятно потому, что код вырван из контекста.
Вам непонятно не потому что я опустил контекст, а потому что вы не разбираетесь в предметной области и у вас нет опыта. И даже мне, спустя год от написания этого кода, может быть непонятно-некоторое-время, но посмотря на комменты (сначала), а потом на код — уже можно разобраться.
необоснованно ограничивают область применения кода («Используется для создания всех контуров одного графического элемента» — а что если кто-то захочет этот код переиспользовать не для графического элемента, а для чего-то ещё? Расстрел на месте? Позорная грамота?),
этот код для конкретного проекта и конкретной задачи, да это ради сильной оптимизации, и использовать в другом месте — нельзя, где есть что-то похожее — там может быть совсем другая реализация, и код там — другой, и как найти — то что надо, и почему оно используется «только тут» и «почему оно» — оно и написано в комменте
описывают какие именно от конкретной реализации ожидания…
Если эти ожидания так важны, то хорошо бы было не комментарии писать, а добавить соответствующие тесты, например: «а правда ли оптимизация позволяет уменьшить GC?»)?
* очень любопытно посмотреть как вы будете проверять авто-тестами что GC будет уменьшаться, притом что в профайлере оно проверено вручную и довольно сложным способом
* есть задача т.е требования к оптимальности и GC и есть ее решение — все рядом, если вы кините в тесты, а в коде класса не напишете — надо кучу времени тратить для чтения других файлов с тестами, а тут все — рядом, плюс — комментарий высокоуровневый, а выражать это в виде тестового кода (относительно низкоуровневого) — та еще сложная задача, или писать\выдумывать очень-высокоуровневые-тесты, которые без комментариев (в самих тестах даже) подскажут что и почему тут делается — по опыту сложней чем написать коммент сразу в коде
Добавляют информацию, которую хорошо бы проверять («Аллокатор для HandleOfContour (в этом классе т.к. сам Builder управляет созданием и освобождением Handle)» — а что, нельзя интерфейс класса сделать так, что бы эти Handle в принципе нельзя было удалить где-то вовне?)
* во 1х это C# managed-код, и можно вообще не удалять ничего, и все отдастся для GC, но с проблемами и тут — оптимизация
* во 2х — интерфейс у этого класса есть — там есть и FreeAll для очистки всех объектов (а там показано в комменте что по-одному они не очищаются), и тут в комменте описано время жизни объекта: объект HandleOfContour не самодостаточный, он — прокси, а коллекция лежит внутри BuilderOfContours, и поэтому освобождать все должен — Builder
В целом это похоже на логику креационистов — взять отдельный кусок (кусочек кода, жука-носорога) и заявить, что по-другому нельзя потому что слишком странно и в привычный контекст не укладывается (контекст-то опустили).
Не очень понял что за логика «креационистов» и причем тут программирование и проектирование. У нас есть задача — есть ее решение, в том числе очень сильно оптимизированное — во 2м примере кода.
В 1м примере с SMAA — либа для анти-алиасинга интегрирована, с изменениями от базовой (и они описаны в комменте), плюс некоторые подробности почему-и-как, и там тестировать юнит-тестами — нереал это.
Это такой проект, со своими требованиями. Там таких классов не 2, а сотни, и мест с комментариями — тысячи.
Какой контекст еще нужен вам?
Статья правильная.
А вот теперь гуру TDD и систем типов, и желающих писать код без комментариев, аргументируйте как тут (см мой пример) можно НЕ писать никаких комментариев, и потом как это будет просто поддерживать без-комментариев VS с-комментариями.
Тот, кому легко и все понятно в своих проектах, просто делает относительно простые и рутинные вещи, без обид. Поэтому им (не говорю конкретно кому) досатточно и чистый код, и никаких комментариев. И TDD здесь не причем — всю внутрянку тестить — и усложнять и увеличивать кол-во тестового кода (для покрытия оригинала) это будет высоко-вероятно в 2 раза больше кода (а тесты тоже код), чем комменты написать, хотя да такие вещи стоит тестировать автоматически, но дело в описании почему и зачем и что (в том числе), а не только «правильно ли это работает»
Пример из моего проекта
namespace Assets.SVG_Importer.SubpixelMorphologicalAA
{
/// <summary>
/// Рендер мгновенного снимка SMAA (Subpixel Morphological Anti-Aliasing)
/// </summary>
/// <remarks>
/// Применимо в конце на итоговую текстуру, чтобы не остались артефакты блендинга
/// (как при MSAA остаются) и не остался алиасинг после масок;
/// В отличие от SSAA не требует повышенного размера текстур для рендера;
/// Хуже работает на тонких объектах,
/// поэтому SSAA отдельно или SSAA+SMAA - где-то лучше чем чистый SMAA;
/// </remarks>
/// <remarks>
/// Оригинал порта для Unity: https://github.com/Chman/SMAA/blob/master/SMAA/Scripts/SMAA.cs
///
/// Особенности изменений в порте
/// * чтобы не требовалось задавать лишнего (параметры вшиты в шейдер):
/// UsePredication = false (без камеры и доп. текстуры)
/// Quality = QualityPreset.Medium (кач-во достаточное, иначе из-за Diag и т.п. артефачит т.е. не все антиалиасит)
/// DetectionMethod = EdgeDetectionMethod.Color (именно он, т.к. при Luma был баг с отсутствием АА на некоторых цветах)
/// Hdr = HDRMode.Auto (текстуры создаются во вне, одинакового типа)
/// * в шейдерах внесены изменения для поддержки GLES 2.0 (Android, WebGL 1.0)
///
/// Текстуры для алгоритма должны быть в ресурсах:
/// AreaTex
/// SearchTex
/// </remarks>
public class ImmediateSmaa
{
/// <summary>
/// Переиспользуемая RT 1
/// </summary>
private readonly RenderTexture _reusableRt1;
/// <summary>
/// Переиспользуемая RT 2
/// </summary>
private readonly RenderTexture _reusableRt2;
/// <summary>
/// Материал фильтра SMAA
/// </summary>
private readonly Material _smaaMaterial;
/// <summary>
/// This texture allows to obtain the area for a certain pattern and distances
/// to the left and to right of the line
/// </summary>
private static readonly Texture2D AreaTex = Resources.Load<Texture2D>("AreaTex");
/// <summary>
/// This texture allows to know how many pixels we must advance
/// in the last step of our line search algorithm, with a single fetch
/// </summary>
private static readonly Texture2D SearchTex = Resources.Load<Texture2D>("SearchTex");
/// <summary>
/// Pass IDs..
/// </summary>
private const int PassEdgeDetection = 2; // Color (именно он, т.к. при Luma был баг с отсутствием АА на некоторых цветах)
private const int PassBlendWeights = 4;
private const int PassNeighborhoodBlending = 5;
/// <param name="reusableRt1">Переиспользуемая RT (non-sRGB sampler)
/// (размер всех текстур равен: исходной, для результата и промежуточных)</param>
/// <param name="reusableRt2">Переиспользуемая RT (non-sRGB sampler)</param>
public ImmediateSmaa(RenderTexture reusableRt1, RenderTexture reusableRt2)
{
_reusableRt1 = reusableRt1;
_reusableRt2 = reusableRt2;
Shader shader = Shader.Find("Hidden/Subpixel Morphological Antialiasing");
if (!shader.isSupported)
{
throw new Exception("ImmediateSmaa: SMAA shader is not supported");
}
_smaaMaterial = new Material(shader);
// (подразумевается в настройках текстуры - linear color space, non-sRGB sampler)
_smaaMaterial.SetTexture("_AreaTex", AreaTex);
_smaaMaterial.SetTexture("_SearchTex", SearchTex);
int width = reusableRt1.width;
int height = reusableRt1.height;
_smaaMaterial.SetVector("_Metrics", new Vector4(1f / (float)width, 1f / (float)height, width, height));
}
/// <summary>
/// Рендеринг в переиспользуемую RT, с фильтром
/// </summary>
/// <param name="source">Исходная RT (gamma color space, non-sRGB sampler)</param>
/// <returns>Переиспользуемая RT с результатом (gamma color space, non-sRGB sampler)</returns>
public RenderTexture Render(RenderTexture source)
{
GraphicsUtils.AssureRenderTextureIsCreatedNotLost(_reusableRt1);
GraphicsUtils.AssureRenderTextureIsCreatedNotLost(_reusableRt2);
GraphicsUtils.ClearRenderTexture(_reusableRt1);
GraphicsUtils.ClearRenderTexture(_reusableRt2);
// Edge Detection (source - gamma color space for color/luma edge detection)
Graphics.Blit(source, _reusableRt1, _smaaMaterial, PassEdgeDetection);
// Blend Weights
Graphics.Blit(_reusableRt1, _reusableRt2, _smaaMaterial, PassBlendWeights);
// todo debug
//TextureSaveUtils.SaveRenderTextureToFile(_reusableRt1, "Q:\\SmaaStep1_Debug.png");
//TextureSaveUtils.SaveRenderTextureToFile(_reusableRt2, "Q:\\SmaaStep2_Debug.png");
/**
* All texture reads and buffer writes must be non-sRGB, with the exception
* of the input read and the output write in
* 'SMAANeighborhoodBlending' (and only in this pass!). If sRGB reads in
* this last pass are not possible, the technique will work anyway, but
* will perform antialiasing in gamma space.
*
* Note: source Gamma correction производится внутри измененного шейдера,
* чтобы в linear color space производился антиалиасинг; на выходе шейдера Gamma space
*/
// Neighborhood Blending
_smaaMaterial.SetTexture("_BlendTex", _reusableRt2);
Graphics.Blit(source, _reusableRt1, _smaaMaterial, PassNeighborhoodBlending);
// todo debug
//TextureSaveUtils.SaveRenderTextureToFile(source, "Q:\\PreSmaa_Debug.png");
//TextureSaveUtils.SaveRenderTextureToFile(_reusableRt1, "Q:\\AfterSmaa_Debug.png");
return _reusableRt1;
}
/// <summary>
/// Очистка RT
/// </summary>
public void Free()
{
_reusableRt1.Release();
_reusableRt2.Release();
}
}
}
Еще Пример из моего проекта
/// <summary>
/// Построитель контуров, т.е. массивов X и Y,
/// образованных виртуальной нарезкой больших массивов;
/// Используется для создания всех контуров одного графического элемента
/// ---
/// С возможностью заранее не знать требуемый размер, и добавлять столько элементов контура, сколько нужно,
/// в процессе интерполяции (иначе там был бы нужен еще один временный буфер),
/// но только до момента финализации контура - после этого становится известен конец текущего массива
/// и начало следующего - с этого момента добавление в первый контур запрещено,
/// и общий массив используется уже для добавления в следующий контур;
/// Это ограничение использования позволяет достичь не-фиксированных массивов и использовать память эффективней
/// ---
/// Оптимизация List[Vector2], для уменьшения GC, для использования в пуле (ThreadLocal)
/// ---
/// Отличается от BuilderOfSlicedArrayVector2 возможностью не знать размер заранее,
/// поэтому полученные Handle другого типа (не HandleOfSlicedArrayVector2)
/// </summary>
public class BuilderOfContours
{
/// <summary>
/// Общие массивы
/// </summary>
private readonly float[] _totalX = new float[CollectionsConfig.VerticesLimitInGraphicElement];
private readonly float[] _totalY = new float[CollectionsConfig.VerticesLimitInGraphicElement];
/// <summary>
/// Аллокатор для HandleOfContour
/// (в этом классе т.к. сам Builder управляет созданием и освобождением Handle)
/// </summary>
private readonly PooledAllocator<HandleOfContour> _handleOfContourAllocator = new PooledAllocator<HandleOfContour>(() => new HandleOfContour());
/// <summary>
/// Список выделенных handle для освобождения,
/// и он же - список контуров для возврата вовне
/// </summary>
private readonly List<HandleOfContour> _contours = new List<HandleOfContour>(CollectionsConfig.ContoursLimitInGraphicElement);
Есть мнение, что TDD не сильно замедляет разработку (если вообще замедляет). Потому что быстрее приходишь к верному решению и изначально пишешь тестируемый код, а его легче менять, он более чистый.
Есть возражения
1) не все можно легко протестировать, например в геймдеве, тем более если есть куча рендерного\визуального, что можно оценить глазами, AI не изобрели; хотя согласен что многие вычислительные штуки надо тестировать автоматически
2) кучу фичей, которые постоянно добавляются\удаляются\меняются — нерентабельно покрывать тестами т.к они поменяются буквально завтра и все тесты (кроме основного кода) выкинуть
3) если это стартап, дело даже не в том что сложно тестировать\нецелесообразно, а в том что по некоторому мнению (я его тоже придерживаюсь) тратиться не столько как вы выразились «не сильно замедляет разработку» а очень даже существенно лишнего времени, я бы сказал что раза в 1.5-2, и за это время может проект стать мало кому нужным, плюс фичи будут выкинуты (см пункт 2) и нужно быстрее (максимально быстро) выходить на рынок
А практика писать в стиле «нам некогда останавливаться и менять квадратные колёса на круглые, у нас сроки» на самом деле замедляет тебя уже через час.
Утрирование. Согласен что если чтото легко покрыть тестами и оно не поменяется завтра, и бизнес выделил время-деньги — это делать нужно, но даже без тестов — писать качественный код — никто не запрещает. И если это вручную тестировать быстрей, а написание автотестов — будет по времени дольше, то се нормально. Но никто еще не может с уверенностью предсказать какой функционал сколько времени в проекте проживет, и заранее закладывать кучу усилий на тесты того, что будет вероятно выкинуто — это как раз «квадратные колеса» имхо, т.е делать недумая просто потому что так ктото «умный» сказал — «TDD везде это тру, он не замедляет» — это не верно.
Не скажу за всех, но имхо — диаграммы классов — это уже очень-низкий-уровень, т.е делать их, а потом еще и писать код — дублирование работы. Исключением может быть диаграммы классов, составленные для обсуждения\документации подхода, и включающие только малое число классов и методов, которые явно не один-в-один будут перенесены на код. Т.е те диаграммы классов, что для обучения паттернам проектирования — они не подходят имхо, для нормального проектирования, потому что низкий уровень. Пример конкретный думаю вы сами сможете найти для не-очень-подробной-высокоуровневой-диаграммы-классов. Однако имхо и без них вообще можно обойтись.
Т.е использовать диаграммы: Последовательности, Прецедентов, state-machine — самые распространенные, и с их помощью можно именно высокий уровень задачи описать, и иногда детали реализации (но не скатиться в очень детальность).
Хотя та же state-machine (состояний) может быть применена как для высокого уровня, например юзер в состоянии = меню, кликает «в бой» и условно если есть соперники — сервер его помещает в бой сразу, или если нет соперников — ставит в очередь ожидания, и потом когда начнется состояние = бой, он может проиграть или выиграть или ливнуть из игры.
Но может быть и низкий уровень вроде описания работы TCP с его syn\ack и состояниями — что обычно редко когда нужно, но всетаки если и нужно — это низкий или средний уровень, думайте сами — нужны ли они вам, мне — обычно нет.
Т.е разница ключевая тут — сколько еще уровней-ПОД находится, и сколько кода надо написать… если много кода еще предстоит написать (т.е скрыто, за пределами диаграммы) — имхо нормальная диаграмма, и предназначенная для overview\обучения\обсуждения, а вот если кучу подробностей — так это почти уровень кода, и дублирование работы.
И что я тут по вашему опровергал? Я говорю лишь то что говорю — а именно — если вам нужны конкретные тестовые варианты (а не целый алгоритм) — вы делаете на каждый тест вариант (основной, хоть на один — если он только важен) — Диаграмму последовательности. Это ее нормальное применение. Да это разные диаграммы. И «дорожки» обычно нужны чтобы показать что происходит на каждой стороне, т.е что делает клиент, что сервер, что человек (3 сущности например).
Если вам нужен алгоритм, во всех его проявлениях, т.е не один тест вариант, нужно юзать Диаграмму деятельности.
Есть помоему комбинированная, где есть и дорожки, и полный алгоритм, но это 3я (точно не 1я и не 2я).
p.s. извиняюсь если это выглядело как опровержение, или если тут так принято, писать коменты только ради опровержений. Я просто уточняю — что имею в своем опыте.
И в моей практике «Зачастую на практике важны имено конкретные варианты развития» это значит что «зачастую», а другую диаграмму в принципе и не юзал почти никогда, за не надобностью, но смысл в ней есть конечно, просто обычно — нужны конкретные варианты, и уже их них можно составить прямо сразу Код, минуя диаграмму деятельности, потому что каждый из тест вариантов — можно проверять прям по нужной диаграмме Последовательности.
Ну а еще в момент анализа (хоть составлении новой функции в существующую систему, которая пойдет и повлияет на другие; хоть в момент составления новой системы) вам самим он помогает, это не мало, это не «всего лишь только мне понятно» это «без него я бы тут запутался если бы код писал»
Если задача — обсудить какието сложные вещи — которые вы не можете быстро сами додумать (потому что не спец, потому что не вы ответственный, потому что надо доказать другим что оно будет или не будет работать) — конечно любой способ донесения информации хорош, но если это происходит часто — все просто научатся UML пользоваться в вашей среде, и далее будет понятно.
А если это на один раз — доносите человеческим языком, задавайте вопросы, обсуждайте — как угодно, но это не смысл и не use-case для UML, и это не его минус.
Те же слова, написанные русскими буквами, зачастую не понятны людям, они задают уточняющие вопросы, чтото переспрашивают… это же не минус русского языка правда?
Зачастую на практике важны имено конкретные варианты развития (они же — тестовые варианты). И показать на диаграмме что и за чем следует, в варианте 1 (а на другой диаграмме — вар 2) — вполне адекватное применение. Но да это не все случаи использования UML.
Просто конкретная диаграмма, как уже высказались, имеет смысл для того, чтобы показать то или иное — общий алгоритм (одной подсистемы, или высокоуровневой системы — разные случаи) или может состояние системы в нужный момент (со всеми объектами, или срез из нужного множества объектов)
И при составлении диаграммы классов для них приходится сначала декомпозировать специфичные для языка вещи на элементарные сущности UML — а потом, при чтении диаграммы, мысленно собирать их обратно.
То что вы конвертируете какието сущности из UML напрямую в язык-прогр, или наоборот из языка-прогр в UML — только подтверждает то, что вы используете ОДИНАКОВЫЙ УРОВЕНЬ и для UML и для языка-прогр, что не имеет никакого смысла на практике. Имеет смысл РАЗНЫЙ УРОВЕНЬ, например это planerka.info/item/diagrammy-posledovatelnosti написанное человеческим языком и там видно на прилично-высоком-уровне что делается каждый из модулей (или даже людей).
Выходит UML нужен только автору, как инструмент фиксации своих идей, если графическую фиксацию он воспринимает лучше других. Мне вот текстовая ближе и субъективно, и по результатам многочисленных психологических тестов.
Не соглашусь с этим: если код писали НЕ Вы — перечитывать кучу хитросплетений модулей и имен и зачем это все сделано и как работает — это явно не быстро. Зачастую ВЫСОКИЙ УРОВЕНЬ в виде документации UML или текстового описания коллегой в чате (что одно и тоже — ВЫСОКИЙ уровень ключевое) — будет эффективней для понимания хоть всей системы, хоть конкретного алгоритма (если по нему вам описали).
Для понимания нюансов — вроде как производительно у нас тут работает (где оптимизить, где итак ок), или какие модули с чем связаны вплоть до параметров методов и иерархий классов — разумеется будет накладно описывать в виде UML просто потому что это другой УРОВЕНЬ детализации, его конечно лучше в коде читать.
Далее мое имхо. Смысл в UML или любом другом языке моделирования (хоть блок-схемы, хоть текстовое описание) — именно в разном УРОВНЕ представления.
Например: сначала архитектор создает скелет — условно диаграмму последовательностей из клиента-сервера-юзера и там отображаются кто кому какие ВЫСОКО-УРОВНЕВЫЕ команды посылает, в этом и смысл, не нужно все детали отображать. А если нет деталей — не возможна кодо-генерация, ну можно условно шаблон сгенерировать из классов и пары методов, а потом всеравно начинку (код кучи if-ов и методов и параметров).
Другой пример: рисуется диаграмма state-machine — она СРЕДНЕ-УРОВНЕВАЯ — и предназначена для решения конкретной задачи, условно там есть Init (где начало) и переходу при кликах юзера в UI, в зависимости от условия (if просто другим языком) делается переход в состояние, в котором и UI будет другой и команды перехода из-в будут другими, т.е конкретный алгоритм можно сказать. Но это все еще НЕ-КОД, т.е конкретные детали всеравно придется кодить (вызывать конкретные методы\функции, передавать нужные параметры, рисовать чтото в UI, отслеживать события из UI от юзера и т.п).
Смысл этих уровней — есть. Но он должен быть осмысленным.
Т.е не так что «сделаем диаграммы на все что у нас в проекте будет — а код сгенерируем» — не сработает т.к. вы диаграммами высокого уровня хотите сгенерить код низкого уровня? это же не верно… просто потому что вам в момент проектирования ВЫСОКОГО-уровня неважно(!) что на низком будет, пусть там хоть Set\List\Dict будет, хоть цикл с полным перебором, и какой там UI и какие события — не важно.
В обратную сторону «напишем код — тулза нам сгенерит документацию на UML» частично может работать, но это для ОБУЧЕНИЯ, т.е входа новых лбюдей в этот проект, или написания книжек по паттернам проектирования (это тоже важно).
Зачастую нужен баланс и понимание того что мы сделаем ВЫСОКИЙ уровень системы клиент-серв (например, если у нас эта задача), сделаем СРЕДНИЙ уровень для ПОДСИСТЕМЫ UI с диаграммой состояний, сделаем еще СРЕДНИЙ уровень для ПОДСИСТЕМЫ рендернга 3D графиик на клиенте, и потом еще напишем кучу НИЗКО-уровневой реализации во многих подсистемах (кучу if, имен методов и параметров, их типов, кучу enum и т.п — все это описывать в ВЫСОКО-уровеном UML\диаграмме зачастую нет смысла, если только они не ключевые в понимании системы).
Где-то (уже не помню) читал о том что мышление наше МНОГО-УРОВНЕВОЕ, но вот тулз для поддержки этого мышления в программировании — пока нет, полноценных по крайней мере, т.е то что есть в виде отдельных кодо-генераторов — уже описал — слабо помогает.
Идеально это если бы были и work-flow (как мы делаем, инструкции для программистов по проектированию) и чем — какими тулзами \ генераторами \ IDE, и как все это в общую систему свести.
Были такие попытки в духе RUP… на сколко знаю — не идеально.
ДУМАТЬ УРОВНЯМИ вам никто не мешает. Так и нужно думать. Т.е. создавать (хоть в голове и потом забыть \ хоть текстом и потом выкинуть, ну можно и задокументировать) какойто один высокий уровень, проектировать постепенно и волзвратно средние уровни, и чтобы все это не устаревало — всеравно как-то нужно поддерживать ЗНАНИЯ. Т.е ключевое тут — НЕ-КОД, НЕ-UML, а ЗНАНИЯ! Т.е программист, когда он решает даже потом вернуться к задаче — не код должен дублировать в виде документации, или документацию пере-генерировать в код и потом фиксить это, ему ЛЮБЫМ СПОСОБОМ надо поддерживать РАЗНЫЕ УРОВНИ ЗНАНИЙ. Пока способов поддержки УРОВНЕЙ — мало, и все не очень… т.е ктото будет лениться писать документацию (даже комменты в коде — помогают, если они на ДРУГОМ УРОВНЕ), потом лениться ее править, и они хотят чтобы чтото за них это сделало… нет такой тулзы, чтобы вам сделать много УРОВНЕЙ — по вашему желанию. Поэтому зачастую программисты поддерживают ТОЛЬКО КОД. Ну чтобы единый источник был. Они думают что это ИДЕАЛ, чтоб не дублировать документацию. Но смысл всеравно остается — эти УРОВНИ они должны гдето брать — когда надо пофиксить чтото, добавить функционал, обучить когото — вспомнинать приходиться из головы, или придумывать\ проектировать заного.
Уточняю — в нашем проекте не игрорвая логика каждый кадр, а один очень толстый pipeline по генерации кое-чего в рунтайме, и испольщзующий кучу алгоритмов и данных, как исходных так и промежуточных, в таком случае — применять собственные менеджеры памяти — очень помогает как ускорить так и снизить потребление памяти. Но задача не из частых.
Просто как пример привел.
В том же С++ или любом другом языке может быть задача с кучей обрабатываемых данных, причем разных структур и промежуточных, с долгой обработкой, а не раз 1 алгоритм запилил и оптимизий. Очень часто данные надо гонять, и объекты в доменной области есть.
Что до юнити — да можно переиспользовать игровые объекты классически — чтоб не создавать каждый раз новый, но речь не только об этом.
Вот например есть либа для триангуляции, вот она при написании на Си имела производительность Х, потом при переписывании на С++ стала Y, потом на C# стала в разы быстрей чем была на Си, изза аллокаций памяти неоптимальныъх в Си, а в C# сборщик памяти ускорил дело, представляете (можно было бы использовать как контр-пример что мол круто сборка мусора ускоряет). Но потом ее еще оптимизировали на пулы и она стала быстрей чем была, на всех платформах, т.е. на C# с ручным менеджментом памяти стала быстрей чем на C# со сборкой мусора, и быстрей чем на Си с ручным менеджментом.
Либа эта LibTessDotNet на C# и при желании нагуглите ее версии На Си и С++ и т.п.
А в моем проекте кроме ее я юзаю еще кучу разного.
Простите что вклиниваюсь, но не могу удержаться, и мой ответ — сравнение C# со сборкой мусора и кучей аллокаций vs С# реализация той же системы с оптимизацией памяти на выделенные буферы и их переиспользование и ручное освобождение с пул аллокаторами и явными зонами ответственности… так вот как минимум на сборку мусора по скорости дало раза в 5-10 ускорения (зависит от условий теста) — на том же самом языке, кроме прочего потребление памяти снизилось, и это было и на ПК и в WebGL — основа на unity. Я могу предположить что при нормальной оптимизации скорость по C++ должна быть еще чуток выше. До 50 раз — может быть а может и нет.
Сторонники статической типизации возлагают на нее надежды по борьбе с багами. Проблема багов значительно шире и эффект от применения статической типизации явно преувеличен
Я как сторонник возлагаю надежды, и даже сообщаю свой опыт, и я писал и на JS и на PHP (уж не говорю это «правильная» там динамика или нет), и вижу что проще не допускать баги в Java или C# и даже в C++, с его управлением памятью, и даже в Си с его старой древней библиотекой, я на всем этом попробовал писать проекты с использованием нормальных доменных моделей, пусть и не из миллиона строк, но опыт у меня есть.
Для борьбы с ней наиболее эффективно лишить программиста инструментов управления памятью. Управлять памятью должен полностью компилятор. Динамические языки ближе к решению данной проблемы и поэтому более эффективны.
Зависит от проекта. Для подавляющего большинства простых проектов, и для большинства средних/сложных — наверное да, управление памятью в духе сборки мусора — вне ведома программиста, это хорошее решение, в сложных проектах где к тому же нужна оптимизация — лучше позволять умные ссылки (как в С++) и какие-то новые разработанные языки в духе Rust/go и что-там-еще. Но управление памятью вводит баги толькоо пределенных классов вроде «утечка (не удаление)» или «двойное удаление (краш)» или «переполнение массива/буфера (уязвимость без проверки лимитов)», но они и направлены чтобы определенные вещи оптимизировать (те же аллокации не делать), так что не все баги они вводят, я бы сказал что доменные баги в духе «в эту функцию(метод класса) передал аргумент и забыл инвариант, а аргумент оказался null (простой случай) или невалидный (меньше 0)» и тут динамика никак не поможет а навредит т.к. инварианты видны хуже (и типы, и без комментариев особенно если, а полагаться на «да итак сойдет» как многие пишут на динамике, фиг разберешь что творится в коде, без тестов (всмысле без тестов не разбрешь, но они могут быть), которые еще и читать замучаешься тоже, когда надо в доменной области разобраться)
а как проверить, что оно работает? Я имею ввиду бизнес логику?
И как же с тестами вы проверяете? Сначала надо спроектировать «как оно должно работать» — это вопрос подумать головой, и в этом как раз статическая типизация поможет, потому что явно видно что куда передается, где find usages, где-то срефакторить где было не-красиво и багоопасно из-за запутанных конструкций и только что введенного нового функционала.
Ну и как вы сможете тесты написать на код которого нет — тот еще вопрос… особенно если это очень исследовательская правка, которую можно реализовать кучей способов, а вы пишете сразу тест, который фиксирует API и возможно реализацию связанную с ним, иначе вы не сможете определить даже типы параметров и что с чем взаимодействует, вы всеравно сначала спроектируете, хоть что-то типизированное, в духе «есть объект Factory у него параметр UnitType и он выдает Unit с заданным UnitType» это уже интерфейс и вон сколько в нем типов, и это прсотейший пример, как вы тесты напишете не спроектировав — не представляю. Тем более не представляю если у вас типов нет в синтаксисе, как вы будете писать тесты как доменную область, не зная что в этой доменной области (бизнес-логике) за объекты. И как вы будете рефакторить, если у вас доменные объекты не ищутся по find usages. А если ищутся — так это уже нормальная статическая типизация.
А да — если воппрос только «как проверить когда спроектировали» — я не говорил ни разу что проверять не надо. Я говорю только о том что в языке со статической типизацией, явно определнные типы — помогут вам спроектировать. И помогут написать тесты (которые — не обязаны быть написаны до пректирования).
Это большое заблуждение, что если пишут на динамическом языке, то проверяют типы в тестах. У нас тысячи тестов и ни один не проверяет тип.
И где же в моем утверждении — хоть намек на проверку типу в тестах?
Вот цитата от моего же сообщения
Вот мне тесты читать не надо чтоб понять что тут ожидается UnitType или MaterialType и т.п., и как оно себя поведет и какие в нем ограничения.
Я сообщаю о том что у меня есть UnitType и MaterialType — это типы, они определны явно, да пусть и enum в простом случае (вместо констант 1-2-3 в разных множествах что еще перепутать можно, но я не говорю однозначно что вы используете константы, мало ли способов). И если они явно опеределены как enum, то я могу посмотреть на код и без тестов увидеть что эти аргументы являются «типом юнита» а в случае int в кривой реализации без типов или «нетипизированного unitType аргумента в function getA(unitType) в JS» что тоже самое (! абсолютно) я не могу быть уверен что передается UnitType и что он может быть только UnitType.Pawn или UnitType.King но не 0 и не строка и не фиг-знает-что-еще. Ну а в случае JS с не явной типизацией которая в аргументе не читается, еще и тесты надо писать, но как вы выразились — вы их даже и не пишете (я про проверку типов), ну значит еще больше не можете быть уверены что ваш код работает, если только вы не провели тесты на абсолютно все комбинации (декартово множество — ререал)
— у нас тут велосипед медленно едет, но у него квадратные колеса
— колеса смените
— некогда, надо быстрей педали крутить
если вы будете 12ч работать, а потом мотивировать себя на изучение новой высоко-требовательной-к-ресурсам-мозга информации — ничего у вас не выйдет
потому что не бывает так
как правильно сказали ниже — работайте меньше, ставьте приоритет — чтобы сейчас получать меньше, но в будущем начать заниматься профессионально (после учебы) тем, чем нравится
А когда опыт есть — никто вам 20к не заставит получать, берете и идете на новую работу, где не балду гонять за 20к нужно (или неквалифицированной работой заниматься), а тем умственным высокооплачиваемым трудом, ради которого собственно вы и учились и нарабатывали опыт
8 лет «хорошо учиться» можно по-разному:
— балду гонять, тогда считайте и не учились
— нарабатывать на будущее опыт, тогда вам достаточно еще несколько лет реально пообщаться с заказчиками (на фрилансе, если есть силы, но это для профи) или в фирме (для не-профи) и поразрабатывать с использованием современных инструментов\языков, когда база есть — это уже чтото
проблема в чем?
Либо это не твое. Тут распознается имхо просто — не приносит удовольствия от процесса, нет результатов, хочется заняться чем-то другим. Особенно когда человек идет учиться не туда куда хотел, а куда направили (родители, общество) — это бесполезная трата времени, и человек сразу понимает это, потому что вообще ничего не идет, и нет желания того чтобы оно «шло», т.е. во время учебы это люди «лишь бы сдать», во время работы это «лишь бы быстрей домой с работы вернуться».
Человек сам знает, или по крайней мере может приложить усилия чтобы узнать, нравится оно ему или нет. Тогда вперед — делать дело, или выбирать другое.
Однако работодателю скорее в современных реалиях будет хотеться чтобы вы и код писать умели, и проектировать, и организовать себя могли, и UI сделали в прилоге, и т.п, особенно если это маленькая фирма, т.е. там — требуются люди-оркестры.
Если хотите немного что-то конкретное поизучать, а не распыляться, тогда предположу что вам в большую фирму нужно устраиваться, и нарабатывать там опыт
«нормальное» это по каким критериям? если формально… вот есть требования (критерии), и адекватность решения — сложный вопрос.
для вас в том числе — приведу
ССЫЛКУ
на свой коммент (и далее см ветку)
по 1му-примеру моему — это «нормальное» решение для задачи Интеграции
во 2му-примеру моему — это «нормальное» решение для задачи Оптимизации
… комменты тут помогают, а решение НЕ-костыль
p.s. если рассматривать «средний случай использования комментариев неопытным\незрелым программистом, не разбирающимся как правильно и делающим костыль потому что не-знает\нет-времени» то соглашусь, что коммент там хуже, чем сразу-нормальное-решение-вместо-костыля, однако не находите что такой случай не равен всем-возможным-применениям-комментов-для-аргументации-решения?
Я могу привести еще примеры:
* было сравнено несколько либ, и выбрана конкретная, и в комменте написано «почему» (критерии — скорость, простота, фичи, баги)
* была собственная разработка\исследование, и несколько вариантов перебрали, решили что «лучше так, а не иначе» (иначе — не то что надо из: производительность, артефакты\баги, сложность кода)
Вам непонятно не потому что я опустил контекст, а потому что вы не разбираетесь в предметной области и у вас нет опыта. И даже мне, спустя год от написания этого кода, может быть непонятно-некоторое-время, но посмотря на комменты (сначала), а потом на код — уже можно разобраться.
этот код для конкретного проекта и конкретной задачи, да это ради сильной оптимизации, и использовать в другом месте — нельзя, где есть что-то похожее — там может быть совсем другая реализация, и код там — другой, и как найти — то что надо, и почему оно используется «только тут» и «почему оно» — оно и написано в комменте
* очень любопытно посмотреть как вы будете проверять авто-тестами что GC будет уменьшаться, притом что в профайлере оно проверено вручную и довольно сложным способом
* есть задача т.е требования к оптимальности и GC и есть ее решение — все рядом, если вы кините в тесты, а в коде класса не напишете — надо кучу времени тратить для чтения других файлов с тестами, а тут все — рядом, плюс — комментарий высокоуровневый, а выражать это в виде тестового кода (относительно низкоуровневого) — та еще сложная задача, или писать\выдумывать очень-высокоуровневые-тесты, которые без комментариев (в самих тестах даже) подскажут что и почему тут делается — по опыту сложней чем написать коммент сразу в коде
* во 1х это C# managed-код, и можно вообще не удалять ничего, и все отдастся для GC, но с проблемами и тут — оптимизация
* во 2х — интерфейс у этого класса есть — там есть и FreeAll для очистки всех объектов (а там показано в комменте что по-одному они не очищаются), и тут в комменте описано время жизни объекта: объект HandleOfContour не самодостаточный, он — прокси, а коллекция лежит внутри BuilderOfContours, и поэтому освобождать все должен — Builder
Не очень понял что за логика «креационистов» и причем тут программирование и проектирование. У нас есть задача — есть ее решение, в том числе очень сильно оптимизированное — во 2м примере кода.
В 1м примере с SMAA — либа для анти-алиасинга интегрирована, с изменениями от базовой (и они описаны в комменте), плюс некоторые подробности почему-и-как, и там тестировать юнит-тестами — нереал это.
Это такой проект, со своими требованиями. Там таких классов не 2, а сотни, и мест с комментариями — тысячи.
Какой контекст еще нужен вам?
А вот теперь гуру TDD и систем типов, и желающих писать код без комментариев, аргументируйте как тут (см мой пример) можно НЕ писать никаких комментариев, и потом как это будет просто поддерживать без-комментариев VS с-комментариями.
Тот, кому легко и все понятно в своих проектах, просто делает относительно простые и рутинные вещи, без обид. Поэтому им (не говорю конкретно кому) досатточно и чистый код, и никаких комментариев. И TDD здесь не причем — всю внутрянку тестить — и усложнять и увеличивать кол-во тестового кода (для покрытия оригинала) это будет высоко-вероятно в 2 раза больше кода (а тесты тоже код), чем комменты написать, хотя да такие вещи стоит тестировать автоматически, но дело в описании почему и зачем и что (в том числе), а не только «правильно ли это работает»
Есть возражения
1) не все можно легко протестировать, например в геймдеве, тем более если есть куча рендерного\визуального, что можно оценить глазами, AI не изобрели; хотя согласен что многие вычислительные штуки надо тестировать автоматически
2) кучу фичей, которые постоянно добавляются\удаляются\меняются — нерентабельно покрывать тестами т.к они поменяются буквально завтра и все тесты (кроме основного кода) выкинуть
3) если это стартап, дело даже не в том что сложно тестировать\нецелесообразно, а в том что по некоторому мнению (я его тоже придерживаюсь) тратиться не столько как вы выразились «не сильно замедляет разработку» а очень даже существенно лишнего времени, я бы сказал что раза в 1.5-2, и за это время может проект стать мало кому нужным, плюс фичи будут выкинуты (см пункт 2) и нужно быстрее (максимально быстро) выходить на рынок
Утрирование. Согласен что если чтото легко покрыть тестами и оно не поменяется завтра, и бизнес выделил время-деньги — это делать нужно, но даже без тестов — писать качественный код — никто не запрещает. И если это вручную тестировать быстрей, а написание автотестов — будет по времени дольше, то се нормально. Но никто еще не может с уверенностью предсказать какой функционал сколько времени в проекте проживет, и заранее закладывать кучу усилий на тесты того, что будет вероятно выкинуто — это как раз «квадратные колеса» имхо, т.е делать недумая просто потому что так ктото «умный» сказал — «TDD везде это тру, он не замедляет» — это не верно.
Т.е использовать диаграммы: Последовательности, Прецедентов, state-machine — самые распространенные, и с их помощью можно именно высокий уровень задачи описать, и иногда детали реализации (но не скатиться в очень детальность).
Хотя та же state-machine (состояний) может быть применена как для высокого уровня, например юзер в состоянии = меню, кликает «в бой» и условно если есть соперники — сервер его помещает в бой сразу, или если нет соперников — ставит в очередь ожидания, и потом когда начнется состояние = бой, он может проиграть или выиграть или ливнуть из игры.
Но может быть и низкий уровень вроде описания работы TCP с его syn\ack и состояниями — что обычно редко когда нужно, но всетаки если и нужно — это низкий или средний уровень, думайте сами — нужны ли они вам, мне — обычно нет.
Т.е разница ключевая тут — сколько еще уровней-ПОД находится, и сколько кода надо написать… если много кода еще предстоит написать (т.е скрыто, за пределами диаграммы) — имхо нормальная диаграмма, и предназначенная для overview\обучения\обсуждения, а вот если кучу подробностей — так это почти уровень кода, и дублирование работы.
Если комментировать ваш первый тезис
Тогда я согласен по большей части
Если вам нужен алгоритм, во всех его проявлениях, т.е не один тест вариант, нужно юзать Диаграмму деятельности.
Есть помоему комбинированная, где есть и дорожки, и полный алгоритм, но это 3я (точно не 1я и не 2я).
p.s. извиняюсь если это выглядело как опровержение, или если тут так принято, писать коменты только ради опровержений. Я просто уточняю — что имею в своем опыте.
И в моей практике «Зачастую на практике важны имено конкретные варианты развития» это значит что «зачастую», а другую диаграмму в принципе и не юзал почти никогда, за не надобностью, но смысл в ней есть конечно, просто обычно — нужны конкретные варианты, и уже их них можно составить прямо сразу Код, минуя диаграмму деятельности, потому что каждый из тест вариантов — можно проверять прям по нужной диаграмме Последовательности.
Если задача — обсудить какието сложные вещи — которые вы не можете быстро сами додумать (потому что не спец, потому что не вы ответственный, потому что надо доказать другим что оно будет или не будет работать) — конечно любой способ донесения информации хорош, но если это происходит часто — все просто научатся UML пользоваться в вашей среде, и далее будет понятно.
А если это на один раз — доносите человеческим языком, задавайте вопросы, обсуждайте — как угодно, но это не смысл и не use-case для UML, и это не его минус.
Те же слова, написанные русскими буквами, зачастую не понятны людям, они задают уточняющие вопросы, чтото переспрашивают… это же не минус русского языка правда?
Просто конкретная диаграмма, как уже высказались, имеет смысл для того, чтобы показать то или иное — общий алгоритм (одной подсистемы, или высокоуровневой системы — разные случаи) или может состояние системы в нужный момент (со всеми объектами, или срез из нужного множества объектов)
То что вы конвертируете какието сущности из UML напрямую в язык-прогр, или наоборот из языка-прогр в UML — только подтверждает то, что вы используете ОДИНАКОВЫЙ УРОВЕНЬ и для UML и для языка-прогр, что не имеет никакого смысла на практике. Имеет смысл РАЗНЫЙ УРОВЕНЬ, например это planerka.info/item/diagrammy-posledovatelnosti написанное человеческим языком и там видно на прилично-высоком-уровне что делается каждый из модулей (или даже людей).
Не соглашусь с этим: если код писали НЕ Вы — перечитывать кучу хитросплетений модулей и имен и зачем это все сделано и как работает — это явно не быстро. Зачастую ВЫСОКИЙ УРОВЕНЬ в виде документации UML или текстового описания коллегой в чате (что одно и тоже — ВЫСОКИЙ уровень ключевое) — будет эффективней для понимания хоть всей системы, хоть конкретного алгоритма (если по нему вам описали).
Для понимания нюансов — вроде как производительно у нас тут работает (где оптимизить, где итак ок), или какие модули с чем связаны вплоть до параметров методов и иерархий классов — разумеется будет накладно описывать в виде UML просто потому что это другой УРОВЕНЬ детализации, его конечно лучше в коде читать.
Например: сначала архитектор создает скелет — условно диаграмму последовательностей из клиента-сервера-юзера и там отображаются кто кому какие ВЫСОКО-УРОВНЕВЫЕ команды посылает, в этом и смысл, не нужно все детали отображать. А если нет деталей — не возможна кодо-генерация, ну можно условно шаблон сгенерировать из классов и пары методов, а потом всеравно начинку (код кучи if-ов и методов и параметров).
Другой пример: рисуется диаграмма state-machine — она СРЕДНЕ-УРОВНЕВАЯ — и предназначена для решения конкретной задачи, условно там есть Init (где начало) и переходу при кликах юзера в UI, в зависимости от условия (if просто другим языком) делается переход в состояние, в котором и UI будет другой и команды перехода из-в будут другими, т.е конкретный алгоритм можно сказать. Но это все еще НЕ-КОД, т.е конкретные детали всеравно придется кодить (вызывать конкретные методы\функции, передавать нужные параметры, рисовать чтото в UI, отслеживать события из UI от юзера и т.п).
Смысл этих уровней — есть. Но он должен быть осмысленным.
Т.е не так что «сделаем диаграммы на все что у нас в проекте будет — а код сгенерируем» — не сработает т.к. вы диаграммами высокого уровня хотите сгенерить код низкого уровня? это же не верно… просто потому что вам в момент проектирования ВЫСОКОГО-уровня неважно(!) что на низком будет, пусть там хоть Set\List\Dict будет, хоть цикл с полным перебором, и какой там UI и какие события — не важно.
В обратную сторону «напишем код — тулза нам сгенерит документацию на UML» частично может работать, но это для ОБУЧЕНИЯ, т.е входа новых лбюдей в этот проект, или написания книжек по паттернам проектирования (это тоже важно).
Зачастую нужен баланс и понимание того что мы сделаем ВЫСОКИЙ уровень системы клиент-серв (например, если у нас эта задача), сделаем СРЕДНИЙ уровень для ПОДСИСТЕМЫ UI с диаграммой состояний, сделаем еще СРЕДНИЙ уровень для ПОДСИСТЕМЫ рендернга 3D графиик на клиенте, и потом еще напишем кучу НИЗКО-уровневой реализации во многих подсистемах (кучу if, имен методов и параметров, их типов, кучу enum и т.п — все это описывать в ВЫСОКО-уровеном UML\диаграмме зачастую нет смысла, если только они не ключевые в понимании системы).
Где-то (уже не помню) читал о том что мышление наше МНОГО-УРОВНЕВОЕ, но вот тулз для поддержки этого мышления в программировании — пока нет, полноценных по крайней мере, т.е то что есть в виде отдельных кодо-генераторов — уже описал — слабо помогает.
Идеально это если бы были и work-flow (как мы делаем, инструкции для программистов по проектированию) и чем — какими тулзами \ генераторами \ IDE, и как все это в общую систему свести.
Были такие попытки в духе RUP… на сколко знаю — не идеально.
ДУМАТЬ УРОВНЯМИ вам никто не мешает. Так и нужно думать. Т.е. создавать (хоть в голове и потом забыть \ хоть текстом и потом выкинуть, ну можно и задокументировать) какойто один высокий уровень, проектировать постепенно и волзвратно средние уровни, и чтобы все это не устаревало — всеравно как-то нужно поддерживать ЗНАНИЯ. Т.е ключевое тут — НЕ-КОД, НЕ-UML, а ЗНАНИЯ! Т.е программист, когда он решает даже потом вернуться к задаче — не код должен дублировать в виде документации, или документацию пере-генерировать в код и потом фиксить это, ему ЛЮБЫМ СПОСОБОМ надо поддерживать РАЗНЫЕ УРОВНИ ЗНАНИЙ. Пока способов поддержки УРОВНЕЙ — мало, и все не очень… т.е ктото будет лениться писать документацию (даже комменты в коде — помогают, если они на ДРУГОМ УРОВНЕ), потом лениться ее править, и они хотят чтобы чтото за них это сделало… нет такой тулзы, чтобы вам сделать много УРОВНЕЙ — по вашему желанию. Поэтому зачастую программисты поддерживают ТОЛЬКО КОД. Ну чтобы единый источник был. Они думают что это ИДЕАЛ, чтоб не дублировать документацию. Но смысл всеравно остается — эти УРОВНИ они должны гдето брать — когда надо пофиксить чтото, добавить функционал, обучить когото — вспомнинать приходиться из головы, или придумывать\ проектировать заного.
Просто как пример привел.
В том же С++ или любом другом языке может быть задача с кучей обрабатываемых данных, причем разных структур и промежуточных, с долгой обработкой, а не раз 1 алгоритм запилил и оптимизий. Очень часто данные надо гонять, и объекты в доменной области есть.
Что до юнити — да можно переиспользовать игровые объекты классически — чтоб не создавать каждый раз новый, но речь не только об этом.
Вот например есть либа для триангуляции, вот она при написании на Си имела производительность Х, потом при переписывании на С++ стала Y, потом на C# стала в разы быстрей чем была на Си, изза аллокаций памяти неоптимальныъх в Си, а в C# сборщик памяти ускорил дело, представляете (можно было бы использовать как контр-пример что мол круто сборка мусора ускоряет). Но потом ее еще оптимизировали на пулы и она стала быстрей чем была, на всех платформах, т.е. на C# с ручным менеджментом памяти стала быстрей чем на C# со сборкой мусора, и быстрей чем на Си с ручным менеджментом.
Либа эта LibTessDotNet на C# и при желании нагуглите ее версии На Си и С++ и т.п.
А в моем проекте кроме ее я юзаю еще кучу разного.
Я как сторонник возлагаю надежды, и даже сообщаю свой опыт, и я писал и на JS и на PHP (уж не говорю это «правильная» там динамика или нет), и вижу что проще не допускать баги в Java или C# и даже в C++, с его управлением памятью, и даже в Си с его старой древней библиотекой, я на всем этом попробовал писать проекты с использованием нормальных доменных моделей, пусть и не из миллиона строк, но опыт у меня есть.
Зависит от проекта. Для подавляющего большинства простых проектов, и для большинства средних/сложных — наверное да, управление памятью в духе сборки мусора — вне ведома программиста, это хорошее решение, в сложных проектах где к тому же нужна оптимизация — лучше позволять умные ссылки (как в С++) и какие-то новые разработанные языки в духе Rust/go и что-там-еще. Но управление памятью вводит баги толькоо пределенных классов вроде «утечка (не удаление)» или «двойное удаление (краш)» или «переполнение массива/буфера (уязвимость без проверки лимитов)», но они и направлены чтобы определенные вещи оптимизировать (те же аллокации не делать), так что не все баги они вводят, я бы сказал что доменные баги в духе «в эту функцию(метод класса) передал аргумент и забыл инвариант, а аргумент оказался null (простой случай) или невалидный (меньше 0)» и тут динамика никак не поможет а навредит т.к. инварианты видны хуже (и типы, и без комментариев особенно если, а полагаться на «да итак сойдет» как многие пишут на динамике, фиг разберешь что творится в коде, без тестов (всмысле без тестов не разбрешь, но они могут быть), которые еще и читать замучаешься тоже, когда надо в доменной области разобраться)
И как же с тестами вы проверяете? Сначала надо спроектировать «как оно должно работать» — это вопрос подумать головой, и в этом как раз статическая типизация поможет, потому что явно видно что куда передается, где find usages, где-то срефакторить где было не-красиво и багоопасно из-за запутанных конструкций и только что введенного нового функционала.
Ну и как вы сможете тесты написать на код которого нет — тот еще вопрос… особенно если это очень исследовательская правка, которую можно реализовать кучей способов, а вы пишете сразу тест, который фиксирует API и возможно реализацию связанную с ним, иначе вы не сможете определить даже типы параметров и что с чем взаимодействует, вы всеравно сначала спроектируете, хоть что-то типизированное, в духе «есть объект Factory у него параметр UnitType и он выдает Unit с заданным UnitType» это уже интерфейс и вон сколько в нем типов, и это прсотейший пример, как вы тесты напишете не спроектировав — не представляю. Тем более не представляю если у вас типов нет в синтаксисе, как вы будете писать тесты как доменную область, не зная что в этой доменной области (бизнес-логике) за объекты. И как вы будете рефакторить, если у вас доменные объекты не ищутся по find usages. А если ищутся — так это уже нормальная статическая типизация.
А да — если воппрос только «как проверить когда спроектировали» — я не говорил ни разу что проверять не надо. Я говорю только о том что в языке со статической типизацией, явно определнные типы — помогут вам спроектировать. И помогут написать тесты (которые — не обязаны быть написаны до пректирования).
И где же в моем утверждении — хоть намек на проверку типу в тестах?
Вот цитата от моего же сообщения
Я сообщаю о том что у меня есть UnitType и MaterialType — это типы, они определны явно, да пусть и enum в простом случае (вместо констант 1-2-3 в разных множествах что еще перепутать можно, но я не говорю однозначно что вы используете константы, мало ли способов). И если они явно опеределены как enum, то я могу посмотреть на код и без тестов увидеть что эти аргументы являются «типом юнита» а в случае int в кривой реализации без типов или «нетипизированного unitType аргумента в function getA(unitType) в JS» что тоже самое (! абсолютно) я не могу быть уверен что передается UnitType и что он может быть только UnitType.Pawn или UnitType.King но не 0 и не строка и не фиг-знает-что-еще. Ну а в случае JS с не явной типизацией которая в аргументе не читается, еще и тесты надо писать, но как вы выразились — вы их даже и не пишете (я про проверку типов), ну значит еще больше не можете быть уверены что ваш код работает, если только вы не провели тесты на абсолютно все комбинации (декартово множество — ререал)