Služebník (návrhový vzor)

Služebník (anglicky Servant) je návrhový vzor využívaný v objektově orientovaném programování. Tento vzor řeší problém, kdy potřebujeme definovat společnou funkčnost pro instance více tříd současně, přičemž tyto třídy nemohou mít společného rodiče, kam bychom definici této funkčnosti umístili.

Návrhový vzor Služebník využijeme v případě, když:

  • Několik tříd potřebuje definovat stejnou činnost a nechceme definovat na několika místech stejný kód
  • Objekt má úkol, který naprogramovat buď neumíme, nebo bychom jej sice zvládli, ale víme, že je úloha již naprogramovaná jinde
  • Připravujeme řešení, které chceme definovat dostatečně obecné, aby je mohli používat všichni, kteří je budou v budoucnu potřebovat, a přitom nevíme, kdo budou ti potřební

Implementace vzoru

[editovat | editovat zdroj]

Služebník nepracuje sám, ale komunikuje s obsluhovanými instancemi. Aby mohl instance bezproblémově obsluhovat, klade na ně požadavky, co všechno musejí obsluhované instance umět

Služebník:

  • Definuje rozhraní, v němž deklaruje své požadavky na obsluhované instance
  • Metody služebníka pak budou akceptovat instance tohoto rozhraní jako své parametry

Instance, která chce být obsloužena:

  • Implementuje příslušné rozhraní, aby se mohla vydávat za jeho instanci. Implementací tohoto rozhraní deklaruje, že umí to, co od ní služebník k její plnohodnotné obsluze požaduje.

Terminologie

[editovat | editovat zdroj]

V následujícím popisu jsou použity termíny:

  • klient – objekt, který něco požaduje po obsluhovaných objektech.
  • služebník – objekt, který nabízí nějakou službu objektům, implementujícím rozhraní IObsluhovaný
  • IObsluhovaný – rozhraní, jehož implementaci vyžaduje služebník od obsluhovaných objektů
  • obsluhovaný – Objekt, který implementuje rozhraní IObsluhovaný a je proto schopen být obsluhován služebníkem.

Možná použití

[editovat | editovat zdroj]

Návrhový vzor Služebník používám ve dvou drobně odlišných situacích.

Služebníka zná obsluhovaný objekt

[editovat | editovat zdroj]
Diagram tříd, kde služebníka (Servant) zná obsluhovaný objekt (User)

V prvním případě Klient (Client) posílá Obsluhovanému objektu zprávu se svým požadavkem. Obsluhovaný neumí nebo nechce na zprávu reagovat sám, a proto si 'pozve' Služebníka, který mu pomůže požadovanou funkci realizovat, a předá se jeho metodě jako parametr. Klient přitom vůbec nemusí vědět, že Obsluhovaný použil služeb Služebníka.

Příklad

Tento jednoduchý příklad v programovacím jazyce Java ukazuje situaci, kdy Služebníka (Servant) zná Obsluhovaný (User) objekt. Příklad je pouze ilustrativní a neposkytuje žádné vykreslování geometrických objektů, ani specifikace jak vypadají.

// MoveServant je trida poskytujici funkcionalitu
// tridam implementujicim interface Movable
public class MoveServant {
// Metoda, ktera presune jakoukoliv tridu implementujici Movable
// na zcela novou pozici
public void move(Movable serviced, PositionAbsolute absolute) {
	// nastavi novou pozici
	serviced.setPosition(absolute);
}

// Metoda, ktera presune jakoukoliv tridu implementujici Movable
// na pozici, posunutou v obou smerech o PositionRelative
public void move(Movable serviced, PositionRelative relative) {
	// nacte aktualni absolutni pozici objektu serviced
	PositionAbsolute position = serviced.getPosition();
	// posune aktualni pozici PositionAbsolute o pozici relativni PositionRelative
	position.add(relative);
	// a aktualizuje vysledek
	serviced.setPosition(position);
}
}

// Interface urcujici, co obsluhovane tridy musi implementovat, aby
// mohli byt obslouzeny servantem MoveServant
public interface Movable {
	// metoda setPosition, ktera umoznuje presouvat Movable
	public void setPosition(PositionAbsolute p);
	// metoda getPosition, ktera umoznuje ziskat polohu
	public PositionAbsolute getPosition();
}

// trojuhelnik implementujici rozhrani Movable
public class Triangle implements Movable {
	private PositionAbsolute p; // pro urceni pozice trojuhelniku

	@Override
	public void setPosition(PositionAbsolute p) { // nastaveni pozice trojuhelniku
		this.p = p;
	}

	@Override
	public PositionAbsolute getPosition() { // ziskani pozice trojuhelniku
		return this.p;
	}
}

// ctyrstran implementujici rozhrani Movable
public class Rectangle implements Movable {
	private PositionAbsolute p; // pro urceni pozice ctyrstranu

	@Override
	public void setPosition(PositionAbsolute p) { // nastaveni pozice ctyrstranu
		this.p = p;
	}

	@Override
	public PositionAbsolute getPosition() { // ziskani pozice ctyrstranu
		return this.p;
	}
}

public class PositionAbsolute { //absolutni pozice
	private int x; //souradnice x
	private int y; //souradnice y

	public int getX() { return x; } //getter pro x
	public int getY() { return y; } //getter pro y

	//pricte k absolutni pozici relativni
	public void add(PositionRelative relative) {
		x += relative.getX();
		y += relative.getY();
	}

	public PositionAbsolute(int sx, int sy) { //konstruktor teto tridy
		x = sx;
		y = sy;
	}
}

public class PositionRelative { //relativni pozice/posunuti
	private int x; //souradnice x
	private int y; //souradnice y

	public int getX() { return x; } //getter pro x
	public int getY() { return y; } //getter pro y

	public PositionRelative(int dx, int dy) { //konstruktor teto tridy
		x = dx;
		y = dy;
	}
}

public class Client {
public static void main(String[] args) {
	MoveServant ms = new MoveServant();
	ms.move(new Triangle(), new PositionAbsolute(10, 10));

	MoveServant ms2 = new MoveServant();
	ms2.move(new Rectangle(), new PositionRelative(5, 5));
}
}

Služebníka zná klientský objekt

[editovat | editovat zdroj]
Diagram tříd, kde služebníka (Servant) obsluhovaný objekt (User) nezná

V druhém případě Klient (Client) ví, že Obsluhovaný objekt implementuje rozhraní Obsluhovaný (Movable) a že klientem požadovanou službu umí splnit Služebník (MoveServant). Zavolá proto Služebníka sám a předá mu Obsluhovaný objekt (Triangle nebo Rectangle) jako parametr. Obsluhovaný v tomto případě vůbec nemusí vědět, že je předáván jako parametr některé z metod Služebníka a že je následně obsluhován Služebníkem.

Příklad

Tento jednoduchý příklad v programovacím jazyce Java ukazuje situaci, kdy Služebníka (Servant) zná Klientský (Serviced) objekt. Příklad je pouze ilustrativní a neposkytuje žádné vykreslování geometrických objektů, ani specifikace jak vypadají.

// MoveServant je trida poskytujici funkcionalitu
// tridam implementujicim interface Movable
public class MoveServant {
// Metoda, ktera presune jakoukoliv tridu implementujici Movable
// na zcela novou pozici
public void move(Movable serviced, PositionAbsolute absolute) {
	// nastavi novou pozici
	serviced.setPosition(absolute);
}

// Metoda, ktera presune jakoukoliv tridu implementujici Movable
// na pozici, posunutou v obou smerech o PositionRelative
public void move(Movable serviced, PositionRelative relative) {
	// nacte aktualni absolutni pozici objektu serviced
	PositionAbsolute position = serviced.getPosition();
	// posune aktualni pozici PositionAbsolute o pozici relativni PositionRelative
	position.add(relative);
	// a aktualizuje vysledek
	serviced.setPosition(position);
}
}

// Interface urcujici, co obsluhovane tridy musi implementovat, aby
// mohli byt obslouzeny servantem MoveServant
public interface Movable {
	// metoda setPosition, ktera umoznuje presouvat Movable
	public void setPosition(PositionAbsolute p);
	// metoda getPosition, ktera umoznuje ziskat polohu
	public PositionAbsolute getPosition();
}

// trojuhelnik implementujici rozhrani Movable
public class Triangle implements Movable {
	private PositionAbsolute p; // pro urceni pozice trojuhelniku

	@Override
	public void setPosition(PositionAbsolute p) { // nastaveni pozice trojuhelniku
		this.p = p;
	}

	@Override
	public PositionAbsolute getPosition() { // ziskani pozice trojuhelniku
		return this.p;
	}

	public void move(PositionAbsolute absolute) {
		MoveServant ms = new MoveServant();
		ms.move(this, absolute);
	}

	public void move(PositionRelative relative) {
		MoveServant ms = new MoveServant();
		ms.move(this, relative);
	}
}

// ctyrstran implementujici rozhrani Movable
public class Rectangle implements Movable {
	private PositionAbsolute p; // pro urceni pozice ctyrstranu

	@Override
	public void setPosition(PositionAbsolute p) { // nastaveni pozice ctyrstranu
		this.p = p;
	}

	@Override
	public PositionAbsolute getPosition() { // ziskani pozice ctyrstranu
		return this.p;
	}

	public void move(PositionAbsolute absolute) {
		MoveServant ms = new MoveServant();
		ms.move(this, absolute);
	}

	public void move(PositionRelative relative) {
		MoveServant ms = new MoveServant();
		ms.move(this, relative);
	}
}

public class PositionAbsolute { //absolutni pozice
	private int x; //souradnice x
	private int y; //souradnice y

	public int getX() { return x; } //getter pro x
	public int getY() { return y; } //getter pro y

	//pricte k absolutni pozici relativni
	public void add(PositionRelative relative) {
		x += relative.getX();
		y += relative.getY();
	}

	public PositionAbsolute(int sx, int sy) { //konstruktor teto tridy
		x = sx;
		y = sy;
	}
}

public class PositionRelative { //relativni pozice/posunuti
	private int x; //souradnice x
	private int y; //souradnice y

	public int getX() { return x; } //getter pro x
	public int getY() { return y; } //getter pro y

	public PositionRelative(int dx, int dy) { //konstruktor teto tridy
		x = dx;
		y = dy;
	}
}

public class Client {
public static void main(String[] args) {
	Triangle triangle = new Triangle();
	triangle.move(new PositionAbsolute(10, 10));

	Rectangle rectangle = new Rectangle();
	rectangle.move(new PositionRelative(5, 5));
}
}

Podobné návrhové vzory

[editovat | editovat zdroj]

Návrhový vzor Služebník je velmi podobný vzoru Command (Příkaz). Architektura je stejná, liší se pouze posloupností úvah architekta aplikace.

Literatura

[editovat | editovat zdroj]