Делегирование (объектно-ориентированное программирование) - Delegation (object-oriented programming)

В объектно-ориентированном программировании , делегирование относится к оценке члена ( свойство или метод ) одного объекта (приемник) в контексте другого исходного объекта (отправитель). Делегирование может быть выполнено явно, путем передачи объекта-отправителя объекту-получателю, что может быть выполнено на любом объектно-ориентированном языке ; или неявно, правилами поиска членов языка, который требует языковой поддержки для функции. Неявное делегирование - это фундаментальный метод повторного использования поведения в программировании на основе прототипов , соответствующий наследованию в программировании на основе классов . Самыми известными языками, поддерживающими делегирование на уровне языка, являются Self , который включает в себя понятие делегирования через понятие изменяемых родительских слотов , которые используются при поиске метода при самостоятельных вызовах, и JavaScript ; см. делегирование JavaScript .

Термин делегирование также широко используется для различных других отношений между объектами; подробнее см. делегирование (программирование) . Часто путаемые концепции просто используют другой объект, более точно называемый консультацией или агрегированием ; и оценка члена на одном объекте путем оценки соответствующего члена на другом объекте, особенно в контексте принимающего объекта, что более точно называется пересылкой (когда объект-оболочка не передает себя завернутому объекту). Шаблон делегации является шаблоном проектирования программного обеспечения для реализации делегирования, хотя этот термин также используется свободно для консультаций или передачи.

Обзор

Это ощущение делегирования как функции языка программирования, использующей правила поиска методов для диспетчеризации так называемых самовызовов, было определено Либерманом в его статье 1986 года «Использование прототипов для реализации общего поведения в объектно-ориентированных системах».

Делегирование зависит от динамического связывания , поскольку требует, чтобы вызов данного метода мог вызывать различные сегменты кода во время выполнения. Он используется в macOS (и ее предшественнице NeXTStep ) как средство настройки поведения компонентов программы. Он позволяет реализовать такие реализации, как использование одного класса, предоставляемого ОС, для управления окнами, поскольку класс принимает делегат, зависящий от программы, и может при необходимости переопределять поведение по умолчанию. Например, когда пользователь щелкает поле закрытия, диспетчер окон отправляет делегату вызов windowShouldClose:, и делегат может отложить закрытие окна, если есть несохраненные данные, представленные содержимым окна.

Делегирование можно охарактеризовать (и отличить от пересылки ) как позднее связывание себя :

... сообщения, отправленные в переменную self(или this) в родительском элементе, «вернутся» к объекту, который изначально получил сообщение.

То есть selfв определении метода в принимающем объекте статически не привязан к этому объекту во время определения (например, во время компиляции или когда функция прикреплена к объекту), а во время оценки он привязан к исходному объект.

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

Языковая поддержка для делегирования

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

Преимущество делегирования в том, что оно может происходить во время выполнения и влиять только на подмножество сущностей определенного типа и даже может быть удалено во время выполнения. Наследование, напротив, обычно нацелено на тип, а не на экземпляры, и ограничивается временем компиляции. С другой стороны, наследование может быть статически проверено типом, в то время как делегирование, как правило, невозможно без дженериков (хотя ограниченная версия делегирования может быть статически типобезопасной). Делегирование можно назвать «наследованием во время выполнения для определенных объектов».

Вот пример псевдокода на языке, подобном C # / Java :

class A {
  void foo() {
    // "this" also known under the names "current", "me" and "self" in other languages
    this.bar();
  }

  void bar() {
    print("a.bar");
  }
};

class B {
  private delegate A a; // delegation link

  public B(A a) {
    this.a = a;
  }

  void foo() {
    a.foo(); // call foo() on the a-instance
  }

  void bar() {
    print("b.bar");
  }
};

a = new A();
b = new B(a); // establish delegation between two objects

Вызов b.foo()приведет к печати a.bar , поскольку он thisотносится к исходному объекту-получателю bв контексте a. Возникающая в результате двусмысленность thisназывается объектной шизофренией .

При thisпреобразовании неявного параметра в явный, вызов (в B, с aделегатом) a.foo()преобразуется в A.foo(b), используя тип aдля разрешения метода, но объект делегирования bдля thisаргумента.

При использовании наследования аналогичный код (с использованием заглавных букв, чтобы подчеркнуть, что разрешение основано на классах, а не на объектах):

class A {
  void foo() {
    this.bar();
  }

  void bar() {
    print("A.bar");
  }
};

class B extends A {
  public B() {}

  void foo() {
    super.foo(); // call foo() of the superclass (A)
  }

  void bar() {
    print("B.bar");
  }
};

b = new B();

Вызов b.foo()приведет к B.bar . В этом случае thisоднозначно: существует единственный объект ,, bи this.bar()разрешается в метод подкласса.

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

Двойное наследование

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

class C extends A {
  delegationlink D d;
}

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

Связанные области

Делегирование можно описать как механизм низкого уровня для совместного использования кода и данных между объектами. Таким образом, он закладывает основу для других языковых конструкций. В частности, ролевые языки программирования использовали делегирование, но особенно старые фактически использовали агрегацию , утверждая, что используют делегирование. Это не следует рассматривать как обман, это просто множественное определение того, что означает делегирование (как описано выше).

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

Делегация также была предложена Эрнстом и Лоренцем для решения рекомендаций по аспектно-ориентированному программированию в 2003 году.

Смотрите также

Различать:

Примечания

использованная литература

внешние ссылки