Besøgende (designmønster)

Den aktuelle version af siden er endnu ikke blevet gennemgået af erfarne bidragydere og kan afvige væsentligt fra den version , der blev gennemgået den 4. januar 2016; checks kræver 26 redigeringer .
Besøgende
Besøgende
Type adfærdsmæssige
Formål uden at ændre hovedklassen , tilføje nye operationer til den.
Struktur
Gælder i sager når det er nødvendigt at udføre en lignende (samme) operation for en række klasser.
fordele
  • ny funktionalitet tilføjes til flere klasser på én gang uden at ændre koden for disse klasser;
  • giver dig mulighed for at få information om typen af ​​et objekt;
  • dobbelt planlægning;
  • mulighed for at beskrive egen algoritme for hver type objekter .
Minusser
  • når du ændrer den servicerede klasse, skal du ændre skabelonkoden;
  • det er svært at tilføje nye klasser, fordi hierarkiet for den besøgende og hans sønner skal opdateres.
Beskrevet i Design Patterns Ja

En  besøgende er et adfærdsdesignmønster , der beskriver en operation, der udføres på objekter af andre klasser. Når du skifter besøgende, er der ingen grund til at ændre de servicerede klasser .

Skabelonen demonstrerer den klassiske metode til at gendanne mistede typeoplysninger uden at ty til dobbelt-afsendelse af downcast .

Problem løst

Du skal udføre nogle afbrudte operationer på en række objekter, men du skal undgå at forurene deres kode. Og der er ingen måde eller ønske om at forespørge om typen af ​​hver node og kaste markøren til den korrekte type, før du udfører den ønskede operation.

Udfordring

En eller flere operationer udføres på hvert objekt af en eller anden struktur. Du skal definere en ny operation uden at ændre objektklasserne.

Løsning

For uafhængighed har den besøgende et separat hierarki. Strukturer har en vis interaktionsgrænseflade.

Brug

Hvis der er en chance for, at det servicerede klassehierarki vil ændre sig, eller det vil være ustabilt, eller den offentlige grænseflade er effektiv nok for skabelonen at få adgang til, så er brugen af ​​den ondsindet.

Der oprettes en basisklasse Visitormed metoder visit()for hver underklasse af forælderen Element. Tilføj en metode accept(visitor)til elementhierarkiet. For hver operation, der skal udføres på objekter Element, udled en Visitorklasse fra. Metodeimplementeringer visit()skal bruge klassens offentlige grænseflade Element. Som et resultat: klienter opretter objekter Visitorog sender dem til hvert objekt Elementved at kalde accept().

Anbefalinger

Skabelonen skal bruges, hvis:

Fordele og ulemper

Fordele :

Ulemper :

Implementering

  1. Tilføj en metode accept(Visitor)til "element"-hierarkiet.
  2. Opret en basisklasse Visitorog definer metoder visit()for hver elementtype.
  3. Opret afledte klasser Visitorfor hver operation udført på elementer.
  4. Klienten opretter et objekt Visitorog sender det til den kaldte metodeaccept().

C++

Implementeringseksempel i C++ #include <iostream> #inkluder <streng> klasse Foo ; klasse Bar ; klasse Bas ; klasse Besøgende { offentligt : virtuelt ugyldigt besøg ( Foo & ref ) = 0 ; virtuelt ugyldigt besøg ( Bøjle & ref ) = 0 ; virtuelt ugyldigt besøg ( Baz & ref ) = 0 ; virtuel ~ Besøgende () = standard ; }; klasseelement { _ offentligt : virtual void accept ( Besøgende & v ) = 0 ; virtuel ~ Element () = standard ; }; klasse Foo : offentligt element { offentligt : void accept ( Besøgende & v ) tilsidesætte { v . besøg ( * dette ); } }; klasse Bar : offentligt element { offentligt : void accept ( Besøgende & v ) tilsidesætte { v . besøg ( * dette ); } }; klasse Baz : offentligt element { offentligt : void accept ( Besøgende & v ) tilsidesætte { v . besøg ( * dette ); } }; klasse GetType : offentlig besøgende { offentligt : std :: strengværdi ; _ offentligt : void visit ( Foo & ref ) override { værdi = "foo" ; } void visit ( Bar & ref ) tilsidesætte { værdi = "bar" ; } void visit ( Baz & ref ) tilsidesætte { værdi = "base" ; } }; int main () { Foo foo ; Bar bar ; baz baz ; Element * elementer [] = { & foo , & bar , & baz }; for ( auto elem : elementer ) { GetType besøgende ; elem -> acceptere ( gæst ); std :: cout << besøgende . værdi << std :: endl ; } returnere 0 ; }

Java

Eksempel på Java- implementering public class Demo { public static void main ( String [] args ) { Point p = new Point2d ( 1 , 2 ); Besøgende v = ny Chebyshev (); p . acceptere ( v ); System . ud . println ( s . getMetric () ); } } interface Visitor { public void visit ( Point2d p ); offentligt ugyldigt besøg ( Point3d p ); } abstrakt klasse Punkt { public abstract void accept ( Besøgende v ); privat dobbelt metrisk = - 1 ; public double getMetric () { return metric ; } public void setMetric ( double metric ) { this . metrisk = metrisk ; } } klasse Point2d udvider Point { public Point2d ( double x , double y ) { this . x = x ; dette . y = y _ } public void accept ( Besøgende v ) { v . besøg ( dette ); } privat dobbelt x ; public double getX () { return x ; } privat dobbelt y ; public double getY () { return y ; } } klasse Point3d udvider Point { public Point3d ( double x , double y , double z ) { this . x = x ; dette . y = y _ dette . z = z _ } public void accept ( Besøgende v ) { v . besøg ( dette ); } privat dobbelt x ; public double getX () { return x ; } privat dobbelt y ; public double getY () { return y ; } privat dobbelt z ; public double getZ () { return z ; } } klasse Euclid implementerer Visitor { public void visit ( Point2d p ) { p . setMetric ( Math . sqrt ( p . getX ( ) * p . getX ( ) + p . getY () * p . getY () ) ); } offentligt ugyldigt besøg ( Point3d p ) { s . setMetric ( Math . sqrt ( p . getX ( ) * p . getX ( ) + p . getY ( ) * p . getY () + p . getZ () * p . getZ () ) ); } } klasse Chebyshev implementerer Visitor { public void visit ( Punkt2d p ) { double axe = Math . abs ( p.getX ( ) ) ; double -ay = Matematik . abs ( s . getY () ); p . setMetric ( ax > ay ? ax : ay ); } offentligt ugyldigt besøg ( Point3d p ) { double axe = Math . abs ( p.getX ( ) ) ; double -ay = Matematik . abs ( s . getY () ); dobbelt az = Matematik . abs ( p . getZ () ); dobbelt max = akse > ay ? økse : ay ; hvis ( maks < az ) max = az ; p . setMetric ( max ); } }

C#

Implementeringseksempel i C# public static class Demo { private static void Main () { Point p = new Point2D ( 1 , 2 ); IVisitor v = ny Chebyshev (); p . acceptere ( v ); Konsol . WriteLine ( s . Metrisk ); } } intern grænseflade IVisitor { void Visit ( Point2D p ); ugyldigt besøg ( Point3Dp ) ; } intern abstrakt klasse Punkt { public double Metric { get ; sæt ; } = - 1 ; offentlig abstrakt void Accepter ( IVBesøgende besøgende ); } intern klasse Point2D : Point { public Point2D ( double x , double y ) { X = x ; Y = y _ } offentlig dobbelt X { ; } public double Y { get ; } public override void Accepter ( IVisitor besøgende ) { besøgende . besøg ( dette ); } } intern klasse Point3D : Point { public Point3D ( double x , double y , double z ) { X = x ; Y = y _ Z = z _ } offentlig dobbelt X { ; } public double Y { get ; } public double Z { get ; } public override void Accepter ( IVisitor besøgende ) { besøgende . besøg ( dette ); } } intern klasse Euclid : IVisitor { public void Visit ( Point2D p ) { p . Metrisk = Matematik . Sqrt ( p . X * p . X + p . Y * p . Y ); } public void Besøg ( Point3D p ) { s . Metrisk = Matematik . Sqrt ( p . X * p . X + p . Y * p . Y + p . Z * p . Z ); } } intern klasse Chebyshev : IVisitor { public void Visit ( Point2D p ) { var ax = Math . abs ( s . X ); varay = matematik . _ Abs ( s . Y ); p . Metrisk = økse > ay ? økse : ay ; } public void Besøg ( Point3D p ) { var ax = Math . abs ( s . X ); varay = matematik . _ Abs ( s . Y ); var az = Matematik . Abs ( p . Z ); varmax = ax > ay ? _ økse : ay ; hvis ( maks < az ) max = az ; p . Metrisk = max ; } }

PHP

Eksempel implementering i php <?php interface Visitor { public function visit ( Punkt $point ); } abstrakt klasse Punkt { public abstract function accept ( Besøgende $visitor ); privat $_metrisk = - 1 ; public function getMetric () { return $this -> _metric ; } public function setMetric ( $metric ) { $this -> _metric = $metrisk ; } } klasse Point2d udvider Point { offentlig funktion __construct ( $x , $y ) { $this -> _x = $x ; $this -> _y = $y ; } public function accept ( Besøgende $visitor ) { $visitor -> visit ( $this ); } privat $_x ; public function getX () { return $this -> _x ; } privat $_y ; public function getY () { return $this -> _y ; } } klasse Point3d udvider Point { public function __construct ( $x , $y , $z ) { $this -> _x = $x ; $this -> _y = $y ; $this -> _z = $z ; } public function accept ( Besøgende $visitor ) { $visitor -> visit ( $this ); } privat $_x ; public function getX () { return $this -> _x ; } privat $_y ; public function getY () { return $this -> _y ; } privat $_z ; offentlig funktion getZ () { return $this -> _z ; } } klasse Euclid implementerer Visitor { public function visit ( Point $p ) { if ( $p instanceof Point2d ) $p -> setMetric ( sqrt ( $p -> getX () * $p -> getX () + $p -> getY () * $p -> getY () ) ); elseif ( $p forekomst af Point3d ) $p -> setMetric ( sqrt ( $p - > getX ( ) * $ p - > getX ( ) + $p -> getY () * $p -> getY () + $p - > getZ () * $p -> getZ () ) ); } } klasse Chebyshev implementerer Visitor { public function visit ( Punkt $p ) { if ( $p instanceof Point2d ){ $ax = abs ( $p -> getX () ); $ay = abs ( $p -> getY () ); $p -> setMetric ( $ax > $ay ? $ax : $ay ); } elseif ( $p instanceof 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 -> accepter ( $v ); echo ( $p -> getMetric () ); }; start ();

Python

Implementeringseksempel i Python fra abc import ABCMeta , abstrakt metode fra at skrive importliste klasse Spy ( metaclass = ABCMeta ): """ Spion besøgende """ @abstractmethod def visit_military_base ( self , military_base : 'MilitaryBase' ) -> Ingen : """ Besøg flådens militærbase """ pass @abstractmethod def visit_headquarters ( selv , hovedkvarter : 'Hovedkvarter' ) -> Ingen : """ Besøg hærens hovedkvarter """ pass klasse MilitaryFacility ( metaclass = ABCMeta ): """ Militærfacilitet - besøgt facilitet """ @abstractmethod def accept ( selv , spion : Spy ) -> Ingen : """ Accepter spiongæst """ pass klasse MilitaryBase ( MilitaryFacility ): """ Ubåds militærbase """ def __init__ ( selv ) -> Ingen : selv . _hemmelige_udkast = 1 selv . _atomubåde = 1 def __repr__ ( self ) -> str : return 'Militærbasen har {} atomubåde og {} hemmelige tegninger' . format ( selv . _atomubåde , selv . _hemmelige_udkast ) def accept ( selv , spion : spion ) -> Ingen : spion . besøg_militærbase ( selv ) def remove_secret_draftings ( self ) -> Ingen : if self . _hemmelige_udkast : selv . _hemmelige_udkast -= 1 def remove_nuclear_submarine ( selv ) -> Ingen : hvis selv . _atomubåde : selv . _atomubåde -= 1 @property def is_combat_ready ( selv ) -> bool : returner selv . _atomubåde > 0 klasse Hovedkvarter ( MilitaryFacility ): """ Hærens hovedkvarter """ def __init__ ( selv ) -> Ingen : selv . _generaler = 3 selv . _hemmelige_dokumenter = 2 def __repr__ ( self ) -> str : return 'Der er {} generaler og {} hemmelige dokumenter i hovedkvarteret ' . format ( self . _generals , self . _secret_documents ) def accept ( selv , spion : spion ) -> Ingen : spion . besøg_hovedkvarter ( selv ) def remove_general ( selv ) -> Ingen : hvis selv . _generaler : selv . _generaler -= 1 def remove_secret_documents ( selv ) -> Ingen : hvis selv . _hemmelige_dokumenter : selv . _hemmelige_dokumenter -= 1 @property def is_command_ready ( selv ) -> bool : returner selv . _generaler > 0 klasse ScoutSpy ( Spy ): """ Scout (konkret spion) """ def __init__ ( self ): self . _indsamlede_oplysninger = {} # Her kender vi allerede den specifikke objekttype def visit_military_base ( self , military_base : MilitaryBase ) -> None : self . _collected_info [ 'base' ] = 'Militærbase: \n\t {} \n\t Klar: {} ' . format ( str ( militærbase ), 'Ja' hvis militærbase . is_combat_ready else 'Nej' ) def visit_headquarters ( selv , hovedkvarter : hovedkvarter ) -> Ingen : selv . _collected_info [ 'headquarters' ] = 'Hovedkvarter: \n\t {} \n\t Kommando: {} ' . format ( str ( hovedkvarter ), 'Kører' , hvis hovedkvarteret . is_command_ready else 'Ikke operationelt' ) def report ( self ) -> str : return 'Information fra spejderen: \n {} \n ' . format ( ' \n ' . join ( self . _collected_info . values ​​​​()) ) klasse JamesBond ( Spy ): """ James Bond (en anden specifik spion) """ def visit_military_base ( selv , military_base : MilitaryBase ) -> Ingen : # James Bond besøger militærbasen military_base . remove_secret_draftings () # stjæler militærbasens hemmelige tegninger . remove_nuclear_submarine () # og til sidst sprænger en atomubåd i luften def visit_headquarters ( selv , hovedkvarter : Hovedkvarter ) -> Ingen : # James Bond besøger hovedkvarteret . remove_general () # ... hovedkvarter . remove_general () # ... hovedkvarter . remove_secret_documents () # ... hovedkvarter . remove_general () # Ødelægger alle generaler sekventielt hovedkvarterer . remove_secret_documents () # og stjæler alle hemmelige dokumenter if __name__ == '__main__' : base = MilitaryBase () hq = Hovedkvarter () # Uanset hvilke MilitaryFacility faciliteter = [ base , hq ] # type: List[MilitaryFacility] scout = ScoutSpy () print ( 'Sender en spejder... \n ' ) for f i faciliteter : f . acceptere ( spejder ) print ( scout.report ( ) ) print ( 'Sender Bond på en mission... \n ' ) spion = JamesBond () for f i faciliteter : f . acceptere ( spion ) print ( 'Sender en spejder for at opdatere data... \n ' ) for f in faciliteter : f . acceptere ( spejder ) print ( scout.report ( ) ) """ OUTPUT: Sender en spejder... Information fra spejderen: Centralt hovedkvarter: Der er 3 generaler og 2 hemmelige dokumenter i hovedkvarteret Kommando: Fungerende Militærbase: Der er 1 atomubåd og 1 hemmelige tegninger i militærbasen Kampberedskab: Ja Sender Bond på en mission... Sender en spejder for at opdatere dataene... Information fra spejderen: Centralt hovedkvarter: Der er 0 generaler og 0 hemmelige dokumenter i hovedkvarteret Kommando: Ikke fungerende Militærbase: Der er 0 atomubåde og 0 hemmelige tegninger i militærbasen Beredskab: Ingen """

Delphi

Implementeringseksempel i Delphi program Demo ; type Point2D = klasse ; Point3D = klasse ; IVisitor = grænsefladeprocedure Besøg ( p : Point2D ) ; _ overbelastning ; procedure Besøg ( s : Point3D ) ; overbelastning ; ende ; Point = klasse privat FMetric : Dobbelt ; offentlig ejendom Metrisk : Dobbeltlæs FMetric skriv FMetric ; _ procedure Accepter ( gæst : IVbesøger ) ; virtuel ; abstrakt ; ende ; Point2D = klasse ( Punkt ) privat FX : Dobbelt ; FY : Dobbelt ; offentlig ejendom X : Dobbeltlæs FX ; _ egenskab Y : Dobbeltlæs FY ; _ konstruktør Opret ( const x , y : Double ) ; procedure Accepter ( besøgende : IVbesøger ) ; tilsidesætte ; ende ; Point3D = klasse ( Punkt ) privat FX : Dobbelt ; FY : Dobbelt ; FZ : Dobbelt ; offentlig ejendom X : Dobbeltlæs FX ; _ egenskab Y : Dobbeltlæs FY ; _ egenskab Z : Dobbeltlæst FZ ; _ konstruktør Opret ( const x , y , z : Double ) ; procedure Accepter ( besøgende : IVbesøger ) ; tilsidesætte ; ende ; Euklid = klasse ( TInterfacedObject , IVisitor ) offentlig procedure Besøg ( p : Point2D ) ; overbelastning ; procedure Besøg ( s : Point3D ) ; overbelastning ; ende ; Chebyshev = klasse ( TInterfacedObject , IVisitor ) offentlig procedure Besøg ( p : Point2D ) ; overbelastning ; procedure Besøg ( s : Point3D ) ; overbelastning ; ende ; {Point2D} procedure Point2D . Accepter ( Besøgende : IVbesøger ) ; begynde Besøgende . Besøg ( Selv ) ; ende ; konstruktør Point2D . Opret ( konst x , y : Dobbelt ) ; begynde FX := x ; FY := y ; ende ; {Point3D} procedure Point3D . Accepter ( Besøgende : IVbesøger ) ; begynde Besøgende . Besøg ( Selv ) ; ende ; konstruktør Point3D . Opret ( konst x , y , z : Dobbelt ) ; begynde FX := x ; FY := y ; FX := z ; ende ; { Euklid } procedure Eulid . Besøg ( p : Point2D ) ; begynde s . Metrisk := Sqrt ( Sqr ( p . X ) + Sqr ( p . Y )) ; ende ; procedure Eulid . Besøg ( s : Point3D ) ; begynde s . Metrisk := Sqrt ( Sqr ( p . X ) + Sqr ( p . Y ) + Sqr ( p . Z )) ; ende ; {Chebyshev} procedure Chebyshev . Besøg ( p : Point2D ) ; var axe , ay : Dobbelt ; begynde ax := Abs ( s . X ) ; ay := Abs ( p . Y ) ; hvis ax > ay p . Metrisk := økse andet p . Metrisk : = ja ende ; procedure Chebyshev . Besøg ( s : Point3D ) ; var ax , ay , az , max : Dobbelt ; begynde ax := Abs ( s . X ) ; ay := Abs ( p . Y ) ; az := Abs ( p . Z ) ; hvis ax > ay max := ax else max := ay ; hvis max < az max := az ; p . Metrisk := max ; ende ; varp : Punkt ; _ v : IVVisitor ; start p := Point2D . Opret ( 1 , 2 ) ; v := Chebyshev . skabe ; p . acceptere ( v ) ; WriteLn ( s . Metrisk : 0 : 2 ) ; v := Eulid . skabe ; p . acceptere ( v ) ; WriteLn ( s . Metrisk : 0 : 2 ) ; p . Gratis ; Læsln ; // vent til tryk på Enter end .

Swift

Implementeringseksempel i Swift protokol WarehouseItem { var name : String { get set } var isBroken : Bool { get set } var price : Int { get set } } klasse WarehouseItemImpl : WarehouseItem { var name : String = "" var isBroken : Bool = false var price : Int = 0 init ( navn : String , isBroken : Bool , pris : Int ) { self . navn = navn selv . isBroken = erBroken self . pris = pris } } protokol Warehouse { var items : [ WarehouseItem ] { get set } func addItem ( item : WarehouseItem ) func accept ( besøgende : BasicVisitor ) } klasse WarehouseImpl : Warehouse { var items : [ WarehouseItem ] = [] func addItem ( vare : WarehouseItem ) { items . tilføj ( element ) } func accept ( besøgende : BasicVisitor ) { for vare i varer { besøgende . besøg ( element som AnyObject ) } } } protokol BasicVisitor { func visit ( _ anObject : AnyObject ) } klasse QualityCheckerVisitor : BasicVisitor { func visit ( _ anObject : AnyObject ) { if let obj = anObject as ? WarehouseItem { if obj . isBroken { print ( "is Broken true" ) } else { print ( "is Broken false" ) } hvis lad _ = et objekt som ? Lager { print ( "Godt lager" ) } } } } klasse PriceCheckerVisitor : BasicVisitor { func visit ( _ anObject : AnyObject ) { if let obj = anObject as ? WarehouseItem { print ( " \( obj . navn ) | Pris: \( obj . pris ) rub." ) } hvis lad _ = et objekt som ? Lager { print ( "Kost ingen" ) } } } // Brug besøgende lad lager = WarehouseImpl () lager . addItem ( vare : WarehouseItemImpl ( navn : "Item 1" , isBroken : true , pris : 100 )) lager . addItem ( vare : WarehouseItemImpl ( navn : "Item 2" , isBroken : false , pris : 300 )) lager . addItem ( vare : WarehouseItemImpl ( navn : "Item 3" , isBroken : false , pris : 500 )) lad pris = PriceCheckerVisitor () lad qulity = QualityCheckerVisitor () lager . acceptere ( besøgende : pris ) lager . acceptere ( besøgende : qulity )

Litteratur

  • E. Gamma, R. Helm, R. Johnson, J. Vlissides . Teknikker til objektorienteret design. Design mønstre. - Sankt Petersborg. : Peter, 2001. - 368 s. — ISBN 5-272-00355-1 .

Links