Tjänare (designmönster)
I mjukvaruteknik definierar servantmönstret ett objekt som används för att erbjuda viss funktionalitet till en grupp klasser utan att definiera den funktionaliteten i var och en av dem. En Servant är en klass vars instans (eller till och med bara klass) tillhandahåller metoder som tar hand om en önskad tjänst, medan objekt för vilka (eller med vilka) tjänaren gör något, tas som parametrar .
Beskrivning och enkelt exempel
Servant används för att ge ett visst beteende till en grupp klasser. Istället för att definiera det beteendet i varje klass - eller när vi inte kan räkna ut detta beteende i den gemensamma föräldraklassen - definieras det en gång i Servant.
Till exempel: vi har några klasser som representerar geometriska objekt (rektangel, ellips och triangel). Vi kan rita dessa föremål på någon duk. När vi behöver tillhandahålla en "move"-metod för dessa objekt kan vi implementera den här metoden i varje klass, eller så kan vi definiera ett gränssnitt som de implementerar och sedan erbjuda "move"-funktionen i en servant. Ett gränssnitt definieras för att säkerställa att betjänade klasser har metoder som servanten behöver för att ge önskat beteende. Om vi fortsätter i vårt exempel, definierar vi ett gränssnitt "Movable" som anger att varje klass som implementerar detta gränssnitt måste implementera metoderna "getPosition" och "setPosition". Den första metoden får ett objekts position på en duk och den andra ställer in positionen för ett objekt och ritar det på en duk. Sedan definierar vi en tjänarklass "MoveServant", som har två metoder "moveTo(Movable movedObject, Position where)" och moveBy(Movable movedObject, int dx, int dy). Servant-klassen kan nu användas för att flytta varje objekt som implementerar Movable. Således visas den "rörliga" koden i endast en klass som respekterar regeln "Separation of Concerns".
Två sätt att genomföra
Det finns två sätt att implementera detta designmönster.
- Användaren känner till servanten (i vilket fall han inte behöver känna till de betjänade klasserna) och skickar meddelanden med sina förfrågningar till servantinstanserna och skickar de betjänade objekten som parametrar.
- De betjänade klasserna (geometriska objekt från vårt exempel) vet inte om servant, men de implementerar "IServiced"-gränssnittet. Användarklassen anropar bara servantmetoden och skickar betjänade objekt som parametrar. Denna situation visas i figur 1.
- Betjänade instanser känner tjänaren och användaren skickar meddelanden till dem med hans förfrågningar (i vilket fall hon inte behöver känna betjänten). De betjänade instanserna skickar sedan meddelanden till instanserna av servanten och ber om service.
- På figur 2 visas motsatt situation, där användaren inte känner till betjäntklass och ringer direkt till betjänade klasser. Betjänade klasser ber sedan tjänaren själva att uppnå önskad funktionalitet.
Hur man implementerar Servant
- Analysera vilket beteende tjänare ska ta hand om. Ange vilka metoder servanten kommer att definiera och vad dessa metoder kommer att behöva från betjänad parameter. Med andra ord, vad betjänad instans måste ge, så att tjänare metoder kan uppnå sina mål.
- Analysera vilka förmågor som servas klasser måste ha, så att de kan servas på rätt sätt.
- Vi definierar ett gränssnitt som kommer att genomdriva implementering av deklarerade metoder.
- Definiera ett gränssnitt som anger begärt beteende för betjänade objekt. Om någon instans vill betjänas av en servant måste den implementera detta gränssnitt.
- Definiera (eller skaffa på något sätt) specificerad tjänare (hans klass).
- Implementera definierat gränssnitt med betjänade klasser.
Exempel
Detta enkla Java-exempel visar situationen som beskrivs ovan. Det här exemplet är endast illustrativt och kommer inte att erbjuda någon faktisk ritning av geometriska objekt, eller specifikation av hur de ser ut.
// Servant class, erbjuder sin funktionalitet till klasser som implementerar // Movable Interface public class MoveServant { // Metod, som kommer att flytta Movable implementeringsklass till position där public void moveTo ( Movable serviced , Position where ) { // Gör några andra saker för att se till att den rör sig smidigt och snyggt, det här är // platsen att erbjuda funktionaliteten servad . setPosition ( där ); } // Metod, som kommer att flytta Movable-implementeringsklassen med dx och dy public void moveBy ( Movable serviced , int dx , int dy ) { // detta är platsen att erbjuda funktionaliteten dx += serviced . getPosition (). xPosition ; dy += servad . getPosition (). yPosition ; servad . setPosition ( ny position ( dx , dy )); } } // Gränssnitt som anger vilka betjänade klasser som behöver implementeras för att // betjänas av servant. public interface Movable { public void setPosition ( Position p ); offentlig position getPosition (); } // En av geometriska klasser public class Triangle implements Movable { // Position of the geometric object on some canvas private Position p ; // Metod, som anger positionen för geometriskt objekt public void setPosition ( Position p ) { this . p = p ; } // Metod, som returnerar position för geometriskt objekt public Position getPosition ( ) { return this . p ; } } // En av geometriska klasser public class Ellipse implements Movable { // Position of the geometric object on some canvas private Position p ; // Metod, som anger positionen för geometriskt objekt public void setPosition ( Position p ) { this . p = p ; } // Metod, som returnerar position för geometriskt objekt public Position getPosition ( ) { return this . p ; } } // En av de geometriska klasserna public class Rektangelverktyg Movable { // Position of the geometric object on some canvas private Position p ; // Metod, som anger positionen för geometriskt objekt public void setPosition ( Position p ) { this . p = p ; } // Metod, som returnerar position för geometriskt objekt public Position getPosition ( ) { return this . p ; } } // Bara en mycket enkel containerklass för position. public class Position { public int xPosition ; public int yPosition ; public Position ( int dx , int dy ) { xPosition = dx ; yPosition = dy ; } }
Liknande designmönster: Kommando
Designmönster Command och Servant är mycket lika och implementeringar av dem är ofta praktiskt taget desamma. Skillnaden mellan dem är inställningen till problemet.
- För Servantmönstret har vi några objekt som vi vill erbjuda lite funktionalitet till. Vi skapar en klass vars instanser erbjuder den funktionen och som definierar ett gränssnitt som betjänade objekt måste implementera. Betjänade instanser skickas sedan som parametrar till servern.
- För kommandomönstret har vi några objekt som vi vill modifiera med någon funktionalitet. Så vi definierar ett gränssnitt som ger kommandon vilken önskad funktionalitet som måste implementeras. Förekomster av dessa kommandon skickas sedan till originalobjekt som parametrar för deras metoder.
Även om designmönster Command och Servant är lika betyder det inte att det alltid är så. Det finns ett antal situationer där användningen av designmönster Command inte relaterar till designmönstret Servant. I dessa situationer behöver vi vanligtvis övergå till anropade metoder bara en referens till en annan metod, som den kommer att behöva för att uppnå sitt mål. Eftersom vi inte kan skicka referenser till metoder på många språk, måste vi skicka ett objekt som implementerar ett gränssnitt som deklarerar signaturen för en godkänd metod.
Se även
Resurser
Pecinovský, Rudolf; Jarmila Pavlíčková; Luboš Pavlíček (juni 2006). Låt oss först modifiera objektens första tillvägagångssätt till designmönster ( PDF) . Elfte årliga konferensen om innovation och teknik inom datavetenskaplig utbildning, Bolognas universitet .