Vizitator (model de design)

Versiunea actuală a paginii nu a fost încă revizuită de colaboratori experimentați și poate diferi semnificativ de versiunea revizuită la 4 ianuarie 2016; verificările necesită 26 de modificări .
Vizitator
Vizitator
Tip de comportamental
Scop fără a schimba clasa principală , adăugați-i operațiuni noi.
Structura
Se aplică în cazuri când este necesar să se efectueze o operație similară (aceeași) pentru un număr de clase.
pro
  • se adaugă o nouă funcționalitate la mai multe clase simultan fără a modifica codul acestor clase;
  • vă permite să obțineți informații despre tipul unui obiect;
  • programare dubla;
  • posibilitatea de a descrie algoritmul propriu pentru fiecare tip de obiecte .
Minusuri
  • atunci când schimbați clasa deservită, trebuie să schimbați codul șablonului;
  • este dificil să adaugi noi clase, deoarece ierarhia vizitatorului și a fiilor săi trebuie actualizată.
Descris în Design Patterns da

Un  vizitator este un model de design comportamental care descrie o operație care este efectuată pe obiecte din alte clase. Când schimbați vizitatorul, nu este nevoie să schimbați clasele deservite .

Șablonul demonstrează metoda clasică de recuperare a informațiilor de tip pierdut fără a recurge la dubla dispecerare downcast .

Problemă rezolvată

Trebuie să faceți unele operații deconectate pe un număr de obiecte, dar trebuie să evitați poluarea codului acestora. Și nu există nicio modalitate sau dorință de a interoga tipul fiecărui nod și de a arunca indicatorul la tipul corect înainte de a efectua operația dorită.

Provocare

Se efectuează una sau mai multe operații asupra fiecărui obiect al unei structuri. Trebuie să definiți o nouă operație fără a schimba clasele de obiecte.

Soluție

Pentru independență, vizitatorul are o ierarhie separată. Structurile au o anumită interfață de interacțiune.

Utilizare

Dacă există șansa ca ierarhia claselor deservite să se schimbe, sau să fie instabilă, sau interfața publică este suficient de eficientă pentru ca șablonul să poată fi accesat, atunci utilizarea sa este rău intenționată.

Se creează o clasă de bază Visitorcu metode visit()pentru fiecare subclasă a părintelui Element. Adăugați o metodă accept(visitor)la ierarhia Elementului. Pentru fiecare operație care trebuie efectuată asupra obiectelor Element, derivă o Visitorclasă din. Implementările metodei visit()trebuie să utilizeze interfața publică a clasei Element. Ca rezultat: clienții creează obiecte Visitorși le transmit fiecărui obiect Elementapelând accept().

Recomandări

Șablonul trebuie utilizat dacă:

Avantaje și dezavantaje

Beneficii :

Dezavantaje :

Implementare

  1. Adăugați o metodă accept(Visitor)la ierarhia „element”.
  2. Creați o clasă de bază Visitorși definiți metode visit()pentru fiecare tip de element.
  3. Creați clase derivate Visitorpentru fiecare operație efectuată pe elemente.
  4. Clientul creează un obiect Visitorși îl transmite metodei apelateaccept().

C++

Exemplu de implementare în C++ #include <iostream> #include <șir> clasa Foo ; Bar de clasa ; clasa Bas ; clasa vizitator { public : vizită virtuală de gol ( Foo & ref ) = 0 ; vizită virtuală de gol ( Bar & ref ) = 0 ; virtual void visit ( Baz & ref ) = 0 ; virtual ~ Vizitator () = implicit ; }; element de clasă { public : virtual void accept ( Vizitator & v ) = 0 ; virtual ~ Element () = implicit ; }; clasa Foo : element public { public : void accept ( Vizitator & v ) override { v . vizita ( * aceasta ); } }; Class Bar : element public { public : void accept ( Vizitator & v ) override { v . vizita ( * aceasta ); } }; clasa Baz : element public { public : void accept ( Vizitator & v ) override { v . vizita ( * aceasta ); } }; clasa GetType : vizitator public { public : std :: stringvalue ; _ public : void visit ( Foo & ref ) override { valoare = "foo" ; } void visit ( Bar & ref ) override { valoare = "bar" ; } void visit ( Baz & ref ) override { valoare = "bază" ; } }; int main () { Foo foo ; bar bar ; baz baz ; Element * elemente [] = { & foo , & bar , & baz }; pentru ( auto elem : elemente ) { Vizitator GetType ; elem -> accept ( vizitator ); std :: cout << vizitator . valoare << std :: endl ; } returnează 0 ; }

Java

Exemplu de implementare Java public class Demo { public static void main ( String [] args ) { Point p = new Point2d ( 1 , 2 ); Vizitator v = nou Cebyshev (); p . accept ( v ); Sistem . afară . println ( p . getMetric () ); } } interfață Vizitator { public void visit ( Point2d p ); vizita public nul ( Point3d p ); } abstract class Point { public abstract void accept ( Vizitator v ); metric dublu privat = - 1 ; public double getMetric () { return metric ; } public void setMetric ( metrică dublă ) { this . metric = metric ; } } clasa Punctul2d extinde Punctul { public Punctul2d ( dublu x , dublu y ) { this . x = x ; aceasta . y = y _ } public void accept ( Vizitator v ) { v . vizita ( aceasta ); } privat dublu x ; public double getX () { return x ; } privat dublu y ; public double getY () { return y ; } } class Point3d extinde Punct { public Point3d ( dublu x , dublu y , dublu z ) { acest lucru . x = x ; aceasta . y = y _ aceasta . z = z _ } public void accept ( Vizitator v ) { v . vizita ( aceasta ); } privat dublu x ; public double getX () { return x ; } privat dublu y ; public double getY () { return y ; } privat dublu 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 () ) ); } public void visit ( Point3d p ) { p . setMetric ( Math . sqrt ( p . getX ( ) * p . getX ( ) + p . getY ( ) * p . getY ( ) + p . getZ () * p . getZ () ) ); } } clasa Chebyshev implementeaza Vizitator { public void visit ( Point2d p ) { dublu ax = Math . abs ( p.getX ( ) ) ; double -ay = Math . abs ( p . getY () ); p . setMetric ( ax > ay ? ax : ay ); } public void visit ( Point3d p ) { double ax = Math . abs ( p.getX ( ) ) ; double -ay = Math . abs ( p . getY () ); dublu az = Math . abs ( p . getZ () ); double max = ax > ay ? ax : da ; if ( max < az ) max = az ; p . setMetric ( max ); } }

C#

Exemplu de implementare în C# public static class Demo { private static void Main () { Point p = new Point2D ( 1 , 2 ); IVisitor v = nou Cebyshev (); p . accept ( v ); Consola . WriteLine ( p . Metric ); } } interfata interna IVisitor { void Visit ( Point2D p ); void Vizită ( Point3Dp ) ; } clasa abstractă internă Point { public double Metric { get ; set ; } = - 1 ; public abstract void Accept ( IVisitor vizitator ); } clasa interna Punct2D : Punct { public Punct2D ( dublu x , dublu y ) { X = x ; Y = y _ } public double X { get ; } public double Y { get ; } public override void Accept ( IVisitor visitor ) { vizitator . vizita ( aceasta ); } } clasa interna Point3D : Punct { public Point3D ( dublu x , dublu y , dublu z ) { X = x ; Y = y _ Z = z _ } public double X { get ; } public double Y { get ; } public double Z { get ; } public override void Accept ( IVisitor visitor ) { vizitator . vizita ( aceasta ); } } clasa interna Euclid : IVisitor { public void Vizitare ( Point2D p ) { p . Metric = Matematică . Sqrt ( p . X * p . X + p . Y * p . Y ); } public void Vizită ( Point3D p ) { p . Metric = Matematică . Sqrt ( p . X * p . X + p . Y * p . Y + p . Z * p . Z ); } } clasa interna Chebyshev : IVisitor { public void Vizitare ( Point2D p ) { var ax = Math . abs ( p . X ); varay = Matematică _ _ Abs ( p . Y ); p . Metric = ax > ay ? ax : da ; } public void Visit ( Point3D p ) { var ax = Math . abs ( p . X ); varay = Matematică _ _ Abs ( p . Y ); var az = Math . Abs ( p . Z ); varmax = ax > ay ? _ ax : da ; if ( max < az ) max = az ; p . Metric = max ; } }

PHP

Exemplu de implementare în php <?php interfață Vizitator { public function visit ( Punct $punct ); } abstract class Point { public abstract function accept ( Vizitator $vizitor ); private $_metric = - 1 ; funcția publică getMetric () { return $this -> _metric ; } funcția publică setMetric ( $metric ) { $this -> _metric = $metric ; } } clasa Point2d extinde Punctul { function public __construct ( $x , $y ) { $this -> _x = $x ; $acest -> _y = $y ; } public function accept ( Vizitator $vizitor ) { $vizitator -> vizita ( $acest ); } privat $_x ; function public getX () { return $this -> _x ; } privat $_y ; function public getY () { return $this -> _y ; } } class Point3d extinde Punct { public function __construct ( $x , $y , $z ) { $this -> _x = $x ; $acest -> _y = $y ; $acest -> _z = $z ; } public function accept ( Vizitator $vizitor ) { $vizitator -> vizita ( $acest ); } privat $_x ; function public getX () { return $this -> _x ; } privat $_y ; function public getY () { return $this -> _y ; } privat $_z ; function public getZ () { return $this -> _z ; } } class Euclid implementează Visitor { public function visit ( Punctul $p ) { if ( $p instanceof Point2d ) $p -> setMetric ( sqrt ( $p -> getX () * $p -> getX () + $p -> getY () * $p -> getY () ) ); elseif ( $p instanță de Punct3d ) $p -> setMetric ( sqrt ( $p -> getX () * $p -> getX () + $p -> getY () * $p -> getY () + $p - > getZ () * $p -> getZ () ) ); } } clasa Chebyshev implementeaza Visitor { public function visit ( Punctul $p ) { if ( $p instanceof Point2d ){ $ax = abs ( $p -> getX () ); $ay = abs ( $p -> getY () ); $p -> setMetric ( $ax > $ay ? $ax : $ay ); } elseif ( $p instanță de Punct3d ){ $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 ); } } } function start (){ $p = new Point2d ( 1 , 2 ); $v = nouCebișev ( ); $p -> accept ( $v ); echo ( $p -> getMetric () ); }; începe ();

Python

Exemplu de implementare în Python din abc import ABCMeta , abstractmethod din tastarea import List clasa Spy ( metaclass = ABCMeta ): """ Vizitator spion """ @abstractmethod def visit_military_base ( self , military_base : 'MilitaryBase' ) -> None : """ Vizitați baza militară navală """ permis @abstractmethod def visit_headquarters ( self , headquarters : 'Headquarters' ) -> None : """ Vizitați cartierul general al armatei """ permis clasă MilitaryFacility ( metaclass = ABCMeta ): """ Unitate militară - unitate vizitată """ @abstractmethod def accept ( self , spy : Spy ) -> None : """ Accept vizitator spion " "" clasa MilitaryBase ( MilitaryFacility ): """ Baza militară submarină """ def __init__ ( self ) -> None : self . _secret_draftings = 1 sine . _submarine_nucleare = 1 def __repr__ ( self ) -> str : return 'Baza militară are {} submarine nucleare și {} planuri secrete' . format ( self . _nuclear_submarines , self . _secret_draftings ) def accept ( self , spion : Spy ) -> None : spion . visit_military_base ( self ) def remove_secret_draftings ( self ) -> None : if self . _secret_draftings : sine . _descrieri_secrete -= 1 def remove_nuclear_submarine ( self ) -> None : if self . _submarine_nucleare : sine . _submarine_nucleare -= 1 @property def is_combat_ready ( self ) -> bool : return self . _submarine_nucleare > 0 Cartierul general de clasă ( MilitaryFacility ): """ Cartierul general al armatei """ def __init__ ( self ) -> None : self . _generali = 3 sine . _documente_secrete = 2 def __repr__ ( self ) -> str : return 'Există {} generali şi {} documente secrete la sediu ' . format ( self . _generals , self . _secret_documents ) def accept ( self , spion : Spy ) -> None : spion . visit_headquarters ( self ) def remove_general ( self ) -> None : if self . _generali : sine . _generali -= 1 def remove_secret_documents ( self ) -> None : if self . _secret_documents : sine . _documente_secrete -= 1 @property def is_command_ready ( self ) -> bool : return self . _generale > 0 clasa ScoutSpy ( Spy ): """ Scout (spion concret) """ def __init__ ( self ): self . _collected_info = {} # Aici știm deja tipul de obiect specific def visit_military_base ( self , military_base : MilitaryBase ) -> None : self . _collected_info [ 'base' ] = 'Baza militară: \n\t {} \n\t Gata: {} ' . format ( str ( military_base ), „Da” dacă military_base . is_combat_ready else „Nu” ) def visit_headquarters ( self , headquarters : Headquarters ) -> None : self . _collected_info [ 'headquarters' ] = 'Sediu: \n\t {} \n\t Comanda: {} ' . format ( str ( sediu ), „În rulare” dacă sediul central . is_command_ready else „Nu este operațional” ) def report ( self ) -> str : return 'Informații de la cercetaș: \n {} \n ' . format ( ' \n ' . join ( self . _collected_info . values ​​​​()) ) clasa JamesBond ( Spion ): """ James Bond (un alt spion specific) """ def visit_military_base ( self , military_base : MilitaryBase ) -> None : # James Bond vizitează baza militară military_base . remove_secret_draftings () # fură desenele secrete ale bazei_militare . remove_nuclear_submarine () # și în cele din urmă aruncă în aer un submarin nuclear def visit_headquarters ( self , headquarters : Headquarters ) -> None : # James Bond vizitează sediul . remove_general () # ... sediu . remove_general () # ... sediu . remove_secret_documents () # ... sediu . remove_general () # Distruge secvenţial toţi generalii sediul . remove_secret_documents () # și fură toate documentele secrete if __name__ == '__main__' : bază = MilitaryBase () hq = Cartier general () # Indiferent de instalațiile MilitaryFacility = [ bază , hq ] # tip: List[MilitaryFacility] scout = ScoutSpy () print ( 'Trimiterea unui cercetaș... \n ' ) pentru f în facilități : f . accepta ( scout ) imprimare ( scout.report ( ) ) print ( „Trimit Bond într-o misiune... \n ' ) spion = JamesBond () pentru f în facilități : f . accept ( spion ) print ( 'Trimiterea unui cercetător pentru a actualiza datele... \n ' ) pentru f în facilități : f . accepta ( scout ) imprimare ( scout.report ( ) ) """ IEȘIRE: Trimit un cercetaș... Informații de la cercetaș: Cartierul general central: Există 3 generali și 2 documente secrete în sediu Comandament: Funcționează Baza militară: Există 1 submarine nucleare și 1 desene secrete în baza militară Pregătirea luptă: Da Îl trimit pe Bond într-o misiune... Se trimite un cercetător pentru a actualiza datele... Informații de la cercetaș: Cartierul general central: Există 0 generali și 0 documente secrete în sediu Comandament: Nu funcționează Baza militară: Există 0 submarine nucleare și 0 desene secrete în baza militară Pregătire: Niciuna """

Delphi

Exemplu de implementare în Delphi program Demo ; tip Point2D = clasa ; Point3D = clasa ; IVisitor = procedura de interfață Vizită ( p : Point2D ) ; suprasarcina ; procedura Vizita ( p : Point3D ) ; suprasarcina ; sfârşitul ; Punct = clasă privată FMetric : Double ; proprietate publică Metric : Citire dublă FMetric scriere FMetric ; procedura Acceptare ( vizitator : IVisitor ) ; virtual ; abstract ; sfârşitul ; Point2D = clasa ( Punct ) privat FX : Double ; FY : dublu ; proprietate publică X : FX dublu citit ; proprietate Y : Citire dublă FY ; constructor Create ( const x , y : Double ) ; procedura Acceptare ( Vizitator : IVisitor ) ; suprascrie ; sfârşitul ; Point3D = clasa ( Punct ) privat FX : Double ; FY : dublu ; FZ : dublu ; proprietate publică X : FX dublu citit ; proprietate Y : Citire dublă FY ; proprietate Z : FZ dublu citit ; constructor Create ( const x , y , z : Double ) ; procedura Acceptare ( Vizitator : IVisitor ) ; suprascrie ; sfârşitul ; Euklid = clasă ( TInterfacedObject , IVisitor ) procedură publică Vizită ( p : Point2D ) ; suprasarcina ; procedura Vizita ( p : Point3D ) ; suprasarcina ; sfârşitul ; Chebyshev = clasă ( TInterfacedObject , IVisitor ) procedură publică Vizită ( p : Point2D ) ; suprasarcina ; procedura Vizita ( p : Point3D ) ; suprasarcina ; sfârşitul ; {Point2D} procedura Point2D . Accept ( Vizitator : IVisitor ) ; începe Vizitator . Vizită ( Sine ) ; sfârşitul ; constructor Point2D . Creați ( const x , y : Double ) ; începe FX := x ; FY := y ; sfârşitul ; {Point3D} procedura Point3D . Accept ( Vizitator : IVisitor ) ; începe Vizitator . Vizită ( Sine ) ; sfârşitul ; constructor Point3D . Creați ( const x , y , z : Double ) ; începe FX := x ; FY := y ; FX := z ; sfârşitul ; { Euclid } procedura Eulid . Vizită ( p : Point2D ) ; începe p . Metric := Sqrt ( Sqr ( p . X ) + Sqr ( p . Y )) ; sfârşitul ; procedura Eulid . Vizită ( p : Point3D ) ; începe p . Metric := Sqrt ( Sqr ( p . X ) + Sqr ( p . Y ) + Sqr ( p . Z )) ; sfârşitul ; {Chebyshev} procedura Cebyshev . Vizită ( p : Point2D ) ; var ax , ay : Double ; start ax := Abs ( p . X ) ; ay := Abs ( p . Y ) ; dacă ax > ay atunci p . Metric := ax else p . Metric : = da sfârşitul ; procedura Cebyshev . Vizită ( p : Point3D ) ; var ax , ay , az , max : Double ; start ax := Abs ( p . X ) ; ay := Abs ( p . Y ) ; az := Abs ( p . Z ) ; if ax > ay then max := ax else max := ay ; daca max < az atunci max := az ; p . Metric := max ; sfârşitul ; varp : Punct ; _ v : IVVizitor ; începe p := Point2D . Creați ( 1 , 2 ) ; v := Cebyshev . a crea ; p . accept ( v ) ; WriteLn ( p . Metric : 0 : 2 ) ; v := Eulid . a crea ; p . accept ( v ) ; WriteLn ( p . Metric : 0 : 2 ) ; p . Gratuit ; Readln ; // așteptați să apăsați Enter final .

Swift

Exemplu de implementare în Swift protocol WarehouseItem { var name : String { get set } var isBroken : Bool { get set } var price : Int { get set } } clasa WarehouseItemImpl : WarehouseItem { var name : String = "" var isBroken : Bool = false var price : Int = 0 init ( nume : String , isBroken : Bool , preţ : Int ) { self . nume = nume de sine . isBroken = isBroken self . preț = preț } } protocol Warehouse { var items : [ WarehouseItem ] { get set } func addItem ( item : WarehouseItem ) func accept ( vizitator : BasicVisitor ) } clasa WarehouseImpl : Warehouse { var items : [ WarehouseItem ] = [] func addItem ( item : WarehouseItem ) { articole . adaugă ( articol ) } func accept ( vizitator : BasicVisitor ) { pentru element în articole { vizitator . vizitați ( element ca AnyObject ) } } } protocol BasicVisitor { func visit ( _ anObject : AnyObject ) } clasa QualityCheckerVisitor : BasicVisitor { func visit ( _ anObject : AnyObject ) { if let obj = anObject as ? WarehouseItem { if obj . isBroken { print ( "este Broken true" ) } else { print ( "este Broken false" ) } dacă fie _ = anObject ca ? Depozit { print ( "Depozit bun" ) } } } } clasa PriceCheckerVisitor : BasicVisitor { func visit ( _ anObject : AnyObject ) { if let obj = anObject as ? WarehouseItem { print ( " \( numele obiectului ) | Preț: \ ( prețul obiectului ) frecați . " ) } dacă fie _ = anObject ca ? Depozit { print ( "Cost none" ) } } } // Utilizați vizitator lăsați depozit = WarehouseImpl () depozit . addItem ( item : WarehouseItemImpl ( nume : „Articol 1” , isBroken : true , preț : 100 )) depozit . addItem ( articol : WarehouseItemImpl ( nume : " Articol 2 " , isBroken : fals , preț : 300 )) depozit . addItem ( articol : WarehouseItemImpl ( nume : „Articol 3” , isBroken : false , preț : 500 )) let price = PriceCheckerVisitor () let qulity = QualityCheckerVisitor () depozit . accept ( vizitator : preţ ) depozit . accept ( vizitator : calitate )

Literatură

  • E. Gamma, R. Helm, R. Johnson, J. Vlissides . Tehnici de proiectare orientată pe obiecte. Modele de design. - Sankt Petersburg. : Peter, 2001. - 368 p. — ISBN 5-272-00355-1 .

Link -uri