Шаблон делегирования

05.02.2021

Делегирование (англ. Delegation) — основной шаблон проектирования, в котором объект внешне выражает некоторое поведение, но в реальности передаёт ответственность за выполнение этого поведения связанному объекту. Шаблон делегирования является фундаментальной абстракцией, на основе которой реализованы другие шаблоны - композиция (также называемая агрегацией), примеси (mixins) и аспекты (aspects).

Плюсы

Возможность изменить поведение конкретного экземпляра объекта вместо создания нового класса путём наследования.

Минусы

Этот шаблон обычно затрудняет оптимизацию по скорости в пользу улучшенной чистоты абстракции.

Применимость

Java

Хотя делегирование не поддерживается языком Java, его поддержка присутствует во многих средах разработки.

Примеры

Java

Простой пример

В этом примере на языке Java, класс B имеет метод-заглушку (method stub), который передаёт методы foo() и bar() классу A. Класс B делает вид, что он имеет атрибуты класса A.

Исходный текст на языке java class A { void foo() { System.out.println("A: вызван метод foo()"); } void bar() { System.out.println("A: вызван метод bar()"); } } class B { // Создаём объект, методам которого будет делегироваться поведение. A a = new A(); void foo() { a.foo(); } void bar() { a.bar(); } } public class Main { public static void main(String[] args) { B b = new B(); b.foo(); b.bar(); } }

Сложный пример

Используя интерфейсы, делегирование можно осуществить более гибко и c защитой типов (typesafe). В этом примере, класс C может делегировать либо классу A либо классу B. Класс C имеет методы для переключения между классами A и B. Включение расширения implements улучшает безопасность типа, потому что каждый класс должен выполнять методы в интерфейсе. Основным недостатком является большее количество кода.

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

Исходный текст на языке java /** * Интерфейс описывает действие, которое будет вызываться при наступлении * события от таймера. */ interface TimerAction { void onTime(); } class WakeUpAction implements TimerAction { @Override public void onTime() { System.out.println("Пора вставать!"); } } class ChickenIsReadyAction implements TimerAction { @Override public void onTime() { System.out.println("Цыплёнок готов!"); } } /** * Класс таймера. При определённых условиях вызывается действие TimerAction. */ class Timer { TimerAction action; /** * Функция, которую вызывает программист для установки времени. */ void run() { if (isTime()) { action.onTime(); } } /** * Некоторая функция, которая берет на себя всю работу со временем. Её * реализация не интересна в данном контексте. * * @return */ private boolean isTime() { return true; } public static void main(String[] args) { System.out.println("Введите тип действия:"); Scanner scanner = new Scanner(System.in); String actionType = scanner.nextLine(); Timer timer = new Timer(); if (actionType.equalsIgnoreCase("set wake up timer")) { timer.action = new WakeUpAction(); } else if (actionType.equalsIgnoreCase("set chicken timer")) { timer.action = new ChickenIsReadyAction(); } timer.run(); }

C++

Сложный пример

Этот пример — это версия на C++ сложного примера на Java, приведённого выше. Так как C++ не имеет конструкции интерфейса, ту же самую роль играет полностью абстрактный класс. Преимущества и недостатки в основном те же самые, что и в примере на Java.

Исходный текст на языке c++ #include <iostream> class I { public: virtual void f() = 0; virtual void g() = 0; }; class A : public I { public: void f() { std::cout << "A: вызываем метод f()" << std::endl; } void g() { std::cout << "A: вызываем метод g()" << std::endl; } }; class B : public I { public: void f() { std::cout << "B: вызываем метод f()" << std::endl; } void g() { std::cout << "B: вызываем метод g()" << std::endl; } }; class C : public I { public: // Конструктор C() : m_i ( new A() ) { } // Деструктор virtual ~C() { delete m_i; } void f() { m_i->f(); } void g() { m_i->g(); } // Этими методами меняем поле-объект, чьи методы будем делегировать void toA() { delete m_i; m_i = new A(); } void toB() { delete m_i; m_i = new B(); } private: // Объявляем объект методы которого будем делегировать I * m_i; }; int main() { C c; c.f(); c.g(); c.toB(); c.f(); c.g(); return 0; } /* Output: A: вызываем метод f() A: вызываем метод g() B: вызываем метод f() B: вызываем метод g() */

C#

Исходный текст на языке C# namespace Patterns { interface I { void f(); void g(); } class A : I { public void f() { System.Console.WriteLine("A: вызываем метод f()"); } public void g() { System.Console.WriteLine("A: вызываем метод g()"); } } class B : I { public void f() { System.Console.WriteLine("B: вызываем метод f()"); } public void g() { System.Console.WriteLine("B: вызываем метод g()"); } } class C : I { // Создаём объект, методы которого будем делегировать I i = new A(); public void f() { i.f(); } public void g() { i.g(); } // Этими методами меняем поле-объект, чьи методы будем делегировать public void toA() { i = new A(); } public void toB() { i = new B(); } } class DelegatePattern { static void Main( string[] args ) { C c = new C(); c.f(); c.g(); c.toB(); c.f(); c.g(); System.Console.ReadKey(); } } }

Нетривиальный пример

Это пример случая, часто встречающегося в практике. Стоит задача создать класс для хранения списка сотрудников. Данные каждого сотрудника хранятся в объекте класса Employee. Есть уже готовый и стандартный класс для хранения списка объектов Employee. В нём уже реализованы механизмы для работы со списком (к примеру — выделение памяти, добавление и удаление из списка). Наследование класса списка сотрудников от класса списка объектов здесь неприемлемо, потому как мы получим все методы (даже те, которые нас не интересуют). Кроме того нам придётся в некоторых случаях производить приведение типов. Самый элегантный выход из этого случая — делегировать классу списка сотрудников часть методов класса списка объектов. В правилах ООП лучше всего список объектов представить частным (приватным) методом списка сотрудников. В данном случае доступ к списку возможен через индексатор.

Исходный текст на языке C# using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Employees { /// <summary> /// Класс для хранения данных о сотруднике. /// </summary> class Employee { private string name; private string department; public Employee(string name, string departament) { this.name = name; this.department = departament; } /// <summary> /// Имя сотрудника. /// </summary> public string Name { get { return this.name; } } /// <summary> /// Отдел работы. /// </summary> public string Department { get { return this.department; } } } /// <summary> /// Класс для хранения списка сотрудников. /// </summary> class EmployeesList { private List<Employee> employees = new List<Employee>(); /// <summary> /// Свойство для получения и записи сотрудника по индексу. /// </summary> /// <param name="index">Индекс сотрудника.</param> /// <returns>Сотрудник.</returns> public Employee this[int index] { get { return employees[index]; } set { employees[index] = value; } } /// <summary> /// Добавление нового сотрудника. /// </summary> /// <param name="employee">Новый сотрудник.</param> public void Add(Employee employee) { employees.Add(employee); } /// <summary> /// Удаление существующего сторудника. /// </summary> /// <param name="employee">Сотрудник для удаления.</param> public void Remove(Employee employee) { employees.Remove(employee); } /// <summary> /// Последовательный поиск сотрудника по имени. /// </summary> /// <param name="name">Имя сотрудника.</param> /// <param name="offset">Позиция, с которой следует начинать поиск.</param> /// <returns>Индекс сотрудника.</returns> public int GetIndexOfEmployeeByName(string name, int offset = 0) { for (int i = offset; i < employees.Count; i++) { if (employees[i].Name == name) { return i; } } return -1; } } class Program { static void Main(string[] args) { //Создание списка сотрудников и добавление записей в него EmployeesList empList = new EmployeesList(); empList.Add(new Employee("Шлёнский Дмитрий", "web студия")); empList.Add(new Employee("Кусый Назар", "web студия")); empList.Add(new Employee("Сорока Орест", "web студия")); //Поиск сотрудника Кусый Назар и вывод результата при поиске с начала и со 2-й позиции Console.WriteLine(empList.GetIndexOfEmployeeByName("Кусый Назар").ToString()); Console.WriteLine(empList.GetIndexOfEmployeeByName("Кусый Назар", 2).ToString()); //Поиск и удаление сотрудника Сорока Орест empList.Remove(empList[empList.GetIndexOfEmployeeByName("Сорока Орест")]); } } } Исходный текст на языке C# 2 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Employees { /// <summary> /// Класс для хранения данных о сотруднике. /// </summary> class Employee { private string name; private string department; public Employee(string name, string departament) { this.name = name; this.department = departament; } /// <summary> /// Имя сотрудника. /// </summary> public string Name { get { return this.name; } } /// <summary> /// Отдел работы. /// </summary> public string Department { get { return this.department; } } } /// <summary> /// Класс для хранения списка сотрудников. /// </summary> class EmployeesList { private List<Employee> employees = new List<Employee>(); /// <summary> /// Свойство для получения и записи сотрудника по индексу. /// </summary> /// <param name="index">Индекс сотрудника.</param> /// <returns>Сотрудник.</returns> public Employee this[int index] { get { return employees[index]; } set { employees[index] = value; } } /// <summary> /// Свойство для получения и записи сотрудника по имени. /// </summary> /// <param name="name">Имя сотрудника.</param> /// <returns>Первого сотрудник, у которого совпало имя или null</returns> public Employee this[string name] { get { foreach (Employee item in employees) { if (item.Name == name) return item; } return null; } } /// <summary> /// Добавление нового сотрудника. /// </summary> /// <param name="employee">Новый сотрудник.</param> public void Add(Employee employee) { employees.Add(employee); } /// <summary> /// Удаление существующего сторудника. /// </summary> /// <param name="employee">Сотрудник для удаления.</param> public void Remove(Employee employee) { employees.Remove(employee); } /// <summary> /// Последовательный поиск сотрудника по имени. /// </summary> /// <param name="name">Имя сотрудника.</param> /// <param name="offset">Позиция, с которой следует начинать поиск.</param> /// <returns>Индекс сотрудника.</returns> public int GetIndexOfEmployeeByName(string name, int offset) { int index = -1; for (int i = offset; i < employees.Count; i++) { if (employees[i].Name == name) { index = i; break; } } return index; } /// <summary> /// Последовательный поиск сотрудника по имени. /// </summary> /// <param name="name">Имя сотрудника.</param> /// <returns>Индекс сотрудника.</returns> public int GetIndexOfEmployeeByName(string name) { return GetIndexOfEmployeeByName(name, 0); } } class Program { static void Main(string[] args) { //Создание списка сотрудников и добавление записей в него EmployeesList empList = new EmployeesList(); empList.Add(new Employee("Шлёнский Дмитрий", "web студия")); empList.Add(new Employee("Кусый Назар", "web студия")); empList.Add(new Employee("Сорока Орест", "web студия")); //Поиск сотрудника Кусый Назар и вывод результата при поиске с начала и со 2-й позиции Console.WriteLine(empList.GetIndexOfEmployeeByName("Кусый Назар").ToString()); Console.WriteLine(empList.GetIndexOfEmployeeByName("Кусый Назар", 2).ToString()); //Поиск и удаление сотрудника Сорока Орест empList.Remove(empList["Сорока Орест"]); } } }

Objective-C 2.0

Простой пример

Исходный текст на языке Objective-C enum { GLEngineChangeView }; @interface GLEngine : NSObject { id _delegate; BOOL _event[NUM]; } - (id) delegate; - (void) setDelegate: (id) delegate; - (void) changeView: (GLView *) view; @end @protocol GLEngineDelegate <NSObject> @optional - (BOOL) engine: (GLEngine *) engine changeView: (GLView *) view; @end @interface GLEngine (Internal) - (void) registerDelegate; @end @implementation GLEngine - (id) delegate { return _delegate; } - (void) setDelegate: (id) delegate { _delegate = delegate; [self registerDelegate]; } - (void) changeView: (GLView *) view { if (_event[GLEngineChangeView]) { // делегируем запрос if ([_delegate engine: self changeView: view]) { // используем изменённый вид } else // запрос не был обработан } // или используем стандартный способ } - (void) registerDelegate { if ([_delegate responseToSelector: @selector(engine: changeView:)]) { _event[GLEngineChangeView] = YES; } } @end @interface MyGLEngineDelegate : NSObject <GLEngineDelegate> { // } @end @implementation MyGLEngineDelegate - (BOOL) engine: (Engine *) engine changeView: (GLView *) view { // изменяем вид } @end

Object Pascal

Простой пример

Исходный текст на языке Object Pascal type IDelegateInterface = interface procedure Method1; procedure Method2; end; TClassA = class(TInterfacedObject, IDelegateInterface) public procedure Method1; procedure Method2; end; TClassB = class(TInterfacedObject, IDelegateInterface) public procedure Method1; procedure Method2; end; TClassDel = class(TInterfacedObject, IDelegateInterface) private FInterface: IDelegateInterface; public procedure Method1; procedure Method2; procedure ToClassA; procedure ToClassB; end; implementation { TClassA } procedure TClassA.Method1; begin Writeln('TClassA.Method1'); end; procedure TClassA.Method2; begin Writeln('TClassA.Method2'); end; { TClassB } procedure TClassB.Method1; begin Writeln('TClassB.Method1'); end; procedure TClassB.Method2; begin Writeln('TClassB.Method2'); end; { TClassDel } procedure TClassDel.Method1; begin FInterface.Method1; end; procedure TClassDel.Method2; begin FInterface.Method2; end; procedure TClassDel.ToClassA; begin FInterface := TClassA.Create; end; procedure TClassDel.ToClassB; begin FInterface := TClassB.Create; end;

Нетривиальный пример

Этот пример — это версия на Object Pascal нетривиального примера, приведённого выше.

Исходный текст на языке Object Pascal unit UnitEmployeers; interface uses Contnrs; type // Класс для хранения данных о сотруднике TEmployee = class private FName: string; FDepartament: string; public constructor Create(Name, Departament: string); published property Name: string read FName; property Departament: string read FDepartament; end; // Класс для хранения списка сотрудников TEmployeersList = class private // Объект класса "список объектов" FEmployeersList: TObjectList; function GetEmployee(Index: Integer): TEmployee; procedure SetEmployee(Index: Integer; const Value: TEmployee); public constructor Create; destructor Destroy; override; function Add(Employee: TEmployee): Integer; procedure Remove(Employee: TEmployee); function IndexEmployeeByName(Name: string; Offset: Integer = 0): Integer; property Employeers[Index: Integer]: TEmployee read GetEmployee write SetEmployee; default; end; implementation { TEmployee } constructor TEmployee.Create(Name, Departament: string); begin FName := Name; FDepartament := Departament; end; { TEmployeersList } constructor TEmployeersList.Create; begin // Создаём объект методы которого будем делегировать FEmployeersList := TObjectList.Create; end; destructor TEmployeersList.Destroy; begin FEmployeersList.Free; inherited; end; function TEmployeersList.GetEmployee(Index: Integer): TEmployee; begin Result := FEmployeersList[Index] as TEmployee; end; procedure TEmployeersList.SetEmployee(Index: Integer; const Value: TEmployee); begin FEmployeersList[Index] := Value; end; function TEmployeersList.IndexEmployeeByName(Name: string; Offset: Integer = 0): Integer; // Последовательный поиск сотрудника по имени // Через аргумент Offset можно задавать позицию с которой вести поиск. // Если сотрудник не найден вернёт значение меньше ноля (-1) var Index: Integer; begin Result := -1; // Предполагаем что его нет в списке for Index := FEmployeersList.Count - 1 downto Offset do if (FEmployeersList[Index] as TEmployee).Name = Name then begin Result := Index; Exit; end; end; function TEmployeersList.Add(Employee: TEmployee): Integer; begin Result := FEmployeersList.Add(Employee); end; procedure TEmployeersList.Remove(Employee: TEmployee); begin FEmployeersList.Remove(Employee); end; end.

К сожалению, не все программисты применяют шаблон делегирования. Например, фирма Borland (разработчик среды программирования Delphi) в своей стандартной библиотеке классов наследовала вышеупомянутый класс списка объектов TObjectList от класса списка указателей TList. Это вызвало недовольство среди некоторых опытных программистов.

PHP5

Простой пример

Этот пример — это версия на PHP простого примера на Java, приведённого выше.

Исходный текст на языке PHP5 <?php class A { public function f() { print "А: Вызываем метод f()<br />"; } public function g() { print "А: Вызываем метод g()<br />"; } } class C { private $_a; public function __construct() { $this->_a = new A; } public function f() { $this->_a->f(); } public function g() { $this->_a->g(); } public function y() { print "C: вызываем метод y()<br />"; } } $obj = new C; $obj->f(); $obj->g(); $obj->y(); ?>

Сложный пример

Этот пример — это версия на PHP сложного примера на Java, приведённого выше.

Исходный текст на языке PHP5 <?php // используем интерфейс для безопасности типа interface I { public function f(); public function g(); } class A implements I { public function f() { print "А: Вызываем метод f()<br />"; } public function g() { print "А: Вызываем метод g()<br />"; } } class B implements I { public function f() { print "B: Вызываем метод f()<br />"; } public function g() { print "B: Вызываем метод g()<br />"; } } class C implements I { private $_i; // создаём объект, методы которого будем делегировать public function __construct() { $this->_i = new A; } // этими методами меняем поле-объект, чьи методы будем делегировать public function toA() { $this->_i = new A; } public function toB() { $this->_i = new B; } // делегированые методы public function f() { $this->_i->f(); } public function g() { $this->_i->g(); } } $obj = new C; $obj->f(); $obj->g(); $obj->toB(); $obj->f(); $obj->g(); ?>

Нетривиальный пример

Этот пример — это версия на PHP нетривиального примера, приведённого выше.

Исходный текст на языке PHP5 <?php // класс для хранения данных о сотруднике class Employee { private $_name; private $_departament; public function __construct($name, $departament) { $this->_name = $name; $this->_departament = $departament; } public function getName() { return $this->_name; } public function getDepartament() { return $this->_departament; } } // класс для хранения списка объектов class ObjectList { private $_objList; public function __construct() { $this->free(); } /** *чтобы не скучать! */ public function free() { $this->_objList = array(); } public function count() { return count($this->_objList); } public function add($obj) { array_push($this->_objList, $obj); } public function remove($obj) { $k = array_search( $obj, $this->_objList, true ); if ( $k !== false ) { unset( $this->_objList[$k] ); } } public function get($index) { return $this->_objList[$index]; } public function set($index, $obj) { $this->_objList[$index] = $obj; } } // класс для хранения сотрудников class EmployeeList { // объект класса "список объектов" private $_employeersList; public function __construct(){ // создаём объект методы которого будем делегировать $this->_employeersList = new ObjectList; } public function getEmployer($index) { return $this->_employeersList->get($index); } public function setEmployer($index, Employee $objEmployer) { $this->_employeersList->set($index, $objEmployer); } public function __destruct() { $this->_employeersList->free(); } public function add(Employee $objEmployer) { $this->_employeersList->add($objEmployer); } public function remove(Employee $objEmployer) { $this->_employeersList->remove($objEmployer); } // последовательный поиск сотрудника по имени // через аргумент $offset можно задавать позицию с которой вести поиск. // если сотрудник не найден вернёт значение меньше ноля (-1) public function getIndexByName($name, $offset=0) { $result = -1; // предполагаем, что его нету в списке $cnt = $this->_employeersList->count(); for ($i = $offset; $i < $cnt; $i++) { if ( !strcmp( $name, $this->_employeersList->get($i)->getName() ) ) { $result = $i; break; } } return $result; } } $obj1 = new Employee("Танасийчук Степан", "web студия"); $obj2 = new Employee("Кусый Назар", "web студия"); $obj3 = new Employee("Сорока Орест", "web студия"); $objList = new EmployeeList(); $objList->add($obj1); $objList->add($obj2); $objList->add($obj3); echo "<pre>"; print_r($objList); echo "<hr>"; $index = $objList->getIndexByName("Кусый Назар"); $obj4 = $objList->getEmployer($index); print_r($obj4); echo "<hr>"; $objList->setEmployer(2, $obj4); print_r($objList); echo "</pre>"; ?>

Python

Простой пример

Исходный текст на языке Python

#coding: utf-8 #python 3 class A: def f(self): print('A : вызываем метод f') def g(self): print('A : вызываем метод g') class C: def __init__(self): self.A = A() def f(self): return self.A.f() def g(self): return self.A.g() c = C() c.f() #A: вызываем метод f c.g() #A: вызываем метод g

JavaScript

Простой пример

Исходный текст на языке JavaScript function A() { this.f = function() { alert("A: вызываем метод f()"); }; this.g = function() { alert("A: вызываем метод g()"); }; } function C() { var a = new A(); this.f = function() { a.f(); }; this.g = function() { a.g(); }; } var c = new C(); c.f(); // "A: вызываем метод f()" c.g(); // "A: вызываем метод g()"

Сложный пример

Исходный текст на языке JavaScript function A() { this.f = function() { alert("A: вызываем метод f()"); }; this.g = function() { alert("A: вызываем метод g()"); }; } function B() { this.f = function() { alert("B: вызываем метод f()"); }; this.g = function() { alert("B: вызываем метод g()"); }; } function C() { // единожды инстанцируем A и B var a = new A(); var b = new B(); var cur = a; // ссылка на текущий объект с реализацией методов; по умолчанию - A this.toA = function() { cur = a; }; this.toB = function() { cur = b; }; this.f = function() { cur.f(); }; this.g = function() { cur.g(); }; } var c = new C(); c.f(); // "A: вызываем метод f()" c.g(); // "A: вызываем метод g()" c.toB(); c.f(); // "B: вызываем метод f()" c.g(); // "B: вызываем метод g()"

Нетривиальный пример

Исходный текст на языке JavaScript function Employee(name, departament) { this.getName = function() { return name; }; this.getDepartament = function() { return departament; }; this.toString = function() { // преобразование в строку для удобного дебага return "Сотрудник "+ name +", "+ departament; }; } function EmployeesList() { var employees = []; this.add = function() { // функция принимает произвольное кол-во аргументов for (var i=0, l=arguments.length; i<l; i++) { if (arguments[i].constructor == Employee) { // проверка типа employees.push(arguments[i]); } } }; this.set = function(obj, index) { if (obj.constructor == Employee) { delete employees[index]; employees[index] = obj; } }; this.remove = function(obj) { for (var i=0, l=employees.length; i<l; i++) { if (employees[i]==obj) { employees.splice(i, 1); i--; l--; } } }; this.getByIndex = function(num) { return employees[num]; }; this.getIndexByName = function(name, offset) { // последовательный поиск сотрудника по имени // через аргумент offset можно задавать позицию с которой вести поиск. (по умолчанию 0) // если сотрудник не найден, вернёт -1 for (var i = offset || 0, l=employees.length; i<l; i++) { if (employees[i].getName()==name) return i; } return -1; }; this.toString = function() { // преобразование в строку для удобного дебага var ret = ""; for (var i=0, l=employees.length; i<l; i++) { ret += i +": "+ employees[i] +" "; } return ret; }; } var o1 = new Employee("Танасийчук Степан", "web студия"); var o2 = new Employee("Кусый Назар", "web студия"); var o3 = new Employee("Сорока Орест", "web студия"); var emps = new EmployeesList(); emps.add(o1, o2, o3); // можно добавлять и по-одиночке alert(emps); // "0: Сотрудник Танасийчук Степан, web студия // 1: Сотрудник Кусый Назар, web студия // 2: Сотрудник Сорока Орест, web студия" var obj4 = emps.getByIndex( emps.getIndexByName("Кусый Назар") ); // получаем ссылку на сотрудника alert(obj4); // "Сотрудник Кусый Назар, web студия" emps.set(obj4, 2); // вместо 2го (от ноля) сотрудника вставляем obj4 alert(emps); // "0: Сотрудник Танасийчук Степан, web студия // 1: Сотрудник Кусый Назар, web студия // 2: Сотрудник Кусый Назар, web студия" emps.remove(obj4); // удаляем сотрудника obj4 alert(emps); // "0: Сотрудник Танасийчук Степан, web студия"

VB.NET

Сложный пример

Исходный текст на VB.NET Namespace Patterns Interface I Sub f() Sub g() End Interface Class A Implements I Public Sub f() Implements I.f System.Console.WriteLine("A: вызываем метод f()") End Sub Public Sub g() Implements I.g System.Console.WriteLine("A: вызываем метод g()") End Sub End Class Class B Implements I Public Sub f() Implements I.f System.Console.WriteLine("B: вызываем метод f()") End Sub Public Sub g() Implements I.g System.Console.WriteLine("B: вызываем метод g()") End Sub End Class Class C Implements I ' Создаём объект, методы которого будем делегировать Private i As I = New A() Public Sub f() Implements i.f i.f() End Sub Public Sub g() Implements i.g i.g() End Sub ' Этими методами меняем поле-объект, чьи методы будем делегировать Public Sub toA() i = New A() End Sub Public Sub toB() i = New B() End Sub End Class Class DelegatePattern Shared Sub Main() Dim c As New C() c.f() c.g() c.toB() c.f() c.g() System.Console.ReadKey() End Sub End Class End Namespace

Нетривиальный пример

Исходный текст на VB.NET Imports System.Collections.Generic Imports System.Linq Imports System.Text Namespace Employees ''' <summary> ''' Класс для хранения данных о сотруднике. ''' </summary> Class Employee Private m_name As String Private m_department As String Public Sub New(ByVal name As String, ByVal departament As String) Me.m_name = name Me.m_department = departament End Sub ''' <summary> ''' Имя сотрудника. ''' </summary> Public ReadOnly Property Name() As String Get Return Me.m_name End Get End Property ''' <summary> ''' Отдел работы. ''' </summary> Public ReadOnly Property Department() As String Get Return Me.m_department End Get End Property End Class ''' <summary> ''' Класс для хранения списка сотрудников. ''' </summary> Class EmployeesList Private employees As New List(Of Employee)() ''' <summary> ''' Свойство для получения и записи сотрудника по индексу. ''' </summary> ''' <param name="index">Индекс сотрудника.</param> ''' <returns>Сотрудник.</returns> Default Public Property Item(ByVal index As Integer) As Employee Get Return employees(index) End Get Set(ByVal value As Employee) employees(index) = value End Set End Property ''' <summary> ''' Добавление нового сотрудника. ''' </summary> ''' <param name="employee">Новый сотрудник.</param> Public Sub Add(ByVal employee As Employee) employees.Add(employee) End Sub ''' <summary> ''' Удаление существующего сторудника. ''' </summary> ''' <param name="employee">Сотрудник для удаления.</param> Public Sub Remove(ByVal employee As Employee) employees.Remove(employee) End Sub ''' <summary> ''' Последовательный поиск сотрудника по имени. ''' </summary> ''' <param name="name">Имя сотрудника.</param> ''' <returns>Индекс сотрудника.</returns> Public Function GetIndexOfEmployeeByName(ByVal name As String) As Integer Dim index As Integer = -1 For i As Integer = 0 To employees.Count - 1 If employees(i).Name = name Then index = i Exit For End If Next Return index End Function ''' <summary> ''' Последовательный поиск сотрудника по имени. ''' </summary> ''' <param name="name">Имя сотрудника.</param> ''' <param name="offset">Позиция, с которой следует начинать поиск.</param> ''' <returns>Индекс сотрудника.</returns> Public Function GetIndexOfEmployeeByName(ByVal name As String, ByVal offset As Integer) As Integer Dim index As Integer = -1 For i As Integer = offset To employees.Count - 1 If employees(i).Name = name Then index = i Exit For End If Next Return index End Function End Class Class Program Shared Sub Main() 'Создание списка сотрудников и добавление записей в него Dim empList As New EmployeesList() empList.Add(New Employee("Шлёнский Дмитрий", "web студия")) empList.Add(New Employee("Кусый Назар", "web студия")) empList.Add(New Employee("Сорока Орест", "web студия")) 'Поиск сотрудника Кусый Назар и вывод результата при поиске с начала и со 2-й позиции Console.WriteLine(empList.GetIndexOfEmployeeByName("Кусый Назар").ToString()) Console.WriteLine(empList.GetIndexOfEmployeeByName("Кусый Назар", 2).ToString()) 'Поиск и удаление сотрудника Сорока Орест empList.Remove(empList(empList.GetIndexOfEmployeeByName("Сорока Орест"))) Console.Read() End Sub End Class End Namespace