Látogató (tervminta)
Az oldal jelenlegi verzióját még nem ellenőrizték tapasztalt közreműködők, és jelentősen eltérhet a 2016. január 4-én felülvizsgált
verziótól ; az ellenőrzések 26 szerkesztést igényelnek .
Látogató |
---|
Látogató |
Típusú |
viselkedési |
Célja |
a fő osztály megváltoztatása nélkül adjon hozzá új műveleteket. |
Szerkezet |
|
esetekben érvényes |
amikor több osztályhoz hasonló (ugyanolyan) műveletet kell végrehajtani. |
profik |
- új funkciókat adnak egyszerre több osztályhoz anélkül, hogy megváltoztatnák ezen osztályok kódját ;
- lehetővé teszi, hogy információt szerezzen az objektum típusáról;
- kettős ütemezés;
- lehetőség saját algoritmus leírására minden objektumtípushoz .
|
Mínuszok |
- a kiszolgált osztály megváltoztatásakor módosítania kell a sablonkódot;
- nehéz új osztályokat felvenni, mert frissíteni kell a látogató és fiai hierarchiáját.
|
Leírása: Tervezési minták |
Igen |
A látogató egy viselkedési tervezési minta , amely leír egy műveletet, amelyet más osztályok objektumain hajtanak végre. Amikor látogatót vált, nincs szükség a kiszolgált osztályok megváltoztatására .
A sablon bemutatja az elveszett típusú információk visszaállításának klasszikus módszerét anélkül, hogy kétszeres
lefelé küldést kellene igénybe venni.
Probléma megoldva
El kell végeznie néhány leválasztott műveletet számos objektumon, de el kell kerülnie a kódjuk szennyezését. És nincs mód vagy vágy arra, hogy lekérdezzük az egyes csomópontok típusát, és a mutatót a megfelelő típusra irányítsuk a kívánt művelet végrehajtása előtt.
Kihívás
Valamely struktúra minden objektumán egy vagy több műveletet hajtanak végre. Új műveletet kell meghatároznia az objektumosztályok megváltoztatása nélkül.
Megoldás
A függetlenség érdekében a látogatónak külön hierarchiája van. A struktúráknak van egy bizonyos interakciós felülete.
Használat
Ha van esély arra, hogy a kiszolgált osztályhierarchia megváltozik, vagy instabil lesz, vagy a nyilvános felület elég hatékony ahhoz, hogy a sablon hozzáférjen, akkor annak használata rosszindulatú.
Egy alaposztály jön létre Visitormetódusokkal visit()a szülő minden alosztályához Element. Adjon hozzá egy metódust accept(visitor)az elemhierarchiához. Minden objektumon végrehajtandó művelethez Elementszármaztassa az Visitorosztályt a következőből. A metódusmegvalósításoknak visit()az osztály nyilvános felületét kell használniuk Element. Ennek eredményeként: a kliensek objektumokat hoznak létre, és a hívással Visitortovábbítják őket minden objektumnak .
Elementaccept()
Ajánlások
A sablont akkor kell használni, ha:
- különböző osztályú objektumok vannak különböző interfészekkel, de rajtuk olyan műveleteket kell végrehajtani, amelyek meghatározott osztályoktól függenek;
- különféle műveleteket kell végrehajtani a szerkezeten, amelyek bonyolítják a szerkezetet;
- gyakran új műveleteket adnak hozzá a szerkezethez.
Előnyök és hátrányok
Előnyök :
- leegyszerűsíti az új műveletek hozzáadását;
- kapcsolódó műveletek egyesítése az osztályban Visitor;
- az osztály Visitorképes emlékezni bizonyos állapotokra önmagában, amikor áthalad a tárolón.
Hátrányok :
- nehéz új osztályokat felvenni, mert frissíteni kell a látogató és fiai hierarchiáját.
Megvalósítás
- Adjon hozzá egy metódust accept(Visitor)az "elem" hierarchiához.
- Hozzon létre egy alaposztályt Visitor, és határozzon meg metódusokat visit()minden elemtípushoz.
- Hozzon létre származtatott osztályokat Visitorminden egyes elemekkel végzett művelethez.
- A kliens létrehoz egy objektumot Visitor, és átadja a hívott metódusnakaccept().
Megvalósítási példa
C++ nyelven
#include <iostream>
#include <karakterlánc>
osztály Foo ;
osztály Bár ;
osztály Bas ;
osztály látogató {
nyilvános :
virtuális érvénytelen látogatás ( Foo & ref ) = 0 ;
virtuális érvénytelen látogatás ( Bar & ref ) = 0 ;
virtuális érvénytelen látogatás ( Baz & ref ) = 0 ;
virtuális ~ Látogató () = alapértelmezett ;
};
class elem {
nyilvános :
virtuális üresedés elfogadása ( Látogató & v ) = 0 ;
virtuális ~ Elem () = alapértelmezett ;
};
osztály Foo : nyilvános elem {
nyilvános :
void elfogad ( Látogató és v ) felülbírálás {
v . látogatás ( * ez );
}
};
class Bar : public Element {
nyilvános :
void elfogad ( Látogató és v ) felülbírálás {
v . látogatás ( * ez );
}
};
class Baz : public Element {
nyilvános :
void elfogad ( Látogató és v ) felülbírálás {
v . látogatás ( * ez );
}
};
osztály GetType : nyilvános látogató {
nyilvános :
std :: karakterláncérték ; _
nyilvános :
void visit ( Foo & ref ) override {
érték = "foo" ;
}
void visit ( Bar & ref ) override {
érték = "sáv" ;
}
void visit ( Baz & ref ) override {
érték = "alap" ;
}
};
int main () {
Foo foo ;
Bár bár ;
baz baz ;
Element * elements [] = { & foo , & bar , & baz };
for ( auto elem : elements ) {
GetType látogató ;
elem -> elfogad ( látogató );
std :: cout << látogató . érték << std :: endl ;
}
return 0 ;
}
Java megvalósítási példa
public class Demo {
public static void main ( String [] args ) {
Point p = new Point2d ( 1 , 2 );
Látogató v = új Csebisev ();
o . elfogad ( v );
Rendszer . ki . println ( p . getMetric ( ) );
}
}
interface Látogató {
public void látogatás ( Pont2d p );
nyilvános üres látogatás ( Pont3d p );
}
abstract class Pont {
public abstract void elfogad ( Vistor v );
privát kettős metrika = - 1 ;
public double getMetric () {
return metrika ;
}
public void setMetric ( kettős metrika ) {
this . metrika = metrika ;
}
}
osztály Pont2d kiterjeszti a pontot {
public Pont2d ( double x , double y ) {
this . x = x ;
ezt . y = y_ _
}
public void elfogad ( Vistor v ) {
v . látogatás ( ez );
}
privát double x ;
public double getX () { return x ; }
magán dupla y ;
public double getY () { return y ; }
}
osztály Pont3d kiterjeszti a pontot {
public Point3d ( double x , double y , double z ) {
this . x = x ;
ezt . y = y_ _
ezt . z = z _
}
public void elfogad ( Vistor v ) {
v . látogatás ( ez );
}
privát double x ;
public double getX () { return x ; }
magán dupla y ;
public double getY () { return y ; }
privát kettős 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 () ) );
}
nyilvános érvénytelen látogatás ( pont3d p ) {
p . setMetric ( Math . sqrt ( p . getX () * p . getX () + p . getY () * p . getY () + p . getZ () * p . getZ () ) );
}
}
class Csebisev implementálja a Látogató {
public void látogatás ( Pont2d p ) {
double ax = Math . abs ( p.getX ( ) ) ; double -ay = Math . abs ( p . getY () ); o . setMetric ( ax > ay ? ax : ay ); } public void látogatás ( Pont3d p ) { double ax = Math . abs ( p.getX ( ) ) ; double -ay = Math . abs ( p . getY () ); double az = Math . absz ( p . getZ () ); double max = ax > ay ? ax : igen ; if ( max < az ) max = az ; o . setMetric ( max ); } }
Megvalósítási példa C# nyelven
public static class Demo
{
private static void Fő ()
{
Pont p = new Pont2D ( 1 , 2 );
IVisitor v = új Csebisev ();
o . elfogad ( v );
Konzol . WriteLine ( p . Metric );
}
}
belső interfész IVisitor
{
void Látogatás ( Pont2D p );
érvénytelen látogatás ( Point3Dp ) ;
}
belső absztrakt osztály Pont
{
public double Metric { get ; készlet ; } = -1 ; _ public abstract void Elfogadás ( IVisitor látogató ); }
belső osztály Pont2D : Pont
{
public Pont2D ( double x , double y )
{
X = x ;
Y = y_ _
}
public double X { get ; }
public double Y { get ; }
public override void Elfogadás ( IVisitor visitor )
{
látogató . látogatás ( ez );
}
}
belső osztály Pont3D : Pont
{
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 Elfogadás ( IVisitor visitor )
{
látogató . látogatás ( ez );
}
}
belső osztály Euklidész : IVisitor
{
public void Látogatás ( Pont2D p )
{
p . Metrika = Math . Sqrt ( p . X * p . X + p . Y * p . Y );
}
public void Látogatás ( Point3D p )
{
p . Metrika = Math . Sqrt ( p . X * p . X + p . Y * p . Y + p . Z * p . Z );
}
}
belső osztály Csebisev : IVisitor
{
public void Látogatás ( Pont2D p )
{
var ax = Math . abs ( X o . ); varay = matematika _ _ Abs ( p . Y ); o . Metrikus = ax > ay ? ax : igen ; }
public void Látogatás ( Pont3D p )
{
var ax = Math . abs ( X o . ); varay = matematika _ _ Abs ( p . Y ); var az = Math . Abs ( Z o . ); varmax = ax > ay ? _ ax : igen ; if ( max < az ) max = az ; o . Metrika = max ; } }
Példa megvalósításra php -ben
<?php
interface Látogató {
public function látogatás ( Pont $pont );
}
abstract class Pont {
public abstract function accept ( Látogató $látogató );
privát $_metrika = - 1 ;
public function getMetric () {
return $this -> _metric ;
}
public function setMetric ( $metrika ) {
$this -> _metric = $metrika ;
}
}
osztály Pont2d kiterjeszti a { pontot
public function __construct ( $x , $y ) {
$this -> _x = $x ;
$this -> _y = $y ;
}
public function accept ( Látogató $látogató ) {
$látogató -> látogatás ( $this );
}
privát $_x ;
public function getX () { return $this -> _x ; }
privát $_y ;
public function getY () { return $this -> _y ; }
}
class Point3d kiterjeszti a pontot {
public function __construct ( $x , $y , $z ) {
$this -> _x = $x ;
$this -> _y = $y ;
$ez -> _z = $z ;
}
public function accept ( Látogató $látogató ) {
$látogató -> látogatás ( $this );
}
privát $_x ;
public function getX () { return $this -> _x ; }
privát $_y ;
public function getY () { return $this -> _y ; }
privát $_z ;
public function getZ () { return $this -> _z ; }
}
class Euclid implements Visitor {
public function látogatás ( $p pont ) { if ( $p instanceof Point2d ) $p -> setMetric ( sqrt ( $p -> getX () * $p -> getX () + $p -> getY () * $p -> getY () ) ); elseif ( $p instanceof Point3d ) $p -> setMetric ( sqrt ( $p -> getX () * $p -> getX () + $p -> getY () * $p -> getY () + $p - > getZ () * $p -> getZ () ) ); } }
class Chebisev implements Visitor {
public function látogatás ( $p pont ) { 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 ); } } }
függvény kezdete (){
$p = new Pont2d ( 1 , 2 );
$v = új Csebisev ( );
$p -> elfogad ( $v );
echo ( $p -> getMetric () );
};
start ();
Megvalósítási példa Pythonban
abc importból ABCMeta , abstractmethod gépelésből import List _ _
osztály kém ( metaclass = ABCMeta ):
"""
Kémlátogató
"""
@abstractmethod
def visit_military_base ( self , military_base : 'MilitaryBase' ) -> Nincs :
""" A
haditengerészet katonai bázisának felkeresése ""
"
belépő
@abstractmethod
def visit_headquarters ( self , headquarters : 'Headquarters' ) -> None :
"""
Látogassa meg a hadsereg főhadiszállását
"""
igazolvány
osztály MilitaryFacility ( metaclass = ABCMeta ):
"""
Katonai létesítmény - meglátogatott létesítmény
"""
@abstractmethod
def accept ( self , spy : Spy ) -> None :
"""
Kémlátogató elfogadása
"""
pass
osztályú katonai bázis ( MilitaryFacility ):
"""
Tengeralattjáró katonai bázis
"""
def __init__ ( self ) -> None :
self . _secret_draftings = 1
én . _nukleáris_tengeralattjárók = 1
def __repr__ ( self ) -> str :
return 'A katonai bázisnak {} nukleáris tengeralattjárója és {} titkos tervrajza van' . formátum (
saját . _nukleáris_tengeralattjárók , saját . _titkos_rajzok
)
def accept ( self , spy : Spy ) -> None :
spy . látogatás_katonai_bázis ( saját )
def remove_secret_draftings ( self ) -> None :
if self . _secret_draftings :
saját . _titkos_rajzok -= 1
def remove_nuclear_submarine ( self ) -> None :
if self . _nukleáris_tengeralattjárók :
saját . _nukleáris_tengeralattjárók -= 1
@property
def is_combat_ready ( self ) -> bool :
önmagát adja vissza . _nukleáris_tengeralattjárók > 0
osztályú parancsnokság ( MilitaryFacility ):
"""
Hadsereg parancsnoksága
"""
def __init__ ( self ) -> None :
self . _generals = 3
én . _secret_documents = 2
def __repr__ ( self ) -> str :
return 'A főhadiszálláson {} tábornok és {} titkos dokumentum van ' . formátum (
self . _generals , self . _secret_documents
)
def accept ( self , spy : Spy ) -> None :
spy . visit_headquarters ( saját )
def remove_general ( self ) -> None :
if self . _generálisok :
én . _tábornokok -= 1
def remove_secret_documents ( self ) -> None :
if self . _secret_documents :
saját . _titkos_dokumentumok -= 1
@property
def is_command_ready ( self ) -> bool :
önmagát adja vissza . _generals > 0
osztály ScoutSpy ( Spy ):
"""
Scout (konkrét kém)
"""
def __init__ ( self ):
self . _collected_info = {}
# Itt már ismerjük a konkrét objektumtípust
def visit_military_base ( self , military_base : MilitaryBase ) -> None :
self . _collected_info [ 'base' ] = 'Katonai bázis: \n\t {} \n\t Kész: {} ' . formátum (
str ( katonai_bázis ),
'Igen' , ha katonai_bázis . is_combat_ready else 'Nem' )
def visit_headquarters ( self , headquarters : Headquarters ) -> None :
self . _collected_info [ 'headquarters' ] = 'Székhely: \n\t {} \n\t Parancs: {} ' . formátum (
str ( headquarters ),
'Futó' , ha headquarters . is_command_ready else 'Nem működik'
)
def report ( self ) -> str :
return 'Információ a felderítőtől: \n {} \n ' . format (
' \n ' . join ( self . _collected_info . Values ())
)
osztály JamesBond ( Kém ):
"""
James Bond (egy másik konkrét kém)
"""
def visit_military_base ( saját , katonai_bázis : MilitaryBase ) -> Nincs : # James Bond meglátogatja a katonai bázist katonai_bázis . remove_secret_draftings () # ellopja a katonai_bázis titkos rajzait . remove_nuclear_submarine () # és végül felrobbant egy nukleáris tengeralattjárót
def visit_headquarters ( self , headquarters : Headquarters ) -> None :
# James Bond meglátogatja a
központot . remove_general () # ...
főhadiszállás . remove_general () # ...
főhadiszállás . remove_secret_documents () # ...
főhadiszállás . remove_general () # Megsemmisíti az összes tábornokot egymás után
headquarters . remove_secret_documents () # és ellopja az összes titkos dokumentumot
if __name__ == '__main__' :
bázis = Katonai Bázis ()
hq = Főhadiszállás ()
# Nem számít, melyik katonai
létesítmény létesítményei = [ alap , hq ] # típusa: Lista[Katonai létesítmény]
scout = ScoutSpy ()
print ( 'Sending a Scout... \n ' )
for f in létesítmények :
f . elfogad ( cserkész )
nyomtatás ( scout.report ( ) )
print ( 'Sending Bond on a mission... \n ' )
spy = JamesBond ()
f - re a létesítményekben : f . elfogad ( kém )
print ( 'Felderítő küldése az adatok frissítésére... \n ' )
for f in létesítményekben :
f . elfogad ( cserkész )
nyomtatás ( scout.report ( ) )
"""
KIMENET:
Felderítőt küldeni...
Információk a felderítőtől:
Központi parancsnokság:
A parancsnokságon 3 tábornok és 2 titkos dokumentum található
Parancsnokság: Működő
Katonai bázis:
A katonai bázison 1 db atomtengeralattjáró és 1 db titkos rajz található
Harckészültség: Igen
Bond küldetése...
Felderítő küldése az adatok frissítésére...
Információ a felderítőtől:
Központi parancsnokság:
0 tábornok és 0 titkos dokumentum van a parancsnokságon
Parancsnokság: Nem működik
Katonai bázis:
0 atomtengeralattjáró és 0 titkos rajz található a katonai bázison
Felkészültség: Nincs
"""
Megvalósítási példa Delphiben
program Demo ;
típus
Point2D = osztály ;
Point3D = osztály ;
IVisitor = interfész
eljárás Visit ( p : Point2D ) ; túlterhelés ;
eljárás Látogatás ( p : Point3D ) ; túlterhelés ;
vége ;
Pont = privát osztály
FMetric : Double ; köztulajdon Metric : Dupla olvasás FMetric FMetric írás ; _ eljárás Elfogadás ( látogató : IVisitor ) ; virtuális ; absztrakt ; vége ;
Pont2D = osztály ( Pont )
privát
FX : Double ;
FY : Dupla ;
köztulajdon
X : Dupla olvasás FX ; _ Y tulajdonság : Dupla olvasás FY ;
konstruktor Létrehozás ( const x , y : Double ) ;
eljárás Elfogadás ( Látogató : IVisitor ) ; felülbírálni ;
vége ;
Point3D = osztály ( Pont )
privát
FX : Double ;
FY : Dupla ;
FZ : Dupla ;
köztulajdon
X : Dupla olvasás FX ; _ Y tulajdonság : Dupla olvasás FY ; tulajdonság Z : Dupla olvasás FZ ;
konstruktor Létrehozás ( const x , y , z : Double ) ;
eljárás Elfogadás ( Látogató : IVisitor ) ; felülbírálni ;
vége ;
Euklid = osztály ( TInterfacedObject , IVisitor )
nyilvános
eljárás Látogatás ( p : Point2D ) ; túlterhelés ;
eljárás Látogatás ( p : Point3D ) ; túlterhelés ;
vége ;
Chebisev = osztály ( TInterfacedObject , IVisitor )
nyilvános
eljárás Látogatás ( p : Point2D ) ; túlterhelés ;
eljárás Látogatás ( p : Point3D ) ; túlterhelés ;
vége ;
{Point2D}
eljárás Pont2D . Elfogadás ( Látogató : IVisitor ) ;
Kezdje
Látogató . Látogatás ( Self ) ;
vége ;
konstruktor Point2D . Létrehozás ( const x , y : Double ) ;
kezdődik
FX := x ;
FY := y ;
vége ;
{Point3D}
eljárás Point3D . Elfogadás ( Látogató : IVisitor ) ;
kezdődik
Látogató . Látogatás ( Self ) ;
vége ;
konstruktor Point3D . Létrehozása ( const x , y , z : Double ) ;
kezdődik
FX := x ;
FY := y ;
FX := z ;
vége ;
{ Euklidész }
eljárás Eulid . Látogatás ( p : Point2D ) ;
kezdődik
p . Metrika := Sqrt ( Sqr ( p . X ) + Sqr ( p . Y )) ;
vége ;
eljárás Eulid . Látogatás ( p : Point3D ) ;
kezdődik
p . Metrikus := Sqrt ( Sqr ( p . X ) + Sqr ( p . Y ) + Sqr ( p . Z )) ;
vége ;
{Csebisev}
eljárás Csebisev . Látogatás ( p : Point2D ) ;
var
ax , ay : Kettős ;
begin
ax := Abs ( p . X ) ;
ay := Abs ( p . Y ) ;
if ax > ay then
p . Metrikus := ax
else
p . Metrikus : = igen vége ;
eljárás Csebisev . Látogatás ( p : Point3D ) ;
var
ax , ay , az , max : Double ;
begin
ax := Abs ( p . X ) ;
ay := Abs ( p . Y ) ;
az := Abs ( p . Z ) ;
if ax > ay akkor
max := ax
else
max := ay ;
if max < az then
max := az ;
o . Metrika := max ;
vége ;
varp
: pont ; _ v : IVVisitor ; kezdődik p := Pont2D . Létrehoz ( 1 , 2 ) ;
v := Csebisev . létrehozni ;
o . elfogadja ( v ) ;
WriteLn ( p . Metric : 0 : 2 ) ;
v := Eulid . létrehozni ;
o . elfogadja ( v ) ;
WriteLn ( p . Metric : 0 : 2 ) ;
o . Ingyenes ;
Readln ; // várja meg, amíg megnyomja az Entert
end .
Megvalósítási példa Swiftben
protokoll WarehouseItem {
var name : String { get set }
var isBroken : Bool { get set }
var price : Int { get set }
}
class WarehouseItemImpl : WarehouseItem {
var név : String = ""
var isBroken : Bool = false
var price : Int = 0
init ( név : String , isBroken : Bool , ár : Int ) {
self . név = név
én . isBroken = isBroken
self . ár = ár
}
}
protokoll Warehouse {
var items : [ WarehouseItem ] { get set }
func addItem ( item : WarehouseItem )
func accept ( látogató : BasicVisitor )
}
class WarehouseImpl : Raktár {
var items : [ WarehouseItem ] = []
func addItem ( cikk : WarehouseItem ) {
tételek . hozzáfűzés ( elem )
}
func accept ( visitor : BasicVisitor ) {
for item in items {
visitor . látogatás ( elem AnyObject néven ) } } }
protokoll BasicVisitor {
func visit ( _ anObject : AnyObject )
}
class QualityCheckerVisitor : BasicVisitor {
func visit ( _ anObject : AnyObject ) {
if let obj = anObject as ? Raktárcikk {
if obj . isBroken {
print ( "is törött igaz" )
} else {
print ( "tört hamis" )
}
if let _ = anObject as ? Raktár {
nyomtatás ( "Jó raktár " )
}
}
}
class PriceCheckerVisitor : BasicVisitor {
func visit ( _ anObject : AnyObject ) {
if let obj = anObject as ? WarehouseItem {
print ( " \( obj . name ) | Ár: \( obj . price ) rub." )
}
if let _ = anObject as ? Raktár {
nyomtatás ( " Nincs költség" )
}
}
// A Visitor használata
let raktár = WarehouseImpl ()
raktár . addItem ( cikk : WarehouseItemImpl ( név : "Tétel 1" , isBroken : true , ár : 100 ))
raktár . addItem ( cikk : WarehouseItemImpl ( név : "Tétel 2" , isBroken : false , ár : 300 ))
raktár . addItem ( cikk : WarehouseItemImpl ( név : " 3. cikk " , törött : false , ár : 500 ))
let price = PriceCheckerVisitor ()
let qulity = QualityCheckerVisitor ()
raktár . elfogad ( látogató : ár )
raktár . elfogad ( látogató : qulity )
Irodalom
- E. Gamma, R. Helm, R. Johnson, J. Vlissides . Objektumorientált tervezés technikái. Tervezési minták. - Szentpétervár. : Péter, 2001. - 368 p. — ISBN 5-272-00355-1 .
Linkek