Делегирование (англ. Delegation) — основной шаблон проектирования, в котором объект внешне выражает некоторое поведение, но в реальности передаёт ответственность за выполнение этого поведения связанному объекту. Шаблон делегирования является фундаментальной абстракцией, на основе которой реализованы другие шаблоны - композиция (также называемая агрегацией), примеси (mixins) и аспекты (aspects).
Делегирование | |
---|---|
Delegation | |
Описан в Design Patterns | Нет |
Плюсы
правитьВозможность изменить поведение конкретного экземпляра объекта вместо создания нового класса путём наследования.
Минусы
правитьЭтот шаблон обычно затрудняет оптимизацию по скорости в пользу улучшенной чистоты абстракции.
Применимость
правитьХотя делегирование не поддерживается языком Java, его поддержка присутствует во многих средах разработки[1].
Примеры
правитьПростой пример
правитьВ этом примере на языке Java, класс B
имеет метод-заглушку (method stub), который передаёт методы foo()
и bar()
классу A
. Класс B
делает вид, что он имеет атрибуты класса A
.
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 улучшает безопасность типа, потому что каждый класс должен выполнять методы в интерфейсе. Основным недостатком является большее количество кода.
Приведем пример. Допустим, нужно реализовать таймер таким образом, чтобы через определённое количество времени вызывалась некоторая функция. Программист таймера хочет предоставить задание функции пользователям своего класса (другим программистам).
/**
* Интерфейс описывает действие, которое будет вызываться при наступлении
* события от таймера.
*/
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++ сложного примера на Java, приведённого выше. Так как C++ не имеет конструкции интерфейса, ту же самую роль играет полностью абстрактный класс. Преимущества и недостатки в основном те же самые, что и в примере на Java.
#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()
*/
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. В нём уже реализованы механизмы для работы со списком (к примеру — выделение памяти, добавление и удаление из списка). Наследование класса списка сотрудников от класса списка объектов здесь неприемлемо, потому как мы получим все методы (даже те, которые нас не интересуют). Кроме того нам придётся в некоторых случаях производить приведение типов. Самый элегантный выход из этого случая — делегировать классу списка сотрудников часть методов класса списка объектов. В правилах ООП лучше всего список объектов представить частным (приватным) методом списка сотрудников. В данном случае доступ к списку возможен через индексатор.
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("Сорока Орест")]);
}
}
}
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["Сорока Орест"]);
}
}
}
Простой пример
править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
Простой пример
править
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 нетривиального примера, приведённого выше.
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. Это вызвало недовольство среди некоторых опытных программистов.
Простой пример
правитьЭтот пример — это версия на PHP простого примера на Java, приведённого выше.
<?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, приведённого выше.
<?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 нетривиального примера, приведённого выше.
<?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
#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(self)
def g(self):
return self._A.g(self)
c = C()
c.f() #A: вызываем метод f
c.g() #A: вызываем метод g
Простой пример
править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()"
Сложный пример
править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()"
Нетривиальный пример
править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] +"\n";
}
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 студия"
Сложный пример
править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
Нетривиальный пример
править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
См. также
правитьПримечания
править- ↑ Брюс Эккель. Философия Java. — 4-е издание. — ООО Издательство "Питер", 2016. — С. 215. — 1165 с.
Литература
править- Джулиан Бакнелл «Фундаментальные алгоритмы и структуры данных в Delphi». Изд. DiaSoft 2003 год
- Гранд М. «Шаблоны проектирования в Java». 2004
- Брюс Эккель "Философия Java". Изд. "Питер" 2016 год
Для улучшения этой статьи по информационным технологиям желательно:
|