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:
- der er forskellige objekter af forskellige klasser med forskellige grænseflader, men der skal udføres operationer på dem, der afhænger af specifikke klasser;
- det er nødvendigt at udføre forskellige operationer på strukturen, der komplicerer strukturen;
- nye operationer på strukturen tilføjes ofte.
Fordele og ulemper
Fordele :
- forenkler tilføjelsen af nye operationer;
- sammenslutning af relaterede operationer i klassen Visitor;
- klassen Visitorkan huske en tilstand i sig selv, når den krydser beholderen.
Ulemper :
- det er svært at tilføje nye klasser, fordi hierarkiet for den besøgende og hans sønner skal opdateres.
Implementering
- Tilføj en metode accept(Visitor)til "element"-hierarkiet.
- Opret en basisklasse Visitorog definer metoder visit()for hver elementtype.
- Opret afledte klasser Visitorfor hver operation udført på elementer.
- Klienten opretter et objekt Visitorog sender det til den kaldte metodeaccept().
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 ;
}
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 ); } }
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 { få ; }
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 { få ; }
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 ; } }
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 ();
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
"""
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 så
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 så
max := ax
else
max := ay ;
hvis max < az så
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 .
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