Besökare (designmönster)

Den aktuella versionen av sidan har ännu inte granskats av erfarna bidragsgivare och kan skilja sig väsentligt från versionen som granskades den 4 januari 2016; kontroller kräver 26 redigeringar .
Besökare
Besökare
Sorts beteendemässiga
Ändamål utan att ändra huvudklassen , lägg till nya operationer till den.
Strukturera
Gäller i ärenden när det är nödvändigt att utföra en liknande (samma) operation för ett antal klasser.
fördelar
  • ny funktionalitet läggs till i flera klasser samtidigt utan att koden för dessa klasser ändras;
  • låter dig få information om typen av ett objekt;
  • dubbel schemaläggning;
  • möjlighet att beskriva en egen algoritm för varje typ av objekt .
Minus
  • när du byter servad klass måste du ändra mallkoden;
  • det är svårt att lägga till nya klasser, eftersom hierarkin för besökaren och hans söner behöver uppdateras.
Beskrivs i Design Patterns Ja

En  besökare är ett beteendedesignmönster som beskriver en operation som utförs på objekt av andra klasser. När du byter besökare behöver du inte ändra de betjänade klasserna .

Mallen visar den klassiska metoden för att återställa förlorad typinformation utan att behöva dubbelsända downcast .

Problem löst

Du måste göra några frånkopplade operationer på ett antal objekt, men du måste undvika att förorena deras kod. Och det finns inget sätt eller någon önskan att fråga typen av varje nod och kasta pekaren till rätt typ innan du utför den önskade operationen.

Utmana

En eller flera operationer utförs på varje objekt av någon struktur. Du måste definiera en ny operation utan att ändra objektklasserna.

Lösning

För oberoende har besökaren en separat hierarki. Strukturer har ett visst interaktionsgränssnitt.

Användning

Om det finns en chans att den betjänade klasshierarkin kommer att ändras, eller den kommer att vara instabil, eller det offentliga gränssnittet är tillräckligt effektivt för att mallen ska kunna komma åt, så är användningen skadlig.

En basklass skapas Visitormed metoder visit()för varje underklass av föräldern Element. Lägg till en metod accept(visitor)i elementhierarkin. För varje operation som måste utföras på objekt Element, härled en Visitorklass från. Metodimplementationer visit()måste använda klassens publika gränssnitt Element. Som ett resultat: klienter skapar objekt Visitoroch skickar dem till varje objekt Elementgenom att anropa accept().

Rekommendationer

Mallen ska användas om:

Fördelar och nackdelar

Fördelar :

Nackdelar :

Implementering

  1. Lägg till en metod accept(Visitor)i "element"-hierarkin.
  2. Skapa en basklass Visitoroch definiera metoder visit()för varje elementtyp.
  3. Skapa härledda klasser Visitorför varje operation som utförs på element.
  4. Klienten skapar ett objekt Visitoroch skickar det till den anropade metodenaccept().

C++

Implementeringsexempel i C++ #include <iostream> #inkludera <sträng> klass Foo ; klass Bar ; klass Bas ; klass besökare { offentliga : virtuellt void besök ( Foo & ref ) = 0 ; virtuellt void besök ( Bar & ref ) = 0 ; virtuellt void - besök ( Baz & ref ) = 0 ; virtuell ~ Besökare () = default ; }; klasselement { _ offentliga : virtuellt void acceptera ( Besökare & v ) = 0 ; virtuell ~ Element () = default ; }; klass Foo : public Element { offentliga : void accept ( besökare & v ) åsidosätt { v . besök ( * detta ); } }; class Bar : public Element { offentliga : void accept ( besökare & v ) åsidosätt { v . besök ( * detta ); } }; klass Baz : public Element { offentliga : void accept ( besökare & v ) åsidosätt { v . besök ( * detta ); } }; class GetType : public Visitor { offentliga : std :: strängvärde ; _ offentliga : void visit ( Foo & ref ) åsidosätta { värde = "foo" ; } void visit ( Bar & ref ) åsidosätta { värde = "stapel" ; } void visit ( Baz & ref ) åsidosätta { värde = "bas" ; } }; int main () { Foo foo ; Bar bar ; baz baz ; Element * elements [] = { & foo , & bar , & baz }; for ( auto elem : element ) { GetType besökare ; elem -> acceptera ( besökare ); std :: cout << besökare . värde << std :: endl ; } returnera 0 ; }

Java

Exempel på Java -implementering public class Demo { public static void main ( String [] args ) { Point p = new Point2d ( 1 , 2 ); Besökare v = ny Chebyshev (); sid . acceptera ( v ); System . ut . println ( s . getMetric () ); } } gränssnitt Besökare { public void visit ( Point2d p ); offentligt tomrumsbesök ( Point3d p ) ; } abstrakt klass Point { public abstract void accept ( besökare v ); privat dubbel metrisk = - 1 ; public double getMetric () { return metric ; } public void setMetric ( double metric ) { this . metrisk = metrisk ; } } klass Point2d utökar Point { public Point2d ( double x , double y ) { this . x = x ; detta . y = y _ } public void accept ( Besökare v ) { v . besök ( detta ); } privat dubbel x ; public double getX () { return x ; } privat dubbel y ; public double getY () { return y ; } } klass Point3d utökar Point { public Point3d ( double x , double y , double z ) { this . x = x ; detta . y = y _ detta . z = z _ } public void accept ( Besökare v ) { v . besök ( detta ); } privat dubbel x ; public double getX () { return x ; } privat dubbel y ; public double getY () { return y ; } privat dubbel z ; public double getZ () { return z ; } } class Euclid implements Visitor { public void visit ( Point2d p ) { p . setMetric ( Math . sqrt ( p . getX () * p . getX () + p . getY () * p . getY () ) ); } offentligt void besök ( Point3d p ) { p . setMetric ( Math . sqrt ( p . getX ( ) * p . getX () + p . getY () * p . getY () + p . getZ () * p . getZ () ) ); } } klass Chebyshev implementerar Besökare { public void visit ( Point2d p ) { double ax = Math . abs ( p.getX ( ) ) ; double -ay = Math . abs ( s . getY () ); sid . setMetric ( ax > ay ? ax : ay ); } offentligt void besök ( Point3d p ) { double ax = Math . abs ( p.getX ( ) ) ; double -ay = Math . abs ( s . getY () ); dubbel az = Math . abs ( p . getZ () ); dubbel max = ax > ay ? yxa : ay ; if ( max < az ) max = az ; sid . setMetric ( max ); } }

C#

Implementeringsexempel i C# public static class Demo { private static void Main () { Point p = new Point2D ( 1 , 2 ); IVisitor v = ny Chebyshev (); sid . acceptera ( v ); Konsol . WriteLine ( s . Metric ); } } internt gränssnitt IVisitor { void Visit ( Point2D p ); void besök ( Point3Dp ) ; } intern abstrakt klass Point { public double Metric { get ; set ; } = - 1 ; public abstract void Acceptera ( IVbesökare ) ; } intern klass Point2D : Point { public Point2D ( double x , double y ) { X = x ; Y = y _ } public double X { get ; } public double Y { get ; } public override void Acceptera ( IVbesökare ) { besökare . _ besök ( detta ); } } intern klass Point3D : Point { public Point3D ( double x , double y , double z ) { X = x ; Y = y _ Z = z _ } public double X { get ; } public double Y { get ; } public double Z { get ; } public override void Acceptera ( IVbesökare ) { besökare . _ besök ( detta ); } } intern klass Euclid : IVisitor { public void Visit ( Point2D p ) { p . Metrisk = matematik . Sqrt ( p . X * p . X + p . Y * p . Y ); } public void Besök ( Point3D p ) { p . Metrisk = matematik . Sqrt ( p . X * p . X + p . Y * p . Y + p . Z * p . Z ); } } intern klass Chebyshev : IVisitor { public void Visit ( Point2D p ) { var ax = Math . abs ( sid X ) ; varay = matematik . _ Abs ( sid Y ) ; sid . Metrisk = axe > ay ? yxa : ay ; } public void Besök ( Point3D p ) { var ax = Math . abs ( s . X ); varay = matematik . _ Abs ( sid Y ) ; var az = Math . Abs ( sid Z ) ; varmax = ax > ay ? _ yxa : ay ; if ( max < az ) max = az ; sid . Metrisk = max ; } }

PHP

Exempel på implementering i php <?php gränssnitt Visitor { public function visit ( Punkt $point ); } abstract class Point { public abstract function accept ( besökare $besökare ); privat $_metric = - 1 ; public function getMetric () { return $this -> _metric ; } public function setMetric ( $metrisk ) { $this -> _metric = $metrisk ; } } klass Point2d utökar Point { offentlig funktion __construct ( $x , $y ) { $this -> _x = $x ; $this -> _y = $y ; } public function accept ( Besökare $besökare ) { $besökare -> besök ( $detta ); } privat $_x ; public function getX () { return $this -> _x ; } privat $_y ; public function getY () { return $this -> _y ; } } klass Point3d utökar Point { public function __construct ( $x , $y , $z ) { $this -> _x = $x ; $this -> _y = $y ; $this -> _z = $z ; } public function accept ( Besökare $besökare ) { $besökare -> besök ( $detta ); } privat $_x ; public function getX () { return $this -> _x ; } privat $_y ; public function getY () { return $this -> _y ; } privat $_z ; public function getZ () { return $this -> _z ; } } klass Euclid implementerar Besökare { public function visit ( Point $p ) { if ( $p instanceof Point2d ) $p -> setMetric ( sqrt ( $p -> getX () * $p -> getX () + $p -> getY () * $p -> getY () ) ); elseif ( $p instans av Point3d ) $p -> setMetric ( sqrt ( $p -> getX () * $p -> getX () + $p -> getY () * $p -> getY () + $p - > getZ () * $p -> getZ () ) ); } } class Chebyshev implementerar Visitor { public function visit ( Point $p ) { if ( $p instanceof Point2d ){ $ax = abs ( $p -> getX () ); $ay = abs ( $p -> getY () ); $p -> setMetric ( $ax > $ay ? $ax : $ay ); } elseif ( $p instans av Point3d ){ $ax = abs ( $p -> getX () ); $ay = abs ( $p -> getY () ); $az = abs ( $p -> getZ () ); $max = $ax > $ay ? $ax : $ay ; if ( $max < $az ) $max = $az ; $p -> setMetric ( $max ); } } } funktion start (){ $p = new Point2d ( 1 , 2 ); $v = nyChebyshev ( ); $p -> acceptera ( $v ); echo ( $p -> getMetric () ); }; start ();

Python

Implementeringsexempel i Python från abc import ABCMeta , abstraktmetod från att skriva importlista class Spy ( metaclass = ABCMeta ): """ Spionbesökare """ @abstractmethod def visit_military_base ( self , military_base : 'MilitaryBase' ) -> Inga : """ Besök marinens militärbas """ pass @abstractmethod def visit_headquarters ( själv , högkvarter : 'Högkvarter' ) -> Inga : """ Besök arméns högkvarter """ pass class MilitaryFacility ( metaclass = ABCMeta ): """ Militär anläggning - besökt anläggning """ @abstractmethod def accept ( själv , spion : Spy ) -> Ingen : """ Acceptera spionbesökare """ pass klass MilitaryBase ( MilitaryFacility ): """ Ubåtsmilitärbas """ def __init__ ( själv ) -> Ingen : själv . _secret_draftings = 1 själv . _kärnkraftsubåtar = 1 def __repr__ ( self ) -> str : return 'Militärbasen har {} atomubåtar och {} hemliga ritningar' . format ( self . _nuclear_ubåtar , self . _secret_draftings ) def accept ( själv , spion : spion ) -> Ingen : spion . visit_military_base ( själv ) def remove_secret_draftings ( self ) -> None : if self . _hemliga_utkast : själv . _hemliga_utkast -= 1 def remove_nuclear_submarine ( self ) -> None : if self . _atomubåtar : själv . _kärnkraftsubåtar -= 1 @property def is_combat_ready ( self ) -> bool : returnera själv . _atomubåtar > 0 klass Högkvarter ( MilitaryFacility ): """ Arméhögkvarteret """ def __init__ ( själv ) -> Ingen : själv . _generals = 3 själv . _hemliga_dokument = 2 def __repr__ ( self ) -> str : return 'Det finns {} generaler och {} hemliga dokument vid högkvarteret ' . format ( self . _generals , self . _secret_documents ) def accept ( själv , spion : spion ) -> Ingen : spion . visit_headquarters ( själv ) def remove_general ( self ) -> None : if self . _generaler : själv . _generals -= 1 def remove_secret_documents ( self ) -> None : if self . _hemliga_dokument : själv . _hemliga_dokument -= 1 @property def is_command_ready ( self ) -> bool : returnera själv . _generals > 0 klass ScoutSpy ( Spy ): """ Scout (konkret spion) """ def __init__ ( self ): self . _samlad_info = {} # Här känner vi redan till den specifika objekttypen def visit_military_base ( self , military_base : MilitaryBase ) -> None : self . _collected_info [ 'base' ] = 'Militärbas: \n\t {} \n\t Klar: {} ' . format ( str ( militärbas ), 'Ja' om militärbas . is_combat_ready else 'Nej' ) def visit_headquarters ( själv , huvudkontor : Högkvarter ) -> Inget : själv . _collected_info [ 'headquarters' ] = 'Högkvarter: \n\t {} \n\t Kommando: {} ' . format ( str ( huvudkontor ), "Körs" om huvudkontoret . is_command_ready else "Inte i drift" ) def report ( self ) -> str : return 'Information från scouten: \n {} \n ' . format ( ' \n ' . join ( self . _collected_info . values ​​​​()) ) klass JamesBond ( Spy ): """ James Bond (en annan specifik spion) """ def visit_military_base ( self , military_base : MilitaryBase ) -> Ingen : # James Bond besöker militärbasen militärbas . remove_secret_draftings () # stjäl militärbasens hemliga ritningar . remove_nuclear_submarine () # och till slut spränger en atomubåt i luften def visit_headquarters ( själv , huvudkontor : Headquarters ) -> Inga : # James Bond besöker huvudkontoret . remove_general () # ... huvudkontor . remove_general () # ... huvudkontor . remove_secret_documents () # ... huvudkontor . remove_general () # Förstör alla generaler sekventiellt högkvarter . remove_secret_documents () # och stjäl alla hemliga dokument om __namn__ == '__main__' : bas = MilitaryBase () hq = Högkvarter () # Oavsett vilka MilitaryFacility - anläggningar = [ bas , hq ] # typ: List[MilitaryFacility] scout = ScoutSpy () print ( 'Skicka en scout... \n ' ) för f i anläggningar : f . acceptera ( scout ) print ( scout.report ( ) ) print ( 'Skicka Bond på ett uppdrag... \n ' ) spion = JamesBond () för f i anläggningar : f . acceptera ( spion ) print ( 'Skicka en scout för att uppdatera data... \n ' ) för f in anläggningar : f . acceptera ( scout ) print ( scout.report ( ) ) """ UTGÅNG: Skickar en scout... Information från scouten: Centrala högkvarteret: Det finns 3 generaler och 2 hemliga dokument i högkvarteret Kommando: Fungerande Militärbas: Det finns 1 atomubåt och 1 hemlig ritning i militärbasen Stridsberedskap: Ja Skickar Bond på uppdrag... Skickar en scout för att uppdatera data... Information från scouten: Centrala högkvarteret: Det finns 0 generaler och 0 hemliga dokument i högkvarteret Kommando: Inte fungerande Militärbas: Det finns 0 atomubåtar och 0 hemliga ritningar i militärbasen Beredskap: Inga """

Delphi

Implementeringsexempel i Delphi programdemo ; _ typ Point2D = klass ; Point3D = klass ; IVisitor = gränssnittsprocedur Besök ( p : Point2D ) ; _ överbelastning ; procedur Besök ( s : Point3D ) ; överbelastning ; slut ; Point = klass privat FMetrisk : Dubbel ; public property Metrisk : Dubbelläs FMetrisk skriv FMetrisk ; _ förfarande Acceptera ( besökare : IVbesökare ) ; virtuell ; abstrakt ; slut ; Point2D = klass ( Point ) privat FX : Dubbel ; FY : Dubbel ; public property X : Dubbelläs FX ; _ egenskap Y : Dubbelläs FY ; _ konstruktor Skapa ( const x , y : Double ) ; förfarande Acceptera ( Besökare : IVbesökare ) ; åsidosätta ; slut ; Point3D = klass ( Point ) privat FX : Dubbel ; FY : Dubbel ; FZ : Dubbel ; public property X : Dubbelläs FX ; _ egenskap Y : Dubbelläs FY ; _ egenskap Z : Dubbelläst FZ ; _ konstruktor Skapa ( const x , y , z : Double ) ; förfarande Acceptera ( Besökare : IVbesökare ) ; åsidosätta ; slut ; Euklid = klass ( TInterfacedObject , IVisitor ) offentligt förfarande Besök ( p : Point2D ) ; överbelastning ; procedur Besök ( s : Point3D ) ; överbelastning ; slut ; Chebyshev = klass ( TInterfacedObject , IVisitor ) offentligt förfarande Besök ( p : Point2D ) ; överbelastning ; procedur Besök ( s : Point3D ) ; överbelastning ; slut ; {Point2D} procedur Point2D . Acceptera ( Besökare : IVbesökare ) ; börja Besökare . Besök ( Själv ) ; slut ; konstruktör Point2D . Skapa ( const x , y : Double ) ; börja FX := x ; FY := y ; slut ; {Point3D} procedur Point3D . Acceptera ( Besökare : IVbesökare ) ; börja Besökare . Besök ( Själv ) ; slut ; konstruktör Point3D . Skapa ( const x , y , z : Double ) ; börja FX := x ; FY := y ; FX := z ; slut ; { Euklid } förfarande Eulid . Besök ( p : Point2D ) ; börja s . Metrisk := Sqrt ( Sqr ( p . X ) + Sqr ( p . Y )) ; slut ; förfarande Eulid . Besök ( p : Point3D ) ; börja s . Metrisk := Sqrt ( Sqr ( p . X ) + Sqr ( p . Y ) + Sqr ( p . Z )) ; slut ; {Chebyshev} förfarande Chebyshev . Besök ( p : Point2D ) ; var axe , ay : Dubbel ; börja ax := Abs ( s . X ) ; ay := Abs ( p . Y ) ; om ax > ay p . Metrisk := ax annat p . Metrisk : = ja slut ; förfarande Chebyshev . Besök ( p : Point3D ) ; var ax , ay , az , max : Dubbel ; börja ax := Abs ( s . X ) ; ay := Abs ( p . Y ) ; az : = Abs ( p.Z ) ; _ om ax > ay max := ax annars max := ay ; om max < az max := az ; sid . Metrisk := max ; slut ; varp : Punkt ; _ v : IVVisitor ; börja p := Point2D . Skapa ( 1 , 2 ) ; v := Chebyshev . skapa ; sid . acceptera ( v ) ; WriteLn ( s . Metrisk : 0 : 2 ) ; v := Eulid . skapa ; sid . acceptera ( v ) ; WriteLn ( s . Metrisk : 0 : 2 ) ; sid . Gratis ; Läsln ; // vänta på tryck Enter end .

Swift

Implementeringsexempel i Swift protokoll WarehouseItem { var name : String { get set } var isBroken : Bool { get set } var price : Int { get set } } class WarehouseItemImpl : WarehouseItem { var name : String = "" var isBroken : Bool = false var price : Int = 0 init ( namn : String , isBroken : Bool , pris : Int ) { self . namn = namn själv . isBroken = ärBroken self . pris = pris } } protokoll Warehouse { var items : [ WarehouseItem ] { get set } func addItem ( artikel : WarehouseItem ) func accept ( besökare : BasicVisitor ) } class WarehouseImpl : Warehouse { var items : [ WarehouseItem ] = [] func addItem ( artikel : WarehouseItem ) { items . lägga till ( objekt ) } func accept ( besökare : BasicVisitor ) { för objekt i objekt { besökare . besök ( objekt som AnyObject ) } } } protokoll BasicVisitor { func visit ( _ anObject : AnyObject ) } class QualityCheckerVisitor : BasicVisitor { func besök ( _ anObject : AnyObject ) { if let obj = anObject as ? WarehouseItem { if obj . isBroken { print ( "is Broken true" ) } else { print ( "is Broken false" ) } om låt _ = ett objekt som ? Lager { print ( "Bra lager" ) } } } } klass PriceCheckerVisitor : BasicVisitor { func besök ( _ anObject : AnyObject ) { if let obj = anObject as ? WarehouseItem { print ( " \( obj . name ) | Pris: \( obj . price ) rub." ) } om låt _ = ett objekt som ? Lager { print ( "Kostnad ingen" ) } } } // Använd besökare låt lager = WarehouseImpl () lager . addItem ( artikel : WarehouseItemImpl ( namn : "Artikel 1" , isBroken : sant , pris : 100 )) lager . addItem ( artikel : WarehouseItemImpl ( namn : "Artikel 2" , isBroken : false , pris : 300 )) lager . addItem ( artikel : WarehouseItemImpl ( namn : "Artikel 3" , isBroken : false , pris : 500 )) let price = PriceCheckerVisitor () let qulity = QualityCheckerVisitor () lager . acceptera ( besökare : pris ) lager . acceptera ( besökare : qulity )

Litteratur

  • E. Gamma, R. Helm, R. Johnson, J. Vlissides . Tekniker för objektorienterad design. Design mönster. - St Petersburg. : Peter, 2001. - 368 sid. — ISBN 5-272-00355-1 .

Länkar