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ă:
- există diverse obiecte de clase diferite cu interfețe diferite, dar trebuie efectuate operațiuni asupra lor care depind de clase specifice;
- este necesar să se efectueze diverse operații asupra structurii care complică structura;
- se adaugă adesea noi operații asupra structurii.
Avantaje și dezavantaje
Beneficii :
- simplifică adăugarea de noi operațiuni;
- unirea operațiunilor conexe în clasă Visitor;
- clasa Visitorîși poate aminti o stare în sine în timp ce traversează containerul.
Dezavantaje :
- este dificil să adaugi noi clase, deoarece ierarhia vizitatorului și a fiilor săi trebuie actualizată.
Implementare
- Adăugați o metodă accept(Visitor)la ierarhia „element”.
- Creați o clasă de bază Visitorși definiți metode visit()pentru fiecare tip de element.
- Creați clase derivate Visitorpentru fiecare operație efectuată pe elemente.
- Clientul creează un obiect Visitorși îl transmite metodei apelateaccept().
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 ;
}
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 ); } }
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 ; } }
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 ();
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
"""
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 .
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