Слуга (шаблон проектирования)
В этой статье есть несколько проблем. Пожалуйста, помогите улучшить его или обсудите эти проблемы на странице обсуждения . ( Узнайте, как и когда удалять эти шаблонные сообщения )
|
В разработке программного обеспечения шаблон слуги определяет объект, используемый для предоставления некоторой функциональности группе классов, без определения этой функциональности в каждом из них. Слуга — это класс, экземпляр которого (или даже просто класс) предоставляет методы принимаются объекты, для которых (или с которыми) сервант что-то делает , которые заботятся о желаемом сервисе, а в качестве параметров .
Описание и простой пример
[ редактировать ]Слуга используется для предоставления некоторого поведения группе классов. Вместо определения этого поведения в каждом классе (или когда мы не можем выделить это поведение в общем родительском классе) оно определяется один раз в Servant.
Например: у нас есть несколько классов, представляющих геометрические объекты (прямоугольник, эллипс и треугольник). Мы можем нарисовать эти объекты на каком-нибудь холсте. Когда нам нужно предоставить метод «перемещения» для этих объектов, мы можем реализовать этот метод в каждом классе или мы можем определить интерфейс, который они реализуют, а затем предложить функциональность «перемещения» в серванте. Интерфейс определяется так, чтобы гарантировать, что обслуживаемые классы имеют методы, необходимые служащему для обеспечения желаемого поведения. Если мы продолжим наш пример, мы определим интерфейс «Movable», указав, что каждый класс, реализующий этот интерфейс, должен реализовать методы «getPosition» и «setPosition». Первый метод получает положение объекта на холсте, а второй устанавливает положение объекта и рисует его на холсте. Затем мы определяем класс-слуга «MoveServant», который имеет два метода «moveTo(Movable movingObject, Positionwhere)» и moveBy(Movable movingObject, int dx, int dy). Класс Servant теперь можно использовать для перемещения любого объекта, реализующего Movable. Таким образом, «перемещающийся» код появляется только в одном классе, который соблюдает правило «разделения ответственности».
Два способа реализации
[ редактировать ]Существует два способа реализации этого шаблона проектирования.
- Пользователь знает серванта (в этом случае ему не нужно знать обслуживаемые классы) и отправляет сообщения со своими запросами экземплярам серванта, передавая обслуживаемые объекты в качестве параметров.
- Обслуживаемые классы (геометрические объекты из нашего примера) не знают о слуге, но реализуют интерфейс «IServiced». Пользовательский класс просто вызывает метод служащего и передает обслуживаемые объекты в качестве параметров. Эта ситуация показана на рисунке 1.
- Обслуживаемые экземпляры знают слугу, и пользователь отправляет им сообщения со своими запросами (в этом случае ей не обязательно знать слугу). Затем обслуживаемые экземпляры отправляют сообщения экземплярам-слугам с запросом на обслуживание.
- На рисунке 2 показана противоположная ситуация, когда пользователь не знает о классе слуг и вызывает непосредственно обслуживаемые классы. Обслуживаемые классы затем запрашивают у слуг достижение желаемой функциональности.
Как реализовать Слугу
[ редактировать ]- Проанализируйте, о каком поведении слуге следует позаботиться. Укажите, какие методы будет определять слуга и что этим методам потребуется от обслуживаемого параметра. Другими словами, что должен предоставить обслуживаемый экземпляр, чтобы методы служащих могли достичь своих целей.
- Проанализируйте, какими способностями должны обладать обслуживаемые классы, чтобы их можно было правильно обслуживать.
- Мы определяем интерфейс, который будет обеспечивать реализацию объявленных методов.
- Определите интерфейс, определяющий запрошенное поведение обслуживаемых объектов. Если какой-то экземпляр хочет, чтобы его обслуживал слуга, он должен реализовать этот интерфейс.
- Определить (или каким-то образом приобрести) указанного слугу (его класс).
- Реализуйте определенный интерфейс с обслуживаемыми классами.
Пример
[ редактировать ]Этот простой пример Java показывает описанную выше ситуацию. Этот пример носит лишь иллюстративный характер и не содержит ни реального рисунка геометрических объектов, ни описания того, как они выглядят.
// Servant class, offering its functionality to classes implementing
// Movable Interface
public class MoveServant {
// Method, which will move Movable implementing class to position where
public void moveTo(Movable serviced, Position where) {
// Do some other stuff to ensure it moves smoothly and nicely, this is
// the place to offer the functionality
serviced.setPosition(where);
}
// Method, which will move Movable implementing class by dx and dy
public void moveBy(Movable serviced, int dx, int dy) {
// this is the place to offer the functionality
dx += serviced.getPosition().xPosition;
dy += serviced.getPosition().yPosition;
serviced.setPosition(new Position(dx, dy));
}
}
// Interface specifying what serviced classes needs to implement, to be
// serviced by servant.
public interface Movable {
public void setPosition(Position p);
public Position getPosition();
}
// One of geometric classes
public class Triangle implements Movable {
// Position of the geometric object on some canvas
private Position p;
// Method, which sets position of geometric object
public void setPosition(Position p) {
this.p = p;
}
// Method, which returns position of geometric object
public Position getPosition() {
return this.p;
}
}
// One of geometric classes
public class Ellipse implements Movable {
// Position of the geometric object on some canvas
private Position p;
// Method, which sets position of geometric object
public void setPosition(Position p) {
this.p = p;
}
// Method, which returns position of geometric object
public Position getPosition() {
return this.p;
}
}
// One of geometric classes
public class Rectangle implements Movable {
// Position of the geometric object on some canvas
private Position p;
// Method, which sets position of geometric object
public void setPosition(Position p) {
this.p = p;
}
// Method, which returns position of geometric object
public Position getPosition() {
return this.p;
}
}
// Just a very simple container class for position.
public class Position {
public int xPosition;
public int yPosition;
public Position(int dx, int dy) {
xPosition = dx;
yPosition = dy;
}
}
Подобный шаблон проектирования: команда
[ редактировать ]Шаблоны проектирования Command и Servant очень похожи, и их реализации часто практически одинаковы. Разница между ними заключается в подходе к проблеме.
- Для шаблона «Слуга» у нас есть несколько объектов, которым мы хотим предложить некоторую функциональность. Мы создаем класс, экземпляры которого предлагают эту функциональность и который определяет интерфейс, который должны реализовать обслуживаемые объекты. Обслуживаемые экземпляры затем передаются в качестве параметров слуге.
- Для шаблона «Команда» у нас есть несколько объектов, которые мы хотим изменить, придав им некоторую функциональность. Итак, мы определяем интерфейс, команды которого и желаемая функциональность должны быть реализованы. Экземпляры этих команд затем передаются исходным объектам в качестве параметров их методов.
Несмотря на то, что шаблоны проектирования Command и Servant похожи, это не значит, что так всегда. Существует ряд ситуаций, когда использование шаблона проектирования Команда не относится к шаблону проектирования Слуга. В таких ситуациях нам обычно нужно передать вызываемым методам просто ссылку на другой метод, который понадобится ему для достижения своей цели. Поскольку мы не можем передавать ссылки на методы во многих языках, нам необходимо передать объект, реализующий интерфейс, который объявляет сигнатуру переданного метода.
См. также
[ редактировать ]Ресурсы
[ редактировать ]Печиновский, Рудольф; Ярмила Павличкова; Любош Павличек (июнь 2006 г.). Давайте сначала изменим подход «Сначала объекты» на шаблоны проектирования (PDF) . Одиннадцатая ежегодная конференция по инновациям и технологиям в области компьютерных наук, Болонский университет .