Vierailija (suunnittelukuvio)

Kokeneet kirjoittajat eivät ole vielä tarkistaneet sivun nykyistä versiota, ja se voi poiketa merkittävästi 4. tammikuuta 2016 tarkistetusta versiosta . tarkastukset vaativat 26 muokkausta .
Vierailija
Vierailija
Tyyppi käyttäytymiseen
Tarkoitus muuttamatta pääluokkaa , lisää siihen uusia toimintoja.
Rakenne
Pätee tapauksissa kun on tarpeen suorittaa samanlainen (sama) toiminto useille luokille.
Plussat
  • uusia toimintoja lisätään useisiin luokkiin kerralla muuttamatta näiden luokkien koodia ;
  • antaa sinun saada tietoa kohteen tyypistä;
  • kaksinkertainen aikataulutus;
  • mahdollisuus kuvata oma algoritmi jokaiselle objektityypille .
Miinukset
  • kun vaihdat huollettua luokkaa, sinun on vaihdettava mallikoodi;
  • uusia luokkia on vaikea lisätä, koska vierailijan ja hänen poikiensa hierarkia on päivitettävä.
Kuvattu suunnittelukuvioissa Joo

Vierailija on käyttäytymismalli , joka kuvaa toimintoa, joka suoritetaan muiden luokkien  kohteille . Kun vaihdat vierailijaa, palveluluokkia ei tarvitse muuttaa .

Malli esittelee klassisen menetelmän kadonneiden tietojen palauttamiseksi turvautumatta kaksoislähetykseen alaspäin .

Ongelma ratkaistu

Sinun on suoritettava erillisiä toimintoja useille objekteille, mutta sinun on vältettävä saastumasta niiden koodia. Eikä ole mitään keinoa tai halua tiedustella kunkin solmun tyyppiä ja heittää osoitinta oikeaan tyyppiin ennen halutun toiminnon suorittamista.

Haaste

Jokaiselle jonkin rakenteen objektille suoritetaan yksi tai useampi toiminto. Sinun on määritettävä uusi operaatio muuttamatta objektiluokkia.

Ratkaisu

Itsenäisyyttä varten vierailijalla on erillinen hierarkia. Rakenteilla on tietty vuorovaikutusrajapinta.

Käyttö

Jos on mahdollista, että huolletun luokan hierarkia muuttuu tai se on epävakaa tai julkinen käyttöliittymä on riittävän tehokas mallille pääsyyn, sen käyttö on haitallista.

Perusluokka luodaan Visitormenetelmillä visit()jokaiselle ylätason alaluokalle Element. Lisää menetelmä accept(visitor)elementtihierarkiaan. ElementJohda Visitorluokka jokaiselle kohteelle suoritettavalle toiminnolle . Metoditoteutuksissa visit()on käytettävä luokan julkista käyttöliittymää Element. Tuloksena: asiakkaat luovat objekteja Visitorja välittävät ne jokaiselle objektille Elementkutsumalla accept().

Suositukset

Mallia tulee käyttää, jos:

Edut ja haitat

Edut :

Haitat :

Toteutus

  1. Lisää menetelmä accept(Visitor)elementtihierarkiaan.
  2. Luo perusluokka Visitorja määritä menetelmät visit()kullekin elementtityypille.
  3. Luo johdetut luokat Visitorjokaiselle elementeille suoritettavalle toiminnolle.
  4. Asiakas luo objektin Visitorja välittää sen kutsutulle menetelmälleaccept().

C++

Toteutusesimerkki C++ :ssa #include <iostream> #sisällytä <merkkijono> luokka Foo ; luokka Baari ; luokka Bas ; luokan vierailija { julkinen : virtuaalinen tyhjäkäynti ( Foo & ref ) = 0 ; _ virtuaalinen tyhjäkäynti ( Bar & ref ) = 0 ; _ virtuaalinen tyhjäkäynti ( Baz & ref ) = 0 ; _ virtuaalinen ~ Vierailija () = oletus ; }; class element { julkinen : virtuaalinen void hyväksyä ( Vierailija & v ) = 0 ; virtuaalinen ~ Elementti () = oletus ; }; luokka Foo : julkinen elementti { julkinen : void hyväksy ( Vierailija & v ) ohitus { v . vierailla ( * tämä ); } }; luokkapalkki : julkinen elementti { _ julkinen : void hyväksy ( Vierailija & v ) ohitus { v . vierailla ( * tämä ); } }; luokka Baz : julkinen elementti { julkinen : void hyväksy ( Vierailija & v ) ohitus { v . vierailla ( * tämä ); } }; luokka GetType : julkinen Vierailija { julkinen : std :: merkkijonoarvo ; _ julkinen : void visit ( Foo & ref ) override { arvo = "foo" ; } void visit ( Bar & ref ) override { arvo = "palkki" ; } void visit ( Baz & ref ) override { arvo = "perus" ; } }; int main () { Foo foo ; baari baari ; baz baz ; Elementti * elementit [] = { & foo , & bar , & baz }; for ( auto elem : elements ) { GetType vierailija ; elem -> hyväksy ( vierailija ); std :: cout << vierailija . arvo << std :: endl ; } paluu 0 ; }

Java

Java- toteutusesimerkki public class Demo { public static void main ( String [ ] args ) { Piste p = new Point2d ( 1 , 2 ); Vierailija v = uusi Chebyshev (); p . hyväksy ( v ); Järjestelmä . ulos . println ( s . getMetric () ); } } käyttöliittymä Vierailija { public void visit ( Point2d p ); julkinen mitätön vierailu ( Point3d p ); } abstrakti luokka Piste { julkinen abstrakti void hyväksyä ( Vierailija v ); yksityinen kaksoismetriikka = -1 ; _ _ public double getMetric ( ) { paluumittari ; } public void setMetric ( kaksoismetriikka ) { this . _ metriikka = metriikka ; } } luokka Piste2d laajentaa pistettä { public Piste2d ( double x , double y ) { this . x = x ; tämä . y = y_ _ } public void hyväksyä ( Vierailija v ) { v . vierailla ( tämä ); } yksityinen double x ; public double getX () { return x ; } yksityinen double y ; public double getY () { return y ; } } luokka Piste3d laajentaa pistettä { public Piste3d ( double x , double y , double z ) { this . x = x ; tämä . y = y_ _ tämä . z = z _ } public void hyväksyä ( Vierailija v ) { v . vierailla ( tämä ); } yksityinen double x ; public double getX () { return x ; } yksityinen double y ; public double getY () { return y ; } yksityinen kaksinkertainen z ; julkinen double getZ () { return z ; } } luokka Euclid toteuttaa Visitor { public void visit ( Point2d p ) { p . setMetric ( Math . sqrt ( p . getX () * p . getX () + p . getY ( ) * p . getY ( ) ) ); } julkinen mitätön vierailu ( Point3d p ) { p . setMetric ( Math . sqrt ( p . getX () * p . getX () + p . getY ( ) * p . getY ( ) + p . getZ () * p . getZ () ) ); } } luokka Chebyshev toteuttaa Vierailija { julkinen void käynti ( Point2d p ) { double ax = matematiikka . abs ( p.getX ( ) ) ; double -ay = Matematiikka . abs ( s . getY () ); p . setMetric ( ax > ay ? ax : ay ); } julkinen void -käynti ( Piste3d p ) { kaksoiskirves = Math . _ abs ( p.getX ( ) ) ; double -ay = Matematiikka . abs ( s . getY () ); double az = Matematiikka . abs ( s . getZ () ); double max = ax > ay ? kirves : ay ; if ( max < az ) max = az ; p . setMetric ( max ); } }

C#

Toteutusesimerkki C# :ssa julkinen staattinen luokka Demo { yksityinen staattinen void Pää () { Piste p = uusi Piste2D ( 1 , 2 ); IVisitor v = uusi Chebyshev (); p . hyväksy ( v ); konsoli . WriteLine ( s . Metric ); } } sisäinen käyttöliittymä IVisitor { void Visit ( Point2D p ); void Visit ( Point3Dp ) ; } sisäinen abstrakti luokka Piste { julkinen double Metric { get ; asettaa ; } = -1 ; _ julkinen abstrakti void Hyväksy ( IVisitor vierailija ); } sisäinen luokka Piste2D : Piste { public Piste2D ( double x , double y ) { X = x ; Y = y_ _ } public double X { get ; } julkinen double Y { get ; } public override void Hyväksy ( IVisitor vierailija ) { vierailija . vierailla ( tämä ); } } sisäinen luokka Piste3D : Piste { public Point3D ( double x , double y , double z ) { X = x ; Y = y_ _ Z = z _ } public double X { get ; } julkinen double Y { get ; } julkinen double Z { get ; } public override void Hyväksy ( IVisitor vierailija ) { vierailija . vierailla ( tämä ); } } sisäinen luokka Euclid : IVisitor { public void Vierailu ( Point2D p ) { p . Metric = matematiikka . Sqrt ( p . X * p . X + p . Y * p . Y ); } public void Vierailu ( Point3D p ) { p . Metric = matematiikka . Sqrt ( p . X * p . X + p . Y * p . Y + p . Z * p . Z ); } } sisäinen luokka Chebyshev : IVisitor { public void Vierailu ( Piste2D p ) { var ax = Math . abs ( s . X ); varay = matematiikka . _ Abs ( s . Y ); p . Metrinen = ax > ay ? kirves : ay ; } public void Vierailu ( Piste3D p ) { var ax = Math . abs ( s . X ); varay = matematiikka . _ Abs ( s . Y ); var az = Math . Abs ( s . Z ); varmax = ax > ay ? _ kirves : ay ; if ( max < az ) max = az ; p . Metrinen = max ; } }

PHP

Esimerkki toteutuksesta php :ssä <?php käyttöliittymä Vierailija { julkinen toimintokäynti ( Point $point ) ; } abstrakti class Piste { julkinen abstrakti funktio hyväksyä ( Vierailija $vierailija ); yksityinen $_metriikka = - 1 ; public function getMetric () { return $this -> _metric ; } julkinen funktio setMetric ( $metriikka ) { $this -> _metric = $metriikka ; } } luokka Piste2d laajentaa pistettä { julkinen funktio __konstrukti ( $x , $y ) { $this -> _x = $x ; $this -> _y = $y ; } public function hyväksyä ( Vierailija $vierailija ) { $vierailija -> vierailu ( $this ); } yksityinen $_x ; julkinen funktio getX () { return $this -> _x ; } yksityinen $_y ; julkinen funktio getY () { return $this -> _y ; } } luokka Piste3d laajentaa pisteen { julkinen funktio __konstrukti ( $x , $y , $z ) { $this -> _x = $x ; $this -> _y = $y ; $this -> _z = $z ; } public function hyväksyä ( Vierailija $vierailija ) { $vierailija -> vierailu ( $this ); } yksityinen $_x ; julkinen funktio getX () { return $this -> _x ; } yksityinen $_y ; julkinen funktio getY () { return $this -> _y ; } yksityinen $_z ; julkinen funktio getZ () { return $this -> _z ; } } luokka Euclid toteuttaa Vierailijan { julkinen toimintokäynti ( Piste $p ) { 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 () ) ); } } luokka Chebyshev toteuttaa Vierailija { julkinen toiminto vierailu ( Piste $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 aloitus (){ $p = new Piste2d ( 1 , 2 ); $v = uusi Chebyshev ( ); $p -> hyväksy ( $v ); echo ( $p -> getMetric () ); }; aloita ();

Python

Toteutusesimerkki Pythonissa from abc import ABCMeta , abstrakti menetelmä kirjoittamisesta Import List luokan vakooja ( metaclass = ABCMeta ): """ Vakoojavierailija """ @abstractmethod def visit_military_base ( self , military_base : 'MilitaryBase' ) -> Ei mitään : """ Vieraile laivaston sotilastukikohdassa "" " @abstractmethod def visit_headquarters ( self , headquarters : 'Headquarters' ) -> Ei mitään : """ Vieraile armeijan esikunnassa """ -passi luokka MilitaryFacility ( metaclass = ABCMeta ): """ Sotilaslaitos - vierailtu laitos """ @abstractmethod def hyväksy ( itse , vakooja : vakooja ) -> Ei mitään : """ Hyväksy vakoojavierailija """ pass luokan sotilastukikohta ( MilitaryFacility ): """ Sukellusveneen sotilastukikohta """ def __init__ ( itse ) -> Ei mitään : itse . _secret_draftings = 1 itse . _ydinsukellusveneet = 1 def __repr__ ( self ) -> str : return 'Sotilasalueella on {} ydinsukellusvenettä ja {} salaisia ​​suunnitelmia' . muoto ( itse . _ydinsukellusveneet , itse . _salaiset_luonnokset ) def hyväksy ( itse , vakooja : Vakooja ) -> Ei mitään : vakooja . visit_military_base ( itse ) def remove_secret_draftings ( itse ) -> Ei mitään : jos itse . _secret_draftings : itse . _salaiset_luonnokset -= 1 def remove_nuclear_submarine ( itse ) -> Ei mitään : jos itse . _nuclear_submarines : itse . _ydinsukellusveneet -= 1 @property def is_combat_ready ( itse ) -> bool : palauttaa itsensä . _ydinsukellusveneet > 0 luokan päämaja ( MilitaryFacility ): """ Armeijan esikunta """ def __init__ ( itse ) -> Ei mitään : itse . _generals = 3 itse . _secret_documents = 2 def __repr__ ( itse ) -> str : return ' Päämajassa on {} kenraalia ja {} salaista asiakirjaa ' . muoto ( itse . _generals , self . _secret_documents ) def hyväksy ( itse , vakooja : Vakooja ) -> Ei mitään : vakooja . visit_headquarters ( itse ) def remove_general ( itse ) -> Ei mitään : jos itse . _kenraalit : itse . _kenraalit -= 1 def remove_secret_documents ( itse ) -> Ei mitään : jos itse . _secret_documents : itse . _salaiset_asiakirjat -= 1 @property def is_command_ready ( itse ) -> bool : palauttaa itsensä . _yleiset > 0 luokka ScoutSpy ( Spy ): """ Scout (konkreettinen vakooja) """ def __init__ ( itse ): itse . _collected_info = {} # Täällä tiedämme jo tietyn objektityypin def visit_military_base ( self , military_base : MilitaryBase ) -> None : self . _collected_info [ 'base' ] = 'Sotilastukikohta: \n\t {} \n\t Valmis: {} ' . muoto ( str ( sotilaallinen_tukikohta ), 'Kyllä' , jos sotilaallinen_tukikohta . is_combat_ready muuten 'Ei' ) def visit_headquarters ( itse , pääkonttori : Headquarters ) -> Ei mitään : itse . _collected_info [ 'headquarters' ] = 'Päämaja: \n\t {} \n\t Komento: {} ' . muoto ( str ( headquarters ), 'Käynnissä' , jos päämaja . is_command_ready else 'Ei käytössä' ) def report ( self ) -> str : return 'Tietoja tiedustelulta: \n {} \n ' . muoto ( ' \n ' . join ( itse . _collected_info . arvot ( ) ) luokan JamesBond ( Spy ): """ James Bond (toinen tietty vakooja) """ def visit_military_base ( itse , sotilastukikohta : MilitaryBase ) -> Ei mitään : # James Bond vierailee sotilastukikohdassa military_base . remove_secret_draftings () # varastaa armeijan tukikohdan salaiset piirustukset . remove_nuclear_submarine () # ja lopulta räjäyttää ydinsukellusveneen def visit_headquarters ( itse , pääkonttori : Headquarters ) -> Ei mitään : # James Bond vierailee päämajassa . remove_general () # ... päämaja . remove_general () # ... päämaja . remove_secret_documents () # ... pääkonttori . remove_general () # Tuhoaa kaikki kenraalit peräkkäin päämaja . remove_secret_documents () # ja varastaa kaikki salaiset asiakirjat if __name__ == '__main__' : tukikohta = Sotilastukikohta () hq = Esikunta () # Riippumatta siitä mitkä MilitaryFacility -tilat = [ base , hq ] # type: List[MilitaryFacility] scout = ScoutSpy () print ( ' Sending a scout... \n ' ) for f tiloissa : f . hyväksyä ( tiedustelija ) tulosta ( scout.report ( ) ) print ( 'Sending Bond on a mission... \n ' ) spy = JamesBond ( ) for f tiloissa : f . hyväksyä ( vakooja ) print ( 'Lähetetään partio päivittääkseen tietoja... \n ' ) for f in tiloissa : f . hyväksyä ( tiedustelija ) tulosta ( scout.report ( ) ) """ OUTPUT: Lähetetään tiedustelija... Tietoa tiedustelulta: Keskusesikunta: Esikunnassa on 3 kenraalia ja 2 salaista asiakirjaa Komento: Toimiva Sotilastukikohta: Sotilastukikohdassa on 1 ydinsukellusvene ja 1 salainen piirros Taisteluvalmius: Kyllä Bondin lähettäminen lähetystyöhön... Lähetetään tiedustelijaa päivittämään tiedot... Tietoja tiedustelulta: Keskusesikunta: Esikunnassa on 0 kenraalia ja 0 salaista asiakirjaa Komento: Ei toimi Sotilastukikohta: Sotilastukikohdassa on 0 ydinsukellusvenettä ja 0 salaisia ​​piirustuksia Valmius: Ei yhtään """

Delphi

Toteutusesimerkki Delphissä ohjelma Demo ; tyyppi Point2D = luokka ; Piste3D = luokka ; IVisitor = käyttöliittymämenettely Visit ( p : Point2D ) ; _ ylikuormitus ; menettely Vierailu ( p : Point3D ) ; ylikuormitus ; loppu ; Piste = luokka yksityinen FMetric : Double ; julkinen omaisuus Metric : Kaksoislue FMetric kirjoittaa FMetric ; _ menettely Hyväksy ( vierailija : IVisitor ) ; virtuaalinen ; abstrakti ; loppu ; Piste2D = luokka ( Piste ) yksityinen FX : Double ; FY : Double ; julkinen omaisuus X : Double read FX ; ominaisuus Y : Kaksoislue FY ; _ rakentaja Luo ( const x , y : Double ) ; menettely Hyväksy ( Vierailija : IVisitor ) ; ohittaa ; loppu ; Point3D = luokka ( Piste ) yksityinen FX : Double ; FY : Double ; FZ : Double ; julkinen omaisuus X : Double read FX ; ominaisuus Y : Kaksoislue FY ; _ ominaisuus Z : Double read FZ ; konstruktori Luo ( const x , y , z : Double ) ; menettely Hyväksy ( Vierailija : IVisitor ) ; ohittaa ; loppu ; Euklid = luokka ( TInterfacedObject , IVisitor ) julkinen menettely Visit ( p : Point2D ) ; ylikuormitus ; menettely Vierailu ( p : Point3D ) ; ylikuormitus ; loppu ; Chebyshev = luokka ( TInterfacedObject , IVisitor ) julkinen menettely Visit ( p : Point2D ) ; ylikuormitus ; menettely Vierailu ( p : Point3D ) ; ylikuormitus ; loppu ; {Piste2D} menettely Piste2D . Hyväksy ( Vierailija : IVisitor ) ; aloita Vierailija . Vieraile ( itse ) ; loppu ; rakentaja Point2D . Luo ( const x , y : Double ) ; alkaa FX := x ; FY := y ; loppu ; {Point3D} menettely Point3D . Hyväksy ( Vierailija : IVisitor ) ; aloita Vierailija . Vieraile ( itse ) ; loppu ; rakentaja Point3D . Luo ( const x , y , z : Double ) ; alkaa FX := x ; FY := y ; FX := z ; loppu ; { Euclid } menettely Eulid . Vierailu ( p : Point2D ) ; aloita p . Metriikka := Sqrt ( Sqr ( p . X ) + Sqr ( p . Y )) ; loppu ; menettely Eulid . Vierailu ( p : Point3D ) ; aloita p . Metriikka := Sqrt ( Sqr ( p . X ) + Sqr ( p . Y ) + Sqr ( p . Z )) ; loppu ; {Chebyshev} menettely Chebyshev . Vierailu ( p : Point2D ) ; var ax , ay : Double ; alkaa ax := Abs ( p . X ) ; ay := Abs ( p . Y ) ; jos ax > ay sitten p . Mittari := ax else p . Metrinen : = ay loppu ; menettely Chebyshev . Vierailu ( p : Point3D ) ; var ax , ay , az , max : Double ; alkaa ax := Abs ( p . X ) ; ay := Abs ( p . Y ) ; az := Abs ( p . Z ) ; jos ax > ay niin max := ax else max := ay ; jos max < az sitten max := az ; p . Mittari := max ; loppu ; varp : piste ; _ v : IVVisitor ; alkaa p := Piste2D . Luo ( 1 , 2 ) ; v := Chebyshev . luoda ; p . hyväksyä ( v ) ; WriteLn ( s . Metric : 0 : 2 ) ; v := Eulid . luoda ; p . hyväksyä ( v ) ; WriteLn ( s . Metric : 0 : 2 ) ; p . Ilmainen ; Readln ; // odota, kunnes painat Enter end .

Swift

Toteutusesimerkki Swiftissä protokolla WarehouseItem { var name : String { get set } var isBroken : Bool { get set } var price : Int { get set } } class WarehouseItemImpl : WarehouseItem { var nimi : String = "" var isBroken : Bool = false var price : Int = 0 init ( nimi : String , isBroken : Bool , hinta : Int ) { self . nimi = nimi itse . isBroken = isBroken self . hinta = hinta } } protokolla Warehouse { var items : [ WarehouseItem ] { get set } func addItem ( nimike : WarehouseItem ) func accept ( vierailija : BasicVisitor ) } class WarehouseImpl : Varasto { var items : [ WarehouseItem ] = [] func addItem ( tuote : WarehouseItem ) { kohteita . liittää ( kohde ) } func accept ( vierailija : BasicVisitor ) { kohteissa olevalle kohteelle { vierailija . _ vierailla ( kohde nimellä AnyObject ) } } } protokolla BasicVisitor { func visit ( _ anObject : AnyObject ) } class QualityCheckerVisitor : BasicVisitor { func visit ( _ anObject : AnyObject ) { if let obj = anObject as ? Varastotuote { if obj . isBroken { print ( "on Broken true" ) } else { print ( "on Broken false" ) } jos _ = anObject as ? _ Varasto { tulosta ( "Hyvä varasto" ) } } } } Class PriceCheckerVisitor : BasicVisitor { func visit ( _ anObject : AnyObject ) { if let obj = anObject as ? WarehouseItem { print ( " \( obj . name ) | Hinta: \( obj . hinta ) rub." ) } jos _ = anObject as ? _ Varasto { tulosta ( "Ei kustannuksia" ) } } } // Käytä Vierailijaa anna varasto = WarehouseImpl () varasto . addItem ( tuote : WarehouseItemImpl ( nimi : " Nimike 1 " , isBroken : true , hinta : 100 )) varasto . addItem ( tuote : WarehouseItemImpl ( nimi : " Nimike 2 " , isBroken : false , hinta : 300 )) varasto . addItem ( tuote : WarehouseItemImpl ( nimi : "Tuote 3" , isBroken : false , hinta : 500 )) anna hinta = PriceCheckerVisitor () anna qulity = QualityCheckerVisitor () varasto . hyväksy ( vierailija : hinta ) varasto . hyväksy ( vierailija : qulity )

Kirjallisuus

  • E. Gamma, R. Helm, R. Johnson, J. Vlissides . Olio-suunnittelun tekniikat. Suunnittelumalleja. - Pietari. : Peter, 2001. - 368 s. — ISBN 5-272-00355-1 .

Linkit