Файл: Руководство по стилю программирования и конструированию по.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.11.2023
Просмотров: 857
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
ГЛАВА 13 Нестандартные типы данных
335
Не считайте, что вы не используете глобальные переменные, поместив
все данные в чудовищный объект и передавая его всюду Размещение всех возможных данных в одном огромном объекте может формально удовлетворять принципу отказа от глобальных переменных, но это приводит исключительно к накладным расходам и не создает преимуществ реальной инкапсуляции. Если вы используете глобальные данные, делайте это открыто. Не пытайтесь замаскиро- вать это с помощью объектов, страдающих ожирением.
Дополнительные ресурсы
Далее указаны дополнительные ресурсы, в которых освеща- ются необычные типы данных:
Maguire Steve.
Writing Solid Code. Redmond, WA: Microsoft Press,
1993. Глава 3 содержит отличное обсуждение опасностей использования указате- лей и множество специальных советов по решению проблем с указателями.
Meyers Scott.
Effective C++, 2d ed. Reading, MA: Addison-Wesley, 1998; Meyers Scott. More
Effective C++. Reading, MA: Addison-Wesley, 1996. Как говорится в названии, эти книги содержат большое количество советов по улучшению программ на C++, включая руководство по безопасному и эффективному использованию указателей. В част- ности, «More Effective C++» содержит отличное обсуждение вопросов управления памятью в языке C++.
Контрольный список: применение необычных
типов данных
Структуры
Используете ли вы структуры вместо отдельных переменных для организа- ции и манипуляции группами взаимосвязанных данных?
Рассматривали ли вы создание класса как альтернативу использованию структуры?
Глобальные данные
Действительно ли все переменные объявлены локально или в области види- мости класса, если только они не обязательно должны быть глобальными?
Различаются ли в соглашениях по именованию переменных локальные,
классовые и глобальные данные?
Документированы ли все глобальные переменные?
Свободен ли код от псевдоглобальных данных — мамонтообразных объек- тов, содержащих мешанину из данных, передающихся в каждый метод?
Используются ли методы доступа вместо глобальных данных?
Организованы ли данные и методы доступа к ним в классы?
Предоставляют ли методы доступа уровень абстракции, независимый от реализации используемого типа данных?
Находятся ли все методы доступа на одном уровне абстракции?
Указатели
Изолированы ли операции с указателями в методах?
Корректны ли обращения к указателям или они могут быть «висячими»?
http://cc2e.com/1385
http://cc2e.com/1392
336
ЧАСТЬ III Переменные
Проверяет ли код корректность указателей перед их использованием?
Проверяется ли корректность переменной, на которую ссылается указатель,
перед ее использованием?
Присваивается ли указателям пустое значение после их освобождения?
Использует ли код все необходимые для читабельности переменные-указа- тели?
Освобождаются ли указатели в связных списках в правильном порядке?
Выделяет ли программа «резервный парашют» памяти, чтобы иметь воз- можность аккуратно завершить выполнение в случае нехватки памяти?
Используются ли указатели только как последнее средство, когда другие методы неприменимы?
Ключевые моменты
쐽
Структуры могут помочь сделать программы менее сложными, упростить их понимание и сопровождение.
쐽
Принимая решение использовать структуру, подумайте, не будет ли класс под- ходить лучше.
쐽
Работа с указателями чревата ошибками. Обезопасьте себя, используя методы или классы для доступа к ним и практику защитного программирования.
쐽
Избегайте глобальных переменных не только потому, что они опасны, но и потому что их можно заменить чем-то лучшим.
쐽
Если вы не можете отказаться от глобальных переменных, работайте с ними через методы доступа. Эти методы предоставляют все то же и даже больше, что и глобальные переменные.
ГЛАВА 13 Нестандартные типы данных
337
Часть IV
ОПЕРАТОРЫ
쐽
Глава 14. Организация последовательного кода
쐽
Глава 15. Условные операторы
쐽
Глава 16. Циклы
쐽
Глава 17. Нестандартные управляющие структуры
쐽
Глава 18. Табличные методы
쐽
Глава 19. Общие вопросы управления
338
ЧАСТЬ IV Операторы
Г Л А В А 1 4
Организация
последовательного кода
Содержание
쐽
14.1. Операторы, следующие в определенном порядке
쐽
14.2. Операторы, следующие в произвольном порядке
Связанные темы
쐽
Общие вопросы управления: глава 19
쐽
Код с условными операторами: глава 15
쐽
Код с операторами цикла: глава 16
쐽
Область видимости переменных и объектов: раздел 10.4
В этой главе мы начнем рассматривать программирование не с точки зрения дан- ных, а с точки зрения выражений. Глава представляет самую простую управляю- щую логику программы: размещение выражений и их блоков в последовательном порядке.
Хотя размещение последовательного кода относительно простая задача, некото- рые организационные тонкости влияют на качество, корректность, читабельность и управляемость кода.
14.1. Операторы, следующие
в определенном порядке
Проше всего организовать такие выражения, для которых важен порядок следо- вания. Вот пример:
Пример выражений, для которых важен порядок следования (Java)
data = ReadData();
results = CalculateResultsFromData( data );
PrintResults( results );
http://cc2e.com/1465
ГЛАВА 14 Организация последовательного кода
339
Если только в этом фрагменте кода не произойдет нечто непонятное, выражения должны выполняться в указанном порядке. Данные должны быть прочитаны прежде,
чем результаты могут быть вычислены, а результаты должны быть вычислены прежде, чем их можно будет напечатать.
Основная идея этого примера состоит в зависимостях. Третье выражение зави- сит от второго, второе — от первого. Факт зависимости одного выражения от другого в этом примере понятен из имен методов. А вот здесь зависимости менее очевидны:
Пример выражений, для которых порядок следования важен,
но не настолько очевиден (Java)
revenue.ComputeMonthly();
revenue.ComputeQuarterly();
revenue.ComputeAnnual();
В этом случае квартальный доход вычисляется в предположении, что месячные доходы уже подсчитаны. Знание бухучета, даже в общих чертах, может вам под- сказывать, что квартальные доходы должны вычисляться перед годовыми. Это зависимость, но при простом прочтении кода она не видна. А здесь зависимости не просто не очевидны, но буквально скрыты:
Пример выражений, для которых порядковые зависимости скрыты (Visual Basic)
ComputeMarketingExpense
ComputeSalesExpense
ComputeTravelExpense
ComputePersonnelExpense
DisplayExpenseSummary
Допустим, метод
ComputeMarketingExpense() инициализирует переменные-члены класса, в которые все остальные методы помещают данные. В этом случае его нужно вызывать перед остальными методами. Как это узнать при прочтении кода? Ис- ходя из того, что вызовы методов не содержат параметров, вы могли бы предпо- ложить, что каждый из этих методов использует данные класса. Но вы не можете знать это наверняка, прочитав этот код.
Если зависимости между выражениями требуют размещения их в опре- деленном порядке, требуются дополнительные действия, чтобы сделать зависимости явными.
Организуйте код так, чтобы зависимости были очевидными В предыду- щем примере на Visual Basic
ComputeMarketingExpense() не должен инициализи- ровать классовые переменные. Имя метода предполагает, что
ComputeMarketing-
Expense() работает аналогично ComputeSalesExpense(), ComputeTravelExpense() толь- ко с маркетинговыми данными, а не с данными о продажах или другими расхо- дами. То, что
ComputeMarketingExpense() инициализирует переменные-члены класса,
— случайность, которой следует избегать. Почему инициализация должна выпол- няться в этом методе, а не в двух других? Пока вы не сможете придумать хоро- шую причину для этого, инициализацию классовых переменных следует осуще-
340
ЧАСТЬ IV Операторы ствлять иным методом, например
InitializeExpenseData(). Имя метода явно указы- вает на то, что он должен быть вызван перед другими расчетами расходов.
Называйте методы так, чтобы зависимости были очевидными В при- мере на Visual Basic метод
ComputeMarketingExpense() назван неправильно, посколь- ку он делает больше, чем просто вычисляет расходы на маркетинг: он еще ини- циализирует члены класса. Если вы против создания отдельного метода для ини- циализации данных, дайте по крайней мере методу
ComputeMarketingExpense() имя,
описывающее все выполняемые им функции. В данном случае
ComputeMarke-
tingExpenseAndInitializeMemberData() будет более адекватным именем. Вы можете сказать, что это имя ужасно, потому что слишком длинное. Но оно описывает то,
что делает метод и вовсе не ужасно. Ужасен сам метод!
Используйте параметры методов, чтобы сделать за-
висимости очевидными Возвращаясь к примеру на Visual
Basic, можно сказать, что, поскольку никакие данные меж- ду методами не передаются, неизвестно, используют ли эти методы одни и те же данные. Переписав код так, чтобы происходила передача дан- ных, вы сообщаете, что порядок выполнения имеет значение. Новый код может выглядеть, например, так:
Пример данных, которые позволяют предположить порядковую
зависимость (Visual Basic)
InitializeExpenseData( expenseData )
ComputeMarketingExpense( expenseData )
ComputeSalesExpense( expenseData )
ComputeTravelExpense( expenseData )
ComputePersonnelExpense( expenseData )
DisplayExpenseSummary( expenseData )
Поскольку все методы используют
expenseData, это наводит на мысль, что они могут работать с одними и теми же данными и что порядок выражений может быть важен.
В этом примере лучшим подходом может быть преобразование процедур в функ- ции, которые принимают
expenseData на входе и возвращают обновленное зна- чение
expenseData. Это сделает наличие зависимостей в коде еще более явным.
Пример данных и вызовов методов, которые указывают на порядковую
зависимость (Visual Basic)
expenseData = InitializeExpenseData( expenseData )
expenseData = ComputeMarketingExpense( expenseData )
expenseData = ComputeSalesExpense( expenseData )
expenseData = ComputeTravelExpense( expenseData )
expenseData = ComputePersonnelExpense( expenseData )
DisplayExpenseSummary( expenseData )
Данные могут также указывать, что порядок выполнения не имеет значения, как в этом случае:
Перекрестная ссылка Об ис- пользовании методов и их па- раметров см. главу 5.
ГЛАВА 14 Организация последовательного кода
341
Пример данных, которые не указывают на порядковую зависимость (Visual Basic)
ComputeMarketingExpense( marketingData )
ComputeSalesExpense( salesData )
ComputeTravelExpense( travelData )
ComputePersonnelExpense( personnelData )
DisplayExpenseSummary( marketingData, salesData, travelData, personnelData )
Так как методы в первых четырех строках не имеют общих данных, код подразу- мевает, что порядок их вызова значения не имеет. Поскольку метод в пятой стро- ке использует данные каждого из первых четырех методов, вы можете предполо- жить, что его надо выполнять после всех этих методов.
Документируйте неявные зависимости с помощью коммента-
риев Попробуйте, во-первых, написать код без порядковых зависимо- стей, во-вторых — написать код, который делает зависимости очевидными.
Если вам все еще кажется, что зависимости видны недостаточно ясно, задокумен- тируйте их. Документирование неявных зависимостей — один из аспектов доку- ментирования допущений, сделанных при кодировании, что необходимо для на- писания систем, пригодных для сопровождения и модификации. В примере на
Visual Basic будет полезно поместить такие комментарии:
Пример выражений, в которых порядковые зависимости скрыты,
но разъясняются с помощью комментариев (Visual Basic)
‘ Рассчитываем расходы. В каждом методе используется переменная класса
’ expenseData. Метод DisplayExpenseSummary должен вызываться последним,
’ так как он зависит от данных, вычисленных другими методами.
InitializeExpenseData
ComputeMarketingExpense
ComputeSalesExpense
ComputeTravelExpense
ComputePersonnelExpense
DisplayExpenseSummary
В этом коде не используются методики, проясняющие порядковые зависимости.
Было бы лучше положиться на такие методики, а не на простые комментарии, но если вы сопровождаете код, находящийся под строгим контролем, или почему- либо не можете его улучшать, используйте документирование для компенсации недостатков кодирования.
Проверяйте зависимости с помощью утверждений или кода обработки
ошибок Если последовательность кода достаточно критична, вы можете исполь- зовать утверждения или статусные переменные и код обработки ошибок, чтобы за- документировать необходимый порядок. Например, в конструкторе класса вы мо- жете инициализировать член класса
isExpenseDataInitialized значением false. Затем в
InitializeExpenseData() вы устанавливаете isExpenseDataInitialized в true. Каждая функция, зависящая от инициализации
expenseData, может проверить, установле- но ли значение
isExpenseDataInitialized в true, прежде чем выполнять операции с
expenseData. Если зависимости между методами глубже, вам могут потребоваться такие переменные, как
isMarketingExpenseComputed, isSalesExpenseComputed и т. д.
335
Не считайте, что вы не используете глобальные переменные, поместив
все данные в чудовищный объект и передавая его всюду Размещение всех возможных данных в одном огромном объекте может формально удовлетворять принципу отказа от глобальных переменных, но это приводит исключительно к накладным расходам и не создает преимуществ реальной инкапсуляции. Если вы используете глобальные данные, делайте это открыто. Не пытайтесь замаскиро- вать это с помощью объектов, страдающих ожирением.
Дополнительные ресурсы
Далее указаны дополнительные ресурсы, в которых освеща- ются необычные типы данных:
Maguire Steve.
Writing Solid Code. Redmond, WA: Microsoft Press,
1993. Глава 3 содержит отличное обсуждение опасностей использования указате- лей и множество специальных советов по решению проблем с указателями.
Meyers Scott.
Effective C++, 2d ed. Reading, MA: Addison-Wesley, 1998; Meyers Scott. More
Effective C++. Reading, MA: Addison-Wesley, 1996. Как говорится в названии, эти книги содержат большое количество советов по улучшению программ на C++, включая руководство по безопасному и эффективному использованию указателей. В част- ности, «More Effective C++» содержит отличное обсуждение вопросов управления памятью в языке C++.
Контрольный список: применение необычных
типов данных
Структуры
Используете ли вы структуры вместо отдельных переменных для организа- ции и манипуляции группами взаимосвязанных данных?
Рассматривали ли вы создание класса как альтернативу использованию структуры?
Глобальные данные
Действительно ли все переменные объявлены локально или в области види- мости класса, если только они не обязательно должны быть глобальными?
Различаются ли в соглашениях по именованию переменных локальные,
классовые и глобальные данные?
Документированы ли все глобальные переменные?
Свободен ли код от псевдоглобальных данных — мамонтообразных объек- тов, содержащих мешанину из данных, передающихся в каждый метод?
Используются ли методы доступа вместо глобальных данных?
Организованы ли данные и методы доступа к ним в классы?
Предоставляют ли методы доступа уровень абстракции, независимый от реализации используемого типа данных?
Находятся ли все методы доступа на одном уровне абстракции?
Указатели
Изолированы ли операции с указателями в методах?
Корректны ли обращения к указателям или они могут быть «висячими»?
http://cc2e.com/1385
http://cc2e.com/1392
336
ЧАСТЬ III Переменные
Проверяет ли код корректность указателей перед их использованием?
Проверяется ли корректность переменной, на которую ссылается указатель,
перед ее использованием?
Присваивается ли указателям пустое значение после их освобождения?
Использует ли код все необходимые для читабельности переменные-указа- тели?
Освобождаются ли указатели в связных списках в правильном порядке?
Выделяет ли программа «резервный парашют» памяти, чтобы иметь воз- можность аккуратно завершить выполнение в случае нехватки памяти?
Используются ли указатели только как последнее средство, когда другие методы неприменимы?
Ключевые моменты
쐽
Структуры могут помочь сделать программы менее сложными, упростить их понимание и сопровождение.
쐽
Принимая решение использовать структуру, подумайте, не будет ли класс под- ходить лучше.
쐽
Работа с указателями чревата ошибками. Обезопасьте себя, используя методы или классы для доступа к ним и практику защитного программирования.
쐽
Избегайте глобальных переменных не только потому, что они опасны, но и потому что их можно заменить чем-то лучшим.
쐽
Если вы не можете отказаться от глобальных переменных, работайте с ними через методы доступа. Эти методы предоставляют все то же и даже больше, что и глобальные переменные.
ГЛАВА 13 Нестандартные типы данных
337
Часть IV
ОПЕРАТОРЫ
쐽
Глава 14. Организация последовательного кода
쐽
Глава 15. Условные операторы
쐽
Глава 16. Циклы
쐽
Глава 17. Нестандартные управляющие структуры
쐽
Глава 18. Табличные методы
쐽
Глава 19. Общие вопросы управления
338
ЧАСТЬ IV Операторы
Г Л А В А 1 4
Организация
последовательного кода
Содержание
쐽
14.1. Операторы, следующие в определенном порядке
쐽
14.2. Операторы, следующие в произвольном порядке
Связанные темы
쐽
Общие вопросы управления: глава 19
쐽
Код с условными операторами: глава 15
쐽
Код с операторами цикла: глава 16
쐽
Область видимости переменных и объектов: раздел 10.4
В этой главе мы начнем рассматривать программирование не с точки зрения дан- ных, а с точки зрения выражений. Глава представляет самую простую управляю- щую логику программы: размещение выражений и их блоков в последовательном порядке.
Хотя размещение последовательного кода относительно простая задача, некото- рые организационные тонкости влияют на качество, корректность, читабельность и управляемость кода.
14.1. Операторы, следующие
в определенном порядке
Проше всего организовать такие выражения, для которых важен порядок следо- вания. Вот пример:
Пример выражений, для которых важен порядок следования (Java)
data = ReadData();
results = CalculateResultsFromData( data );
PrintResults( results );
http://cc2e.com/1465
ГЛАВА 14 Организация последовательного кода
339
Если только в этом фрагменте кода не произойдет нечто непонятное, выражения должны выполняться в указанном порядке. Данные должны быть прочитаны прежде,
чем результаты могут быть вычислены, а результаты должны быть вычислены прежде, чем их можно будет напечатать.
Основная идея этого примера состоит в зависимостях. Третье выражение зави- сит от второго, второе — от первого. Факт зависимости одного выражения от другого в этом примере понятен из имен методов. А вот здесь зависимости менее очевидны:
Пример выражений, для которых порядок следования важен,
но не настолько очевиден (Java)
revenue.ComputeMonthly();
revenue.ComputeQuarterly();
revenue.ComputeAnnual();
В этом случае квартальный доход вычисляется в предположении, что месячные доходы уже подсчитаны. Знание бухучета, даже в общих чертах, может вам под- сказывать, что квартальные доходы должны вычисляться перед годовыми. Это зависимость, но при простом прочтении кода она не видна. А здесь зависимости не просто не очевидны, но буквально скрыты:
Пример выражений, для которых порядковые зависимости скрыты (Visual Basic)
ComputeMarketingExpense
ComputeSalesExpense
ComputeTravelExpense
ComputePersonnelExpense
DisplayExpenseSummary
Допустим, метод
ComputeMarketingExpense() инициализирует переменные-члены класса, в которые все остальные методы помещают данные. В этом случае его нужно вызывать перед остальными методами. Как это узнать при прочтении кода? Ис- ходя из того, что вызовы методов не содержат параметров, вы могли бы предпо- ложить, что каждый из этих методов использует данные класса. Но вы не можете знать это наверняка, прочитав этот код.
Если зависимости между выражениями требуют размещения их в опре- деленном порядке, требуются дополнительные действия, чтобы сделать зависимости явными.
Организуйте код так, чтобы зависимости были очевидными В предыду- щем примере на Visual Basic
ComputeMarketingExpense() не должен инициализи- ровать классовые переменные. Имя метода предполагает, что
ComputeMarketing-
Expense() работает аналогично ComputeSalesExpense(), ComputeTravelExpense() толь- ко с маркетинговыми данными, а не с данными о продажах или другими расхо- дами. То, что
ComputeMarketingExpense() инициализирует переменные-члены класса,
— случайность, которой следует избегать. Почему инициализация должна выпол- няться в этом методе, а не в двух других? Пока вы не сможете придумать хоро- шую причину для этого, инициализацию классовых переменных следует осуще-
340
ЧАСТЬ IV Операторы ствлять иным методом, например
InitializeExpenseData(). Имя метода явно указы- вает на то, что он должен быть вызван перед другими расчетами расходов.
Называйте методы так, чтобы зависимости были очевидными В при- мере на Visual Basic метод
ComputeMarketingExpense() назван неправильно, посколь- ку он делает больше, чем просто вычисляет расходы на маркетинг: он еще ини- циализирует члены класса. Если вы против создания отдельного метода для ини- циализации данных, дайте по крайней мере методу
ComputeMarketingExpense() имя,
описывающее все выполняемые им функции. В данном случае
ComputeMarke-
tingExpenseAndInitializeMemberData() будет более адекватным именем. Вы можете сказать, что это имя ужасно, потому что слишком длинное. Но оно описывает то,
что делает метод и вовсе не ужасно. Ужасен сам метод!
Используйте параметры методов, чтобы сделать за-
висимости очевидными Возвращаясь к примеру на Visual
Basic, можно сказать, что, поскольку никакие данные меж- ду методами не передаются, неизвестно, используют ли эти методы одни и те же данные. Переписав код так, чтобы происходила передача дан- ных, вы сообщаете, что порядок выполнения имеет значение. Новый код может выглядеть, например, так:
Пример данных, которые позволяют предположить порядковую
зависимость (Visual Basic)
InitializeExpenseData( expenseData )
ComputeMarketingExpense( expenseData )
ComputeSalesExpense( expenseData )
ComputeTravelExpense( expenseData )
ComputePersonnelExpense( expenseData )
DisplayExpenseSummary( expenseData )
Поскольку все методы используют
expenseData, это наводит на мысль, что они могут работать с одними и теми же данными и что порядок выражений может быть важен.
В этом примере лучшим подходом может быть преобразование процедур в функ- ции, которые принимают
expenseData на входе и возвращают обновленное зна- чение
expenseData. Это сделает наличие зависимостей в коде еще более явным.
Пример данных и вызовов методов, которые указывают на порядковую
зависимость (Visual Basic)
expenseData = InitializeExpenseData( expenseData )
expenseData = ComputeMarketingExpense( expenseData )
expenseData = ComputeSalesExpense( expenseData )
expenseData = ComputeTravelExpense( expenseData )
expenseData = ComputePersonnelExpense( expenseData )
DisplayExpenseSummary( expenseData )
Данные могут также указывать, что порядок выполнения не имеет значения, как в этом случае:
Перекрестная ссылка Об ис- пользовании методов и их па- раметров см. главу 5.
ГЛАВА 14 Организация последовательного кода
341
Пример данных, которые не указывают на порядковую зависимость (Visual Basic)
ComputeMarketingExpense( marketingData )
ComputeSalesExpense( salesData )
ComputeTravelExpense( travelData )
ComputePersonnelExpense( personnelData )
DisplayExpenseSummary( marketingData, salesData, travelData, personnelData )
Так как методы в первых четырех строках не имеют общих данных, код подразу- мевает, что порядок их вызова значения не имеет. Поскольку метод в пятой стро- ке использует данные каждого из первых четырех методов, вы можете предполо- жить, что его надо выполнять после всех этих методов.
Документируйте неявные зависимости с помощью коммента-
риев Попробуйте, во-первых, написать код без порядковых зависимо- стей, во-вторых — написать код, который делает зависимости очевидными.
Если вам все еще кажется, что зависимости видны недостаточно ясно, задокумен- тируйте их. Документирование неявных зависимостей — один из аспектов доку- ментирования допущений, сделанных при кодировании, что необходимо для на- писания систем, пригодных для сопровождения и модификации. В примере на
Visual Basic будет полезно поместить такие комментарии:
Пример выражений, в которых порядковые зависимости скрыты,
но разъясняются с помощью комментариев (Visual Basic)
‘ Рассчитываем расходы. В каждом методе используется переменная класса
’ expenseData. Метод DisplayExpenseSummary должен вызываться последним,
’ так как он зависит от данных, вычисленных другими методами.
InitializeExpenseData
ComputeMarketingExpense
ComputeSalesExpense
ComputeTravelExpense
ComputePersonnelExpense
DisplayExpenseSummary
В этом коде не используются методики, проясняющие порядковые зависимости.
Было бы лучше положиться на такие методики, а не на простые комментарии, но если вы сопровождаете код, находящийся под строгим контролем, или почему- либо не можете его улучшать, используйте документирование для компенсации недостатков кодирования.
Проверяйте зависимости с помощью утверждений или кода обработки
ошибок Если последовательность кода достаточно критична, вы можете исполь- зовать утверждения или статусные переменные и код обработки ошибок, чтобы за- документировать необходимый порядок. Например, в конструкторе класса вы мо- жете инициализировать член класса
isExpenseDataInitialized значением false. Затем в
InitializeExpenseData() вы устанавливаете isExpenseDataInitialized в true. Каждая функция, зависящая от инициализации
expenseData, может проверить, установле- но ли значение
isExpenseDataInitialized в true, прежде чем выполнять операции с
expenseData. Если зависимости между методами глубже, вам могут потребоваться такие переменные, как
isMarketingExpenseComputed, isSalesExpenseComputed и т. д.