Тратить больше времени на чтение, а не на написание кода - явный признак недоверия к кодовой базе. Принцип разделения запросов команд (CQS) может исправить это.

Представьте, что каждый раз, когда вы слушаете «Single Ladies», песня меняется.

1-й раз: «Все одинокие дамы ...»

2-й раз: «Каждая дама…»

3-й раз: «Все единственные девушки…»

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

Код имеет смысл, когда он является естественным продолжением знакомых вещей. Вот почему мы даем значимые имена переменным и методам и не называем x1, x2, x3.

Кодекс, пропагандирующий недоверие

Изучите следующий код:

Прежде чем читать дальше, попробуйте угадать, что должен делать каждый метод.

SetTemperature(int temperature) 

Этот метод очевиден, он должен устанавливать температуру машины. Что представляет собой возвращаемый int? Текущая температура? А может код статуса? Без понятия.

GetTemperatureReadings()

На основе имени он должен возвращать список всех показаний температуры. Прохладный.

Alert(int temperature)

Этот метод должен запускать какое-то предупреждение.

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

Намерение позади

Метод SetTemperature () обновляет температуру устройства для мороженого. Метод вернет окончательную температуру машины и добавит ее в список всех показаний.

Однако метод GetTemperatureReadings () вызовет предупреждение, если текущая температура выше 10, и вернет список всех температур.

Эта проблема

Мы не можем этому доверять.

Во-первых, для метода SetTemperature () мы понятия не имели, что он вернет, если не проверять документацию или реализацию. Это означает потерянное время. Вот почему мы чувствуем, что «этот код - отстой».

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

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

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

Рефакторинг в сторону CQS

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

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

Если вы помните только одно из этой статьи, так это:

  • методы команды всегда должны возвращать void
  • методы запроса должны иметь возвращаемый тип, и они НИКОГДА не изменяют состояние системы.

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

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

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

Итак, что случилось?

В SetTemperature () я изменил тип возвращаемого значения на void. Теперь подпись метода вместе с именем передает четкое сообщение: метод - это команда.

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

Пасхальное яйцо. Метод GetTemperatureReadings () по-прежнему не соответствует требованиям CQS. Вы можете понять почему? Ответ в конце статьи.

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

Мы закончили расширенным классом. Пять методов вместо трех. Теперь вы можете возразить, что то, что раньше выполнялось одним вызовом метода, SetTempera (), теперь требует двух вызовов. Это немного неэффективно. На это я говорю: «Это не имеет значения». Улучшение ремонтопригодности и удобочитаемости кода намного перевешивает негатив.

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

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

Правила предназначены для нарушения

Хотя хит «Одинокие дамы» всегда будет одним и тем же, бывают случаи, когда наблюдение за чем-то меняет свое состояние. Например, если вы хотите измерить давление внутри шины, при установке манометра в шине будет потеряно незначительное количество воздуха. Измерение без потери воздуха слишком обременительно. Это того не стоит.

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

  • когда мы хотим pop () стек, хотя можно сделать реализацию, которая следует принципу, в этом случае лучше разбить его
  • в контексте async - ›в C # async вы никогда не должны возвращать void

Нарушение правил может быть полезным, но чтобы делать это правильно, вы должны помнить следующее:

  1. Знайте правило, прежде чем его нарушать
  2. Прежде чем нарушать правило, необходимо понять его.

Выводы

  • Разделяйте методы между командами и запросами.
  • Командные методы должны возвращать void.
  • Методы запроса должны возвращать значение и НИКОГДА не изменять состояние системы.
  • назовите ваши методы кратко

Раствор пасхального яйца:

public List<int> GetTemperatureReadings() //removed call to alert
{
    //the problem was the Add reading to temperature readings.
    //the add was altering the list making this query method into a command. 
    return new List<int>(temperatureReadings); 
}

дальнейшее чтение