Язык программирования C++ для профессионалов



           

Интерфейсы и реализации


Идеальный интерфейс должен

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

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

class X { // пример плохого определения интерфейса Y a; Z b; public: void f(const char* ...); void g(int[],int); void set_a(Y&); Y& get_a(); };

В этом интерфейсе содержится ряд потенциальных проблем:

  • Типы Y и Z используются так, что определения Y и Z должны быть известны во время трансляции.
  • У функции X::f может быть произвольное число параметров неизвестного типа (возможно, они каким-то образом контролируются "строкой формата", которая передается в качестве первого параметра).
  • Функция X::g имеет параметр типа int[]. Возможно это нормально, но обычно это свидетельствует о том, что определение слишком низкого уровня абстракции. Массив целых не является достаточным определением, так как неизвестно из скольких он может состоять элементов.
  • Функции set_a() и get_a(), по всей видимости, раскрывают представление объектов класса X, разрешая прямой доступ к X::a.

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

Язык С++ раскрывает представление класса как часть интерфейса. Это представление может быть скрытым (с помощью private или protected), но обязательно доступным транслятору, чтобы он мог разместить автоматические (локальные) переменные, сделать подстановку тела функции и т.д. Отрицательным следствием этого является то, что использование типов классов в представлении класса может привести к возникновению нежелательных зависимостей. Приведет ли использование членов типа Y и Z к проблемам, зависит от того, каковы в действительности типы Y и Z. Если это достаточно простые типы, наподобие complex или String, то их использование будет вполне допустимым в большинстве случаев. Такие типы можно считать устойчивыми, и необходимость включать определения их классов будет вполне допустимой нагрузкой для транслятора. Если же Y и Z сами являются классами интерфейса большого компонента (например, типа графической системы или системы обеспечения банковских счетов), то прямую зависимость от них можно считать неразумной. В таких случаях предпочтительнее использовать член, являющийся указателем или ссылкой:




Содержание  Назад  Вперед