Besökare (designmönster)
Den aktuella versionen av sidan har ännu inte granskats av erfarna bidragsgivare och kan skilja sig väsentligt från
versionen som granskades den 4 januari 2016; kontroller kräver
26 redigeringar .
Besökare |
---|
Besökare |
Sorts |
beteendemässiga |
Ändamål |
utan att ändra huvudklassen , lägg till nya operationer till den. |
Strukturera |
|
Gäller i ärenden |
när det är nödvändigt att utföra en liknande (samma) operation för ett antal klasser. |
fördelar |
- ny funktionalitet läggs till i flera klasser samtidigt utan att koden för dessa klasser ändras;
- låter dig få information om typen av ett objekt;
- dubbel schemaläggning;
- möjlighet att beskriva en egen algoritm för varje typ av objekt .
|
Minus |
- när du byter servad klass måste du ändra mallkoden;
- det är svårt att lägga till nya klasser, eftersom hierarkin för besökaren och hans söner behöver uppdateras.
|
Beskrivs i Design Patterns |
Ja |
En besökare är ett beteendedesignmönster som beskriver en operation som utförs på objekt av andra klasser. När du byter besökare behöver du inte ändra de betjänade klasserna .
Mallen visar den klassiska metoden för att återställa förlorad typinformation utan att behöva dubbelsända
downcast .
Problem löst
Du måste göra några frånkopplade operationer på ett antal objekt, men du måste undvika att förorena deras kod. Och det finns inget sätt eller någon önskan att fråga typen av varje nod och kasta pekaren till rätt typ innan du utför den önskade operationen.
Utmana
En eller flera operationer utförs på varje objekt av någon struktur. Du måste definiera en ny operation utan att ändra objektklasserna.
Lösning
För oberoende har besökaren en separat hierarki. Strukturer har ett visst interaktionsgränssnitt.
Användning
Om det finns en chans att den betjänade klasshierarkin kommer att ändras, eller den kommer att vara instabil, eller det offentliga gränssnittet är tillräckligt effektivt för att mallen ska kunna komma åt, så är användningen skadlig.
En basklass skapas Visitormed metoder visit()för varje underklass av föräldern Element. Lägg till en metod accept(visitor)i elementhierarkin. För varje operation som måste utföras på objekt Element, härled en Visitorklass från. Metodimplementationer visit()måste använda klassens publika gränssnitt Element. Som ett resultat: klienter skapar objekt Visitoroch skickar dem till varje objekt Elementgenom att anropa accept().
Rekommendationer
Mallen ska användas om:
- det finns olika objekt av olika klasser med olika gränssnitt, men operationer måste utföras på dem som beror på specifika klasser;
- det är nödvändigt att utföra olika operationer på strukturen som komplicerar strukturen;
- nya operationer på strukturen tillkommer ofta.
Fördelar och nackdelar
Fördelar :
- förenklar tillägget av nya verksamheter;
- sammanslutning av relaterade verksamheter i klassen Visitor;
- klassen Visitorkan komma ihåg något tillstånd i sig när den korsar behållaren.
Nackdelar :
- det är svårt att lägga till nya klasser, eftersom hierarkin för besökaren och hans söner behöver uppdateras.
Implementering
- Lägg till en metod accept(Visitor)i "element"-hierarkin.
- Skapa en basklass Visitoroch definiera metoder visit()för varje elementtyp.
- Skapa härledda klasser Visitorför varje operation som utförs på element.
- Klienten skapar ett objekt Visitoroch skickar det till den anropade metodenaccept().
Implementeringsexempel i
C++
#include <iostream>
#inkludera <sträng>
klass Foo ;
klass Bar ;
klass Bas ;
klass besökare {
offentliga :
virtuellt void besök ( Foo & ref ) = 0 ;
virtuellt void besök ( Bar & ref ) = 0 ;
virtuellt void - besök ( Baz & ref ) = 0 ;
virtuell ~ Besökare () = default ;
};
klasselement { _
offentliga :
virtuellt void acceptera ( Besökare & v ) = 0 ;
virtuell ~ Element () = default ;
};
klass Foo : public Element {
offentliga :
void accept ( besökare & v ) åsidosätt {
v . besök ( * detta );
}
};
class Bar : public Element {
offentliga :
void accept ( besökare & v ) åsidosätt {
v . besök ( * detta );
}
};
klass Baz : public Element {
offentliga :
void accept ( besökare & v ) åsidosätt {
v . besök ( * detta );
}
};
class GetType : public Visitor {
offentliga :
std :: strängvärde ; _
offentliga :
void visit ( Foo & ref ) åsidosätta {
värde = "foo" ;
}
void visit ( Bar & ref ) åsidosätta {
värde = "stapel" ;
}
void visit ( Baz & ref ) åsidosätta {
värde = "bas" ;
}
};
int main () {
Foo foo ;
Bar bar ;
baz baz ;
Element * elements [] = { & foo , & bar , & baz };
for ( auto elem : element ) {
GetType besökare ;
elem -> acceptera ( besökare );
std :: cout << besökare . värde << std :: endl ;
}
returnera 0 ;
}
Exempel på Java -implementering
public class Demo {
public static void main ( String [] args ) {
Point p = new Point2d ( 1 , 2 );
Besökare v = ny Chebyshev ();
sid . acceptera ( v );
System . ut . println ( s . getMetric () );
}
}
gränssnitt Besökare {
public void visit ( Point2d p );
offentligt tomrumsbesök ( Point3d p ) ; }
abstrakt klass Point {
public abstract void accept ( besökare v );
privat dubbel metrisk = - 1 ;
public double getMetric () {
return metric ;
}
public void setMetric ( double metric ) {
this . metrisk = metrisk ;
}
}
klass Point2d utökar Point {
public Point2d ( double x , double y ) {
this . x = x ;
detta . y = y _
}
public void accept ( Besökare v ) {
v . besök ( detta );
}
privat dubbel x ;
public double getX () { return x ; }
privat dubbel y ;
public double getY () { return y ; }
}
klass Point3d utökar Point {
public Point3d ( double x , double y , double z ) {
this . x = x ;
detta . y = y _
detta . z = z _
}
public void accept ( Besökare v ) {
v . besök ( detta );
}
privat dubbel x ;
public double getX () { return x ; }
privat dubbel y ;
public double getY () { return y ; }
privat dubbel 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 () ) );
}
offentligt void besök ( Point3d p ) {
p . setMetric ( Math . sqrt ( p . getX ( ) * p . getX () + p . getY () * p . getY () + p . getZ () * p . getZ () ) );
}
}
klass Chebyshev implementerar Besökare {
public void visit ( Point2d p ) {
double ax = Math . abs ( p.getX ( ) ) ; double -ay = Math . abs ( s . getY () ); sid . setMetric ( ax > ay ? ax : ay ); } offentligt void besök ( Point3d p ) { double ax = Math . abs ( p.getX ( ) ) ; double -ay = Math . abs ( s . getY () ); dubbel az = Math . abs ( p . getZ () ); dubbel max = ax > ay ? yxa : ay ; if ( max < az ) max = az ; sid . setMetric ( max ); } }
Implementeringsexempel i
C#
public static class Demo
{
private static void Main ()
{
Point p = new Point2D ( 1 , 2 );
IVisitor v = ny Chebyshev ();
sid . acceptera ( v );
Konsol . WriteLine ( s . Metric );
}
}
internt gränssnitt IVisitor
{
void Visit ( Point2D p );
void besök ( Point3Dp ) ;
}
intern abstrakt klass Point
{
public double Metric { get ; set ; } = - 1 ;
public abstract void Acceptera ( IVbesökare ) ;
}
intern klass Point2D : Point
{
public Point2D ( double x , double y )
{
X = x ;
Y = y _
}
public double X { get ; }
public double Y { get ; }
public override void Acceptera ( IVbesökare ) { besökare . _ besök ( detta ); } }
intern klass Point3D : Point
{
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 Acceptera ( IVbesökare ) { besökare . _ besök ( detta ); } }
intern klass Euclid : IVisitor
{
public void Visit ( Point2D p )
{
p . Metrisk = matematik . Sqrt ( p . X * p . X + p . Y * p . Y );
}
public void Besök ( Point3D p )
{
p . Metrisk = matematik . Sqrt ( p . X * p . X + p . Y * p . Y + p . Z * p . Z );
}
}
intern klass Chebyshev : IVisitor
{
public void Visit ( Point2D p )
{
var ax = Math . abs ( sid X ) ; varay = matematik . _ Abs ( sid Y ) ; sid . Metrisk = axe > ay ? yxa : ay ; }
public void Besök ( Point3D p )
{
var ax = Math . abs ( s . X );
varay = matematik . _ Abs ( sid Y ) ; var az = Math . Abs ( sid Z ) ; varmax = ax > ay ? _ yxa : ay ; if ( max < az ) max = az ; sid . Metrisk = max ; } }
Exempel på implementering i
php
<?php
gränssnitt Visitor {
public function visit ( Punkt $point );
}
abstract class Point {
public abstract function accept ( besökare $besökare );
privat $_metric = - 1 ;
public function getMetric () {
return $this -> _metric ;
}
public function setMetric ( $metrisk ) {
$this -> _metric = $metrisk ;
}
}
klass Point2d utökar Point {
offentlig funktion __construct ( $x , $y ) {
$this -> _x = $x ;
$this -> _y = $y ;
}
public function accept ( Besökare $besökare ) {
$besökare -> besök ( $detta );
}
privat $_x ;
public function getX () { return $this -> _x ; }
privat $_y ;
public function getY () { return $this -> _y ; }
}
klass Point3d utökar Point {
public function __construct ( $x , $y , $z ) {
$this -> _x = $x ;
$this -> _y = $y ;
$this -> _z = $z ;
}
public function accept ( Besökare $besökare ) {
$besökare -> besök ( $detta );
}
privat $_x ;
public function getX () { return $this -> _x ; }
privat $_y ;
public function getY () { return $this -> _y ; }
privat $_z ;
public function getZ () { return $this -> _z ; }
}
klass Euclid implementerar Besökare {
public function visit ( Point $p ) {
if ( $p instanceof Point2d )
$p -> setMetric ( sqrt ( $p -> getX () * $p -> getX () + $p -> getY () * $p -> getY () ) );
elseif ( $p instans av Point3d )
$p -> setMetric ( sqrt ( $p -> getX () * $p -> getX () + $p -> getY () * $p -> getY () + $p - > getZ () * $p -> getZ () ) );
}
}
class Chebyshev implementerar Visitor {
public function visit ( Point $p ) {
if ( $p instanceof Point2d ){
$ax = abs ( $p -> getX () );
$ay = abs ( $p -> getY () );
$p -> setMetric ( $ax > $ay ? $ax : $ay );
}
elseif ( $p instans av 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 -> acceptera ( $v );
echo ( $p -> getMetric () );
};
start ();
Implementeringsexempel i
Python
från abc import ABCMeta , abstraktmetod
från att skriva importlista
class Spy ( metaclass = ABCMeta ):
"""
Spionbesökare
"""
@abstractmethod
def visit_military_base ( self , military_base : 'MilitaryBase' ) -> Inga :
"""
Besök marinens militärbas
"""
pass
@abstractmethod
def visit_headquarters ( själv , högkvarter : 'Högkvarter' ) -> Inga :
"""
Besök arméns högkvarter
"""
pass
class MilitaryFacility ( metaclass = ABCMeta ):
"""
Militär anläggning - besökt anläggning
"""
@abstractmethod
def accept ( själv , spion : Spy ) -> Ingen :
"""
Acceptera spionbesökare
"""
pass
klass MilitaryBase ( MilitaryFacility ):
"""
Ubåtsmilitärbas
"""
def __init__ ( själv ) -> Ingen :
själv . _secret_draftings = 1
själv . _kärnkraftsubåtar = 1
def __repr__ ( self ) -> str :
return 'Militärbasen har {} atomubåtar och {} hemliga ritningar' . format (
self . _nuclear_ubåtar , self . _secret_draftings
)
def accept ( själv , spion : spion ) -> Ingen :
spion . visit_military_base ( själv )
def remove_secret_draftings ( self ) -> None :
if self . _hemliga_utkast :
själv . _hemliga_utkast -= 1
def remove_nuclear_submarine ( self ) -> None :
if self . _atomubåtar :
själv . _kärnkraftsubåtar -= 1
@property
def is_combat_ready ( self ) -> bool :
returnera själv . _atomubåtar > 0
klass Högkvarter ( MilitaryFacility ):
"""
Arméhögkvarteret
"""
def __init__ ( själv ) -> Ingen :
själv . _generals = 3
själv . _hemliga_dokument = 2
def __repr__ ( self ) -> str :
return 'Det finns {} generaler och {} hemliga dokument vid högkvarteret ' . format ( self . _generals , self . _secret_documents )
def accept ( själv , spion : spion ) -> Ingen :
spion . visit_headquarters ( själv )
def remove_general ( self ) -> None :
if self . _generaler :
själv . _generals -= 1
def remove_secret_documents ( self ) -> None :
if self . _hemliga_dokument :
själv . _hemliga_dokument -= 1
@property
def is_command_ready ( self ) -> bool :
returnera själv . _generals > 0
klass ScoutSpy ( Spy ):
"""
Scout (konkret spion)
"""
def __init__ ( self ):
self . _samlad_info = {}
# Här känner vi redan till den specifika objekttypen
def visit_military_base ( self , military_base : MilitaryBase ) -> None :
self . _collected_info [ 'base' ] = 'Militärbas: \n\t {} \n\t Klar: {} ' . format (
str ( militärbas ),
'Ja' om militärbas . is_combat_ready else 'Nej'
)
def visit_headquarters ( själv , huvudkontor : Högkvarter ) -> Inget :
själv . _collected_info [ 'headquarters' ] = 'Högkvarter: \n\t {} \n\t Kommando: {} ' . format (
str ( huvudkontor ),
"Körs" om huvudkontoret . is_command_ready else "Inte i drift"
)
def report ( self ) -> str :
return 'Information från scouten: \n {} \n ' . format (
' \n ' . join ( self . _collected_info . values ())
)
klass JamesBond ( Spy ):
"""
James Bond (en annan specifik spion)
"""
def visit_military_base ( self , military_base : MilitaryBase ) -> Ingen :
# James Bond besöker militärbasen
militärbas . remove_secret_draftings () # stjäl militärbasens hemliga ritningar
. remove_nuclear_submarine () # och till slut spränger en atomubåt i luften
def visit_headquarters ( själv , huvudkontor : Headquarters ) -> Inga :
# James Bond besöker
huvudkontoret . remove_general () # ...
huvudkontor . remove_general () # ...
huvudkontor . remove_secret_documents () # ...
huvudkontor . remove_general () # Förstör alla generaler sekventiellt
högkvarter . remove_secret_documents () # och stjäl alla hemliga dokument
om __namn__ == '__main__' :
bas = MilitaryBase ()
hq = Högkvarter ()
# Oavsett vilka MilitaryFacility -
anläggningar = [ bas , hq ] # typ: List[MilitaryFacility]
scout = ScoutSpy ()
print ( 'Skicka en scout... \n ' )
för f i anläggningar :
f . acceptera ( scout )
print ( scout.report ( ) )
print ( 'Skicka Bond på ett uppdrag... \n ' )
spion = JamesBond ()
för f i anläggningar :
f . acceptera ( spion )
print ( 'Skicka en scout för att uppdatera data... \n ' )
för f in anläggningar :
f . acceptera ( scout )
print ( scout.report ( ) )
"""
UTGÅNG:
Skickar en scout...
Information från scouten:
Centrala högkvarteret:
Det finns 3 generaler och 2 hemliga dokument i högkvarteret
Kommando: Fungerande
Militärbas:
Det finns 1 atomubåt och 1 hemlig ritning i militärbasen
Stridsberedskap: Ja
Skickar Bond på uppdrag...
Skickar en scout för att uppdatera data...
Information från scouten:
Centrala högkvarteret:
Det finns 0 generaler och 0 hemliga dokument i högkvarteret
Kommando: Inte fungerande
Militärbas:
Det finns 0 atomubåtar och 0 hemliga ritningar i militärbasen
Beredskap: Inga
"""
Implementeringsexempel i
Delphi
programdemo ; _
typ
Point2D = klass ;
Point3D = klass ;
IVisitor = gränssnittsprocedur
Besök ( p : Point2D ) ; _ överbelastning ; procedur Besök ( s : Point3D ) ; överbelastning ; slut ;
Point = klass
privat
FMetrisk : Dubbel ;
public
property Metrisk : Dubbelläs FMetrisk skriv FMetrisk ; _ förfarande Acceptera ( besökare : IVbesökare ) ; virtuell ; abstrakt ; slut ;
Point2D = klass ( Point )
privat
FX : Dubbel ;
FY : Dubbel ;
public
property X : Dubbelläs FX ; _ egenskap Y : Dubbelläs FY ; _
konstruktor Skapa ( const x , y : Double ) ;
förfarande Acceptera ( Besökare : IVbesökare ) ; åsidosätta ;
slut ;
Point3D = klass ( Point )
privat
FX : Dubbel ;
FY : Dubbel ;
FZ : Dubbel ;
public
property X : Dubbelläs FX ; _ egenskap Y : Dubbelläs FY ; _ egenskap Z : Dubbelläst FZ ; _
konstruktor Skapa ( const x , y , z : Double ) ;
förfarande Acceptera ( Besökare : IVbesökare ) ; åsidosätta ;
slut ;
Euklid = klass ( TInterfacedObject , IVisitor )
offentligt
förfarande Besök ( p : Point2D ) ; överbelastning ;
procedur Besök ( s : Point3D ) ; överbelastning ;
slut ;
Chebyshev = klass ( TInterfacedObject , IVisitor )
offentligt
förfarande Besök ( p : Point2D ) ; överbelastning ;
procedur Besök ( s : Point3D ) ; överbelastning ;
slut ;
{Point2D}
procedur Point2D . Acceptera ( Besökare : IVbesökare ) ;
börja
Besökare . Besök ( Själv ) ;
slut ;
konstruktör Point2D . Skapa ( const x , y : Double ) ;
börja
FX := x ;
FY := y ;
slut ;
{Point3D}
procedur Point3D . Acceptera ( Besökare : IVbesökare ) ;
börja
Besökare . Besök ( Själv ) ;
slut ;
konstruktör Point3D . Skapa ( const x , y , z : Double ) ;
börja
FX := x ;
FY := y ;
FX := z ;
slut ;
{ Euklid }
förfarande Eulid . Besök ( p : Point2D ) ;
börja
s . Metrisk := Sqrt ( Sqr ( p . X ) + Sqr ( p . Y )) ;
slut ;
förfarande Eulid . Besök ( p : Point3D ) ;
börja
s . Metrisk := Sqrt ( Sqr ( p . X ) + Sqr ( p . Y ) + Sqr ( p . Z )) ;
slut ;
{Chebyshev}
förfarande Chebyshev . Besök ( p : Point2D ) ;
var
axe , ay : Dubbel ;
börja
ax := Abs ( s . X ) ;
ay := Abs ( p . Y ) ;
om ax > ay då
p . Metrisk := ax
annat
p . Metrisk : = ja slut ;
förfarande Chebyshev . Besök ( p : Point3D ) ;
var
ax , ay , az , max : Dubbel ;
börja
ax := Abs ( s . X ) ;
ay := Abs ( p . Y ) ;
az : = Abs ( p.Z ) ; _ om ax > ay då max := ax annars max := ay ; om max < az då max := az ; sid . Metrisk := max ; slut ;
varp
: Punkt ; _ v : IVVisitor ; börja p := Point2D . Skapa ( 1 , 2 ) ;
v := Chebyshev . skapa ;
sid . acceptera ( v ) ;
WriteLn ( s . Metrisk : 0 : 2 ) ;
v := Eulid . skapa ;
sid . acceptera ( v ) ;
WriteLn ( s . Metrisk : 0 : 2 ) ;
sid . Gratis ;
Läsln ; // vänta på tryck Enter
end .
Implementeringsexempel i
Swift
protokoll WarehouseItem {
var name : String { get set }
var isBroken : Bool { get set }
var price : Int { get set }
}
class WarehouseItemImpl : WarehouseItem {
var name : String = ""
var isBroken : Bool = false
var price : Int = 0
init ( namn : String , isBroken : Bool , pris : Int ) {
self . namn = namn
själv . isBroken = ärBroken
self . pris = pris
}
}
protokoll Warehouse {
var items : [ WarehouseItem ] { get set }
func addItem ( artikel : WarehouseItem )
func accept ( besökare : BasicVisitor )
}
class WarehouseImpl : Warehouse {
var items : [ WarehouseItem ] = []
func addItem ( artikel : WarehouseItem ) {
items . lägga till ( objekt )
}
func accept ( besökare : BasicVisitor ) {
för objekt i objekt {
besökare . besök ( objekt som AnyObject )
}
}
}
protokoll BasicVisitor {
func visit ( _ anObject : AnyObject )
}
class QualityCheckerVisitor : BasicVisitor {
func besök ( _ anObject : AnyObject ) {
if let obj = anObject as ? WarehouseItem {
if obj . isBroken {
print ( "is Broken true" )
} else {
print ( "is Broken false" )
}
om låt _ = ett objekt som ? Lager {
print ( "Bra lager" )
}
}
}
}
klass PriceCheckerVisitor : BasicVisitor {
func besök ( _ anObject : AnyObject ) {
if let obj = anObject as ? WarehouseItem {
print ( " \( obj . name ) | Pris: \( obj . price ) rub." )
}
om låt _ = ett objekt som ? Lager {
print ( "Kostnad ingen" )
}
}
}
// Använd besökare
låt lager = WarehouseImpl ()
lager . addItem ( artikel : WarehouseItemImpl ( namn : "Artikel 1" , isBroken : sant , pris : 100 ))
lager . addItem ( artikel : WarehouseItemImpl ( namn : "Artikel 2" , isBroken : false , pris : 300 ))
lager . addItem ( artikel : WarehouseItemImpl ( namn : "Artikel 3" , isBroken : false , pris : 500 ))
let price = PriceCheckerVisitor ()
let qulity = QualityCheckerVisitor ()
lager . acceptera ( besökare : pris )
lager . acceptera ( besökare : qulity )
Litteratur
- E. Gamma, R. Helm, R. Johnson, J. Vlissides . Tekniker för objektorienterad design. Design mönster. - St Petersburg. : Peter, 2001. - 368 sid. — ISBN 5-272-00355-1 .
Länkar