Пересылка (объектно-ориентированное программирование) - Forwarding (object-oriented programming)

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

Делегация

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

Разница между пересылкой и делегированием заключается в привязке параметра self в оболочке при вызове через оболочку. При делегировании параметр self привязывается к оболочке, при пересылке - к обертке. ... Пересылка - это форма автоматической повторной отправки сообщения; делегирование - это форма наследования с привязкой к родительскому классу (суперклассу) во время выполнения, а не во время компиляции / компоновки, как при «нормальном» наследовании.[1]

Например, учитывая следующий код:

// Отправительпустота п() {  Распечатать("n1");}// Приемникпустота м() {  Распечатать(«m2,»); п();}пустота п() {  Распечатать("n2");}

при делегировании это выведет m2, n1 потому что п () оценивается в контексте исходного (отправляющего) объекта, а при пересылке будет выводиться m2, n2 потому что п () оценивается в контексте принимающего объекта.[1]

При нерегулярном использовании переадресацию часто называют «делегированием» или формой делегирования, но при осторожном использовании они четко различаются по тому, что себя относится к. Хотя делегирование аналогично наследование, позволяя повторное использование поведения (и конкретно повторное использование кода ) без изменение контекста оценки, пересылка аналогична сочинение, поскольку выполнение зависит только от принимающего (члена) объекта, а не от (исходного) отправляющего объекта. В обоих случаях повторное использование является динамическим, то есть определяется во время выполнения (на основе объект которому делегировано или перенаправлено использование), а не статическим, что означает, что определяется во время компиляции / компоновки (на основе учебный класс который унаследован от). Как и наследование, делегирование позволяет объекту-отправителю изменять исходное поведение, но подвержено проблемам, аналогичным хрупкий базовый класс; в то время как пересылка обеспечивает более надежную инкапсуляцию и позволяет избежать этих проблем; видеть композиция выше наследования.[1]

Примеры

Простой пример явной пересылки в Java: экземпляр B переадресовывает звонки в фу метод его а поле:

учебный класс B {    А а;    Т фу() { возвращаться а.фу(); }}

Обратите внимание, что при выполнении a.foo (), то это объект а (подтип А), а не исходный объект (экземпляр B). Дальше, а не обязательно быть примером А: это может быть экземпляр подтипа. В самом деле, А не обязательно даже быть классом: это может быть интерфейс /протокол.

В отличие от наследования, в котором фу определен в суперклассе А (который должен быть классом, а не интерфейсом), и при вызове экземпляра подкласса B, он использует код, определенный в А, но это объект по-прежнему является экземпляром B:

учебный класс А {    Т фу() { /* ... */ };}учебный класс B расширяет А {}

В этом примере Python класс B направляет фу метод и Икс свойство к объекту в его а поле: используя их на б (экземпляр B) то же самое, что использовать их на б.а (пример А к которому они пересылаются).

учебный класс А:    def __в этом__(себя, Икс) -> Никто:        себя.Икс = Икс    def фу(себя):        Распечатать(себя.Икс)учебный класс B:    def __в этом__(себя, а) -> Никто:        себя.а = а    def фу(себя):        себя.а.фу()    @свойство    def Икс(себя):        возвращаться себя.а.Икс    @Икс.сеттер    def Икс(себя, Икс):        себя.а.Икс = Икс    @Икс.удалитель    def Икс(себя):        дель себя.а.Икса = А(42)б = B(а)б.фу()  # Печатает '42'.б.Икс  # Имеет значение '42'б.Икс = 17   # b.a.x теперь имеет значение 17дель б.Икс  # Удаляет b.a.x.

Простой

В этом Ява например, Принтер учебный класс имеет Распечатать метод. Этот метод печати вместо выполнения самой печати перенаправляет объекту класса RealPrinter. Внешнему миру кажется, что Принтер объект делает печать, но RealPrinter объект - это тот, кто действительно выполняет работу.

Экспедирование - это просто передача обязанности кому-то / чему-то другому. Вот простой пример:

учебный класс RealPrinter { // получатель"    пустота Распечатать() {         Система.из.println("Привет, мир!");     }}учебный класс Принтер { // Отправитель"    RealPrinter п = новый RealPrinter(); // создаем приемник    пустота Распечатать() {        п.Распечатать(); // вызывает получателя    }} общественный учебный класс Главный {    общественный статический пустота главный(Нить[] аргументы) {        // внешнему миру кажется, что принтер действительно печатает.        Принтер принтер = новый Принтер();        принтер.Распечатать();    }}

Сложный

Более сложный случай - это Шаблон декоратора что с помощью интерфейсы, переадресацию можно сделать более гибкой и безопасный. «Гибкость» здесь означает, что C не нужно ссылаться на А или же B в любом случае, поскольку переключение переадресации абстрагируется от C. В этом примере класс C может перенаправить в любой класс, реализующий интерфейс я. Учебный класс C есть способ переключиться на другого экспедитора. В том числе орудия статьи улучшает безопасность типа, потому что каждый класс должен реализовывать методы в интерфейсе. Главный компромисс - больше кода.

интерфейс я {	пустота ж();	пустота грамм();} учебный класс А орудия я {	общественный пустота ж() { Система.из.println("A: делаю f ()"); }	общественный пустота грамм() { Система.из.println("A: выполнение g ()"); }} учебный класс B орудия я {	общественный пустота ж() { Система.из.println("B: делаю f ()"); }	общественный пустота грамм() { Система.из.println("B: выполнение g ()"); }} // изменение реализующего объекта во время выполнения (обычно выполняется во время компиляции)учебный класс C орудия я {	я я = ноль;	// пересылка	общественный C(я я){ setI(я); }	общественный пустота ж() { я.ж(); }	общественный пустота грамм() { я.грамм(); } 	// нормальные атрибуты	общественный пустота setI(я я) { это.я = я; }} общественный учебный класс Главный {	общественный статический пустота главный(Нить[] аргументы) {		C c = новый C(новый А());		c.ж();	// вывод: A: выполнение f ()		c.грамм();	// вывод: A: выполнение g ()		c.setI(новый B());		c.ж();	// вывод: B: выполнение f ()		c.грамм();	// вывод: B: выполнение g ()	}}

Приложения

Пересылка используется во многих шаблонах проектирования.[2] Переадресация используется непосредственно в нескольких шаблонах:

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

Рекомендации

  1. ^ а б c Бючи, Мартин; Век, Вольфганг (2000). "Универсальные оболочки" (PDF). ECOOP 2000 - объектно-ориентированное программирование. Конспект лекций по информатике. 1850. стр.212–213. Дои:10.1007/3-540-45102-1_10. ISBN  978-3-540-67660-7.
  2. ^ Гамма, Эрих; Хелм, Ричард; Джонсон, Ральф; Влиссидес, Джон (1995). Паттерны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования. Эддисон-Уэсли. Bibcode:1995dper.book ..... G. ISBN  978-0-201-63361-0.