> -- jackbauer писал(а):
> Представим, что у нас последовательность действий не такая короткая, как в этом примере, а немного подлиннее, но все равно линейная:1. получить ресурс12. создать ресурс2 с использованием ресурс13. получить ресурс34. создать ресурс4 с использованием ресурс2 и ресурс35. создать ресурс5 с использованием ресурс16. проделать некоторые операции с использованием ресурс3, 4 и 5.Честно говоря, предыдущий пример был сложнее из-за выпрыгивания из цикла. Здесь всё гораздо проще.## Код:
```
PROCEDURE GetResourcesForXXX (VAR res3, res4, res5: Resource): BOOLEAN;
VAR 
   res1, res2: Resource;
   success: BOOLEAN;
BEGIN
   InitRes1 (res1);
   InitRes2 (res2);
   success := GetRes1 (res1)
      & GetRes2 (res2, res1)
      & GetRes3 (res3)
      & GetRes4 (res4, res2, res3)
      & GetRes5 (res5, res1);
   FreeRes1 (res1);
   FreeRes2 (res2);
   RETURN success
END GetResourcesForXXX;

PROCEDURE XXX;
VAR res3, res4, res5: Resource;
BEGIN
   InitRes3 (res3);
   InitRes4 (res4);
   InitRes5 (res5);
   IF GetResourcesForXXX (res3, res4, res5) THEN
      DoXXX (res3, res4, res5); (* это не обязательно отдельная процедура, просто некий код, использующий ресурсы *)
   END;
   FreeRes3 (res3);
   FreeRes4 (res4);
   FreeRes5 (res5);
END XXX;
```
Процедура GetResourcesForXXX добавлена исключительно ради локализации ресурсов res1 и res2 - таким образом подчёркивается, что они не используются в DoXXX, да и освобождаются пораньше. Если сильно хочется, всё можно слепить в одну процедуру тривиальным рефакторингом.

Предполагается, что не является ошибкой освобождение ресурса, для которого выполнен Init, но не выполнен Get (или Get завершился с ошибкой). Таково подавляющее большинство ресурсов, в том числе в вашем примере: если buf == NULL, то delete [] buf ничего не делает и не выбрасывает ошибку. В Delphi если объект obj: TObject = nil, то obj.Free не вызывает деструктор и не выбрасывает исключение (в отличие от obj.Destroy, например). В WinApi CloseHandle (NULL) ничего не делает, и так далее. Другими словами, InitRes может заключаться просто в присвоении NULL сразу же при объявлении идентификатора (в языке Си), и всё.

Если же для некоторый ресурс следует освобождать только после успешного Get, то добавляется простейшая охрана, как в вашем примере: if (fh) fclose (fh).

Наконец, помните, что это не обязательно код, это прежде всего схема. Читабельность кода - это во многом легкость узнавания схемы. Поэтому не обязательно должны присутствовать дополнительные процедуры, часто достаточно поделить код на блоки краткими комментариями.> -- jackbauer писал(а):
> Теперь допустим, что требования немного поменялись. На шаге 3 нам не нужно больше создавать ресурс3. Получается, что мы не только должны изменить реализацию, но и интерфейсы всех функций, которые следуют за шагом 3.Выяснилось, что функция только одна, да и то не обязательно её создавать. Если же эту процедуру сделать локальной (т.е. поместить внутрь PROCEDURE XXX), то изменения в её интерфейсе никого не коснутся в принципе. Если же процедуру GetResourcesForXXX использует кто-то ещё помимо XXX, то её интерфейс необходимо изменить, так как интерфейс отражает требования к задаче XXX, и эти требования изменились.

Допустим, res3 стал не нужен. Удаляем переменную res3 из процедуры XXX, компилируем. По ошибкам компилятора легко вычищается всё остальное.> -- jackbauer писал(а):
> Так как ты упомянул "грамотную" декомпозицию, то значит ли это, что упомянутый здесь ранее метод вложенных if-ов ты считаешь неприемлимым?Я предпочитаю обращение на "вы". Глубокий уровень вложенности затрудняет чтение, поскольку становится трудно сопоставить начало блока и его конец, тем более если блок занимает более одного экрана по вертикали. Метод считаю приемлемым, но начиная с определённой глубины вложенности лучше перейти в отдельную процедуру (опять же, если не тянется слишком много параметров, и если процедура может пригодиться кому-то ещё для повторного использования). Это путь компромиссов, как и всё искусство дизайна.> -- jackbauer писал(а):
> Поэтому с практической точки зрения, твой подход не работает, или как ещё можно сказать, не масштабируется: с увеличением количества шагов резко возрастает количество действий, которые программист должен проделать, чтобы написать или внести изменение в код.К сожалению, не вижу данного вывода в свете моего ответа. Более того, этот вывод представляется мне несколько странным по сути, ведь на этом же основании следует отказаться от документирования ПО и написания тестов: слишком много работы при изменении интерфейсов или постановки задачи.

Как практикующий программист, добавлю: у меня этот подход работает.