Все, что от вас требуется, - это дисциплина.

За последние десять лет я работал примерно с сотней инженеров-программистов. Все они разные, со своими сильными и слабыми сторонами.

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

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

Выполните анализ компромиссов

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

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

Давайте посмотрим, как это работает на практике.

Представьте, что вы создали два простых класса с некоторой логикой внутри:

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

Анализ компромиссов - это процесс записи всех подходов к проблеме с указанием плюсов и минусов каждого.

Вот как может выглядеть анализ компромисса:

1. Создайте экземпляр ReportSenderclass в конце GenerateReportmethod, используя новое ключевое слово, и вызовите Sendmethod.

Плюсы: Самый простой подход, требующий минимум времени на реализацию.

Минусы: два класса будут тесно связаны, потому что ключевое слово new является связующим. Класс GenerateReport нельзя повторно использовать отдельно от ReportSender.Также GenerateReport нельзя тестировать отдельно.

2. Используйте технику внедрения зависимостей, чтобы внедрить интерфейс ReportSenderclass в конструктор GenerateReportclass и вызвать Sendmethod в конце Generatemethod.

Плюсы: два класса слабо связаны. Оба класса можно тестировать изолированно.

Минусы: Что делать, если в приложении помимо ReportSenderclass есть десятки классов, которым нужно что-то делать с отчетом после его создания? Логика GenerateReport будет загрязнена множеством обязанностей по уведомлению всех заинтересованных сторон.

3. Используйте шаблон проектирования Observer. GenerateReportclass должен определять событие ReportGenerationCompleted, на которое каждый заинтересованный объект может подписаться и отреагировать соответствующим образом.

Плюсы: Список подписчиков можно изменять во время работы. Кроме того, эти два класса слабо связаны, потому что GenerateReporteven не нужно знать о существовании ReportSenderclass или его интерфейса.

Минусы: нужно вовремя отказаться от подписки, чтобы избежать утечки памяти.

4. Использовать шаблон разработки "Посредник"…

5. Использовать шаблон разработки агрегатора событий…

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

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

Подумайте о производительности

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

Код часто работает медленно по следующим причинам:

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

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

Насколько быстро мой код обработает больший набор данных?

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

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

Код в строке 12 будет быстро работать с несколькими пользователями, но с миллионами пользователей производительность будет намного хуже, потому что структура данных List поддерживает только линейный поиск. Будет еще хуже, если HasAccessметод вызывается много раз, потому что сложность будет O (n²).

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

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

Выполните углубленный анализ перед кодированием

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

Этап анализа можно разбить на три основные части:

Анализ требований

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

Анализируем дизайн

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

Анализ области воздействия

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



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

Последние мысли

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

Спасибо за внимание! Вы также можете прочитать другие мои статьи: