Silta (suunnittelukuvio)

Kokeneet kirjoittajat eivät ole vielä tarkistaneet sivun nykyistä versiota, ja se voi poiketa merkittävästi 9. maaliskuuta 2016 tarkistetusta versiosta . tarkastukset vaativat 11 muokkausta .
Silta
Silta
Tyyppi rakenteellinen
Kuvattu suunnittelukuvioissa Joo

Siltakuvio on rakennesuunnittelukuvio  , jota käytetään ohjelmistosuunnittelussa " erottelemaan abstraktio ja toteutus , jotta ne voivat muuttua itsenäisesti". Siltakuvio käyttää kapselointia , yhdistämistä ja voi käyttää periytymistä jakaakseen vastuut luokkien välillä.

Tarkoitus

Kun luokka vaihtuu usein, oliokeskeisen lähestymistavan eduista tulee erittäin hyödyllisiä, jolloin voit tehdä muutoksia ohjelmaan mahdollisimman vähän tuntemalla ohjelman toteutuksesta. Siltakuvio on hyödyllinen silloin, kun paitsi itse luokka vaihtuu usein, myös sen toiminta.

Kuvaus

Kun abstraktio ja toteutus erotetaan toisistaan, ne voivat muuttua itsenäisesti. Toisin sanoen siltakuvion kautta toteutettuna rajapinnan rakenteen muuttaminen ei häiritse toteutuksen rakenteen muuttamista. Harkitse tällaista abstraktiota hahmona. Muotoja on monenlaisia, jokaisella on omat ominaisuutensa ja menetelmänsä. On kuitenkin jotain, joka yhdistää kaikki hahmot. Jokaisen muodon on esimerkiksi kyettävä piirtämään itsensä, skaalata jne. Samanaikaisesti piirustusgrafiikka voi vaihdella käyttöjärjestelmän tai grafiikkakirjaston tyypistä riippuen. Muotojen on kyettävä piirtämään itse itsensä erilaisissa graafisissa ympäristöissä, mutta ei ole käytännöllistä toteuttaa kaikkia piirtomenetelmiä jokaiseen muotoon tai muokata muotoa joka kerta, kun piirustustapa muuttuu. Tässä tapauksessa siltakuvio auttaa, jolloin voit luoda uusia luokkia, jotka toteuttavat piirtämisen erilaisissa graafisissa ympäristöissä. Tätä lähestymistapaa käyttämällä on erittäin helppoa lisätä sekä uusia muotoja että tapoja piirtää niitä.

Kaavioissa nuolen edustamalla yhteydellä voi olla 2 merkitystä: a) "laji", Liskovin substituutioperiaatteen mukaisesti ja b) yksi abstraktin mahdollisista toteutuksista. Kielet käyttävät tyypillisesti periytymistä toteuttamaan sekä a) että b), mikä pyrkii paisuttamaan luokkahierarkioita.

Silta palvelee juuri tämän ongelman ratkaisemista: objektit luodaan pareittain hierarkian A ja hierarkian B objektista, hierarkiassa A periytymisellä on Liskovin mukaan "variantti" ja käsitteelle " abstraktion toteutus” käytetään linkkiä objektista A sen parilliseen objektiin B.

Käyttö

Java AWT -arkkitehtuuri perustuu täysin tähän malliin - java.awt.xxx - hierarkia kahvojen ja sun.awt.xxx toteutusten osalta.

Esimerkkejä

C++ esimerkki

Lähdeteksti C++ :ssa #include <iostream> käyttäen nimiavaruutta std ; luokan vetolaatikko { julkinen : virtuaalinen tyhjiöpiirrosYmpyrä ( int x , int y , int säde ) = 0 ; _ }; luokka SmallCircleDrawer : julkinen vetolaatikko { julkinen : const double radius Kerroin = 0,25 ; void drawCircle ( int x , int y , int radius ) ohitus { cout << "Pienen ympyrän keskipiste " << x << ", " << y << " säde = " << säde * sädeKerroin << endl ; } }; luokka LargeCircleDrawer : julkinen vetolaatikko { julkinen : const double radius Kerroin = 10 ; void drawCircle ( int x , int y , int radius ) ohitus { cout << "Suuri ympyrän keskipiste " << x << ", " << y << " säde = " << säde * sädeKerroin << endl ; } }; luokka muoto { suojattu : Laatikko * laatikko ; julkinen : Muoto ( laatikko * drw ) { laatikko = drw ; } muoto () {} virtuaalinen tyhjiöpiirto ( ) = 0 ; virtuaalinen void enlargeSäde ( int - kerroin ) = 0 ; }; luokka Ympyrä : julkinen muoto { int x , y , säde ; julkinen : Ympyrä ( int _x , int _y , int _radius , Drawer * drw ) { laatikko = drw ; setX ( _x ); setY ( _y ); setRadius ( _radius ); } void draw () ohita { vetolaatikko -> piirräCircle ( x , y , säde ); } void enlargeSäde ( int kerroin ) ohitus { säde *= kerroin ; } void setX ( int_x ) { _ x = _x ; } void setY ( int_y ) { _ y = _y _ } void setRadius ( int _radius ) { säde = _säde ; } }; int main ( int argc , char * argv []) { Muoto * muodot [ 2 ] = { uusi ympyrä ( 5 , 10 , 10 , uusi suuri ympyrälaatikko ()), uusi ympyrä ( 20 , 30 , 100 , uusi SmallCircleDrawer ())}; for ( int i = 0 ; i < 2 ; i ++ ) { muodot [ i ] -> piirrä (); } paluu 0 ; } // Tulostus Suuri ympyrän keskipiste = 5 , 10 säde = 100 Pienen ympyrän keskipiste = 20 , 30 säde = 25,0

Java-esimerkki

Java lähde julkinen käyttöliittymä Vetolaatikko { public void drawCircle ( int x , int y , int säde ); } public class SmallCircleDrawer toteuttaa Drawer { julkinen staattinen lopullinen kaksoissädeKerroin = 0,25 ; _ @Override public void drawCircle ( int x , int y , int radius ) { System . ulos . println ( "Pienen ympyrän keskipiste = " + x + "," + y + " säde = " + säde * sädeKerroin ); } } public class LargeCircleDrawer toteuttaa Drawer { julkinen staattinen lopullinen int sädeKerroin = 10 ; @Override public void drawCircle ( int x , int y , int radius ) { System . ulos . println ( "Suuri ympyrän keskipiste = " + x + "," + y + " säde = " + säde * sädeKerroin ); } } julkinen abstrakti luokka muoto { suojattu Laatikon laatikko ; suojattu muoto ( laatikon vetolaatikko ){ tämä . laatikko = laatikko ; } julkinen abstrakti void draw (); public abstract void enlargeSäde ( int kerroin ); } public class Ympyrä laajentaa muotoa { yksityinen int x ; yksityinen int y ; yksityinen int säde ; public Circle ( int x , int y , int radius , Vetolaatikon vetolaatikko ) { super ( laatikko ); setX ( x ); setY ( y ); setRadius ( säde ); } @Ohita julkinen void arvonta () { vetolaatikko . piirräYmpyrä ( x , y , säde ); } @Override public void enlargeSäde ( int kerroin ) { säde *= kerroin ; } public int getX () { return x ; } public int getY () { return y ; } public int getRadius ( ) { paluusäde ; } public void setX ( int x ) { tämä . x = x ; } public void setY ( int y ) { this . y = y_ _ } public void setRadius ( int radius ) { this . säde = säde ; } } // Luokka, joka näyttää kuinka "Bridge"-suunnittelukuvio toimii. julkisen luokan hakemus { public static void main ( Merkkijono [] args ){ Muoto [] muodot = { uusi Ympyrä ( 5 , 10 , 10 , uusi SuuriCircleDrawer ()), uusi Ympyrä ( 20 , 30 , 100 , uusi SmallCircleDrawer ())}; for ( Muoto seuraava : muodot ) seuraava . piirtää (); } } // Tulostus Suuri ympyrän keskipiste = 5 , 10 säde = 100 Pieni ympyrän keskipiste = 20 , 30 säde = 25,0

Esimerkki C#:ssa

Lähdeteksti C# käyttäen System ; nimiavaruus silta { // MainApp-testisovellus class MainApp { staattinen void Main () { Abstraktio ab = uusi Tarkennettu abstraktio (); // Aseta toteutus ja kutsu ab . Toteuttaja = uusi ConcreteImplementorA (); ab . operaatiot (); // Muuta toteutusta ja kutsu ab . Toteuttaja = uusi ConcreteImplementorB (); ab . operaatiot (); // Odota käyttäjän konsolia . lue (); } } /// <summary> /// Abstraktio - abstraktio /// </summary> /// <remarks> /// <li> /// <lu>määritä abstraktioliittymä;</lu> /// < lu >tallentaa viittauksen objektiin <katso cref="Implementor"/></lu> /// </li> /// </remarks> class Abstraktio { // Omaisuus julkinen Toteuttaja Toteuttaja { get ; asettaa ; } public virtual void Toiminto () { Toteuttaja . operaatiot (); } } /// <summary> /// Toteutusohjelma /// </summary> /// <remarks> /// <li> /// <lu> määrittelee käyttöliittymän toteutusluokille. Sen ei tarvitse /// vastata tarkasti <see cref="Abstraction"/> luokan käyttöliittymää. Itse asiassa molemmat ///-rajapinnat voivat olla täysin erilaisia. Tyypillisesti luokan käyttöliittymä /// <katso cref="Implementor"/> edustaa vain primitiivisiä operaatioita, kun taas luokka /// <katso cref="Abstraction"/> määrittää ylemmän tason operaatiot /// näiden primitiivien perusteella; <// lu> /// </li> /// </remarks> abstract class Toteuttaja { public abstract void Operaatio (); } /// <summary> /// RefinedAbstraction /// </summary> /// <remarks> /// <li> /// <lu>laajentaa abstraktion määrittämää käyttöliittymää <katso cref="Abstraction" / ></lu> /// </li> /// </remarks> class RefinedAbstraction : Abstraktio { public override void Operation () { Toteuttaja . operaatiot (); } } /// <summary> /// ConcreteImplementor - konkreettinen toteuttaja /// </summary> /// <huomautus> /// <li> /// <lu>sisältää käyttöliittymän konkreettisen toteutuksen <katso cref="Implementor" / ></lu> /// </li> /// </remarks> class ConcreteImplementorA : Toteutusohjelma { public override void Operation () { Konsoli . WriteLine ( "ConcreteImplementorA Operation" ); } } // "ConcreteImplementorB" luokka ConcreteImplementorB : Toteutusohjelma { julkinen ohitus void Käyttö () { Konsoli . WriteLine ( "ConcreteImplementorB-toiminto" ); } } }

PHP5 esimerkki

PHP5 lähdekoodi käyttöliittymä IPprinter { julkinen toiminto printHeader ( $textHeader ); julkinen toiminto printBody ( $textBody ); } class PdfPrinter toteuttaa IPrinterin { public function printHeader ( $textHeader ) { echo 'Tämä on otsikkosi (' . $textHeader . ') pdf-tiedostossa<br>' ; } public function printBody ( $textBody ) { echo 'Tämä on tekstikappaleesi (' . $textBody . ') pdf-tiedostossa<br>' ; } } class ExcelPrinter toteuttaa IPrinterin { public function printHeader ( $textHeader ) { echo 'Tämä on otsikkosi (' . $textHeader . ') xls-tiedostossa<br>' ; } public function printBody ( $textBody ) { echo 'Tämä on tekstikappaleesi (' . $textBody . ') xls-tiedostossa<br>' ; } } abstrakti luokka Raportti { suojattu $tulostin ; public function __construct ( IPprinter $tulostin ) { $this -> tulostin = $tulostin ; } public function printHeader ( $textHeader ) { $this -> printer -> printHeader ( $textHeader ); } public function printBody ( $textBody ) { $this -> printer -> printBody ( $textBody ); } } class WeeklyReport laajentaa Raportti { julkinen funktio tulosta ( array $teksti ) { $this -> printHeader ( $teksti [ 'otsikko' ]); $this -> printBody ( $teksti [ 'body' ]); } } $raportti = uusi viikkoraportti ( uusi ExcelPrinter ()); $raportti -> print ([ 'header' => 'oma otsikko excelille' , 'body' => 'oma runko excelille' ]); // Tämä on otsikkosi (excel-otsikoni) xls-tiedostossa</ br>Tämä on tekstitekstisi (excelin tekstiosani) xls-tiedostossa<br> $report = new WeeklyReport ( uusi PdfPrinter ()); $report -> print ([ 'header' => 'oma otsikko pdf:lle' , 'body' => 'oma body for pdf' ]); // Tämä on otsikkosi (pdf-tiedoston otsikko) pdf-tiedostossa</br>Tämä on tekstisi (pdf-tiedoston tekstiosa) pdf-tiedostossa<br>

PHP5.4 esimerkki

Lähdeteksti PHP5.4 :ssä ominaisuus TData { yksityinen $data ; julkinen funktio __konstrukti ( array $data ) { $this -> data = $data ; $this -> valmistaa (); } abstrakti suojattu funktio valmistaa (); } ominaisuus TShow { yksityinen $sisältö ; public function show () { tulosta $this -> content ; } } class XmlFormat { käytä TData , TShow ; suojattu toiminto valmistaa () { $this -> content = '<?xml version="1.1" encoding="UTF-8" ?><root>' ; foreach ( $this -> data as $name => $item ) { $this -> content .= "< $nimi > $tuote </ $name >" ; } $this -> content .= '</root>' ; } } class JsonFormat { käytä TData , TShow ; suojattu toiminto valmistaa () { $this -> content = json_encode ( $this -> data ); } } class SelfFormat { käytä TData , TShow ; suojattu funktio valmistaa () { $sisältö = array (); foreach ( $this -> data muodossa $name => $item ) { $string = '' ; if ( on_merkkijono ( $nimi )) { $nLen = strlen ( $nimi ); $string .= "[nimi|merkkijono( { $nLen } ){ { $name } }:val|" ; } if ( is_int ( $nimi )) { $string .= "[index|int{ { $name } }:val|" ; } if ( is_string ( $item )) { $vLen = strlen ( $item ); $string .= "merkkijono( $vLen ){ { $item } " ; } if ( is_int ( $item )) { $string .= "int{ { $item } " ; } $merkkijono .= "}]" ; array_push ( $sisältö , $merkkijono ); } $this -> content = 'selfMadeDataFormat:Array(' . count ( $this -> data ) . '):' ; $this -> content .= implode ( ',' , $content ); $this -> content .= ':endSelfMadeDataFormat' ; } } $xml = new XmlFormat ( array ( 'a' => 'b' , 'c' )); $json = new JsonFormat ( array ( 'a' => 'b' , 'c' )); $self = new SelfFormat ( array ( 'a' => 'b' , 'c' )); $itse -> näytä (); /* selfMadeDataFormat:Array(2):[nimi|merkkijono(1){a}:val|string(1){b}],[index|int{0}:val|string(1){c}]: endSelfMadeDataFormat */ $xml -> näytä (); /* <?xml version="1.1" encoding="UTF-8" ?><root><a>b</a><0>c</0></root> */ $json -> näytä ( ); /* {"a":"b","0":"c"} */

CoffeeScript-esimerkki

Lähdeteksti CoffeeScript -kielellä # Toteuttajaluokka Istorage get : (avain) -> set : (avain, arvo) -> # ConcreteImplementor - luokan IFlashStorage laajentaa IStoragea # ... # ConcreteImplementor - luokka IJavaStorage laajentaa IStoragea # ... # ConcreteImplementor - luokka ISessionStorage laajentaa IStoragea # ... # ConcreteImplementor - luokka ICookieStorage laajentaa IStoragea # ... # ConcreteImplementor - luokka IGhostStorage laajentaa IStoragea # ... # Abstraktioluokka Astorage # suojattu _implementer : if sessionStorage uusi ISessionStorage else if navigator . plugins [ "Shockwave Flash" ] uusi IFlashStorage else if navigator . javaEnabled () uusi IJavaStorage else if navigator . cookieKäytössä uusi ICookieStorage muu uusi IGhostStorage # julkinen lataus : (avain) -> unohdin : (avain) -> tallenna : (avain, arvo) -> # RefinedAbstraction- luokka InfoStorage laajentaa ATallennuskuormaa : (avain) -> @_implementer . get ( "Info: #{ key } " ) tallenna : (avain, arvo) -> @_implementer . set ( "Info: #{ key } " , arvo ) unohdin : (avain) -> @_implementer . set ( "Info: #{ key } " , null )

JavaScript-esimerkki

JavaScript lähdekoodi // Toteuttaja ("rajapinta") -toiminto Toteutaja () { this . operaatio = funktio () {}; } // ConcreteImplementor (Implementation Implementor) -funktio ConcreteImplementorA () { this . toiminta = function () { hälytys ( "ConcreteImplementorA.operation" ); }; } ConcreteImplementorA . prototyyppi = Objekti . luoda ( Toteuttaja . prototyyppi ); Concrete ImplementorA . prototyyppi . konstruktori = ConcreteImplementorA ; function ConcreteImplementorB () { this . toiminta = function () { hälytys ( "ConcreteImplementorB.operation" ); }; } ConcreteImplementorB . prototyyppi = Objekti . luoda ( Toteuttaja . prototyyppi ); Concrete ImplementorB . prototyyppi . konstruktori = ConcreteImplementorB ; // Abstraktiofunktio Abstraktio ( ) { var implementor ; tämä . getImplementor = funktio () { // toteuttajan käyttäminen RefinedAbstraction- palautustoteuttimesta ; }; tämä . setImplementor = funktio ( val ) { toteuttaja = val ; }; tämä . operaatio = toiminto () { toteuttaja . operaatio (); }; } // RefinedAbstraction- funktio RefinedAbstraction () { var abstr = new Abstraction (); tämä . setImplementor = function ( val ) { abstr . setImplementor ( val ); }; tämä . operaatio = function () { abstr . operaatio (); }; } // käyttö: var refAbstr = new RefinedAbstraction (); refAbstr . setImplementor ( uusi ConcreteImplementorA () ); refAbstr . operaatio (); // "ConcreteImplementorA. operation" refAbstr . setImplementor ( uusi ConcreteImplementorB () ); refAbstr . operaatio (); // "ConcreteImplementorB. operation"

Ilman tarvetta ylikuormittaa abstraktiomenetelmiä, RefinedAbstraction voidaan yksinkertaistaa huomattavasti:

function RefinedAbstraction () { Abstraktio . soita ( tämä ); }

Voit myös tallentaa viittauksia ylikuormitettuihin menetelmiin heti abstraktion instantoimisen jälkeen:

function RefinedAbstraction () { Abstraktio . soita ( tämä ); var abstr_setImplementor = tämä . setImplementor ; tämä . setImplementor = funktio ( val ) { abstr_setImplementor ( val ); }; }

VB.NET esimerkki

Lähdeteksti VB.NET -kielellä nimitilan silta ' Ohjelma - Testisovellusluokka Ohjelman jaettu alapää ( ) Dim AB Abstraktiona = Uusi jalostettu abstraktio ( ) ' Asenna toteutus ja soita AB . Toteutuslaite = New ConcreteImplementorA () AB . operaatio () ' Asenna toteutus ja soita AB . Toteutuslaite = New ConcreteImplementorB () AB . operaatio () Odota käyttäjän toimia Konsoli . Lue () Lopeta Sub End Class '''' <summary> '''' Abstraktio - abstraktio ''' </summary> ''' <huomautukset> ''' <li> ''' <lu>määritä abstraktioliittymä;</lu> ''' < lu >tallentaa viittauksen objektiin <katso cref="Implementor"/></lu> ' '' </li> ''' </remarks> Luokka Abstraction Protected m_implementor toteuttajana ' Julkisen omaisuuden toteuttaja ( ) Toteuttajana Hanki palautus m_implementor End Get Set ( ByVal value As Implementor ) m_implementor = arvo End Set End Property Julkinen ohitettava alitoiminto ( ) m_implementor . Toiminto ( ) Loppuosa loppuluokka _ '''' <summary> '''' Toteutusohjelma ''' </summary> ''' <remarks> ''' <li> ''' <lu> määrittää käyttöliittymän toteutusluokille. Sen ei tarvitse täsmälleen vastata luokan käyttöliittymää <katso cref="Abstraction"/>. Itse asiassa molemmat '''-rajapinnat voivat olla täysin erilaisia. Yleensä luokan käyttöliittymä ''' <see cref="Implementor"/> edustaa vain primitiivisiä operaatioita, ja luokka ''' <see cref="Abstraction"/> määrittää ylemmän tason operaatiot näiden primitiivien perusteella ''';< / lu> '''' </li> ''' </ remarks > MustInherit Class Implementor Public MustOverride Sub Operation () Loppuluokka '''' <summary> ''' RefinedAbstraction - tarkennettu abstraktio ''' </summary> ''' <remarks> ''' <li> ''' <lu> laajentaa abstraktion määrittelemää käyttöliittymää <katso cref= "Abstraktio" /></lu> ''' </li> ''' </remarks> Luokka RefinedAbstraction perii abstraktion Julkinen ohittaa alitoiminnon ( ) toteuttaja . Toiminto ( ) Loppuosa loppuluokka _ '''' <summary> '''' ConcreteImplementor - konkreettinen toteuttaja ''' </summary> ''' <remarks> ''' <li> ''' <lu>sisältää käyttöliittymän konkreettisen toteutuksen <katso cref= "Implementor"/ ></lu> ''' </li> ''' </remarks> Luokka ConcreteImplementorA perii toteuttajan Julkinen ohittaa alitoiminnot ( ) Konsoli . WriteLine ( "ConcreteImplementorA Operation" ) End Sub End Class ' "ConcreteImplementorB" Luokka ConcreteImplementorB perii Implementorin Julkinen ohittaa alitoiminnot ( ) Konsoli . WriteLine ( "ConcreteImplementorB-toiminto" ) End Sub End Class End Namespace

Python-esimerkki

Lähdekoodi Pythonissa # Toteutusluokka DrawingAPI : def drawCircle ( itse , x , y , säde ) : hyväksy # ConcreteImplementor 1/2 luokka DrawingAPI1 ( DrawingAPI ): def drawCircle ( self , x , y , radius ): tulosta "API1.circle at %f : %f radius %f " % ( x , y , radius ) # ConcreteImplementor 2/2 luokka DrawingAPI2 ( DrawingAPI ): def drawCircle ( self , x , y , radius ): tulosta "API2.circle at %f : %f radius %f " % ( x , y , radius ) # Abstraktioluokka Muoto : # Matalan tason def piirto ( itse ) : läpäistävä # Korkean tason def resizeByPercentage ( itse , pct ): läpäissyt # Tarkennettu abstraktioluokka CircleShape ( Shape ): def __init__ ( self , x , y , radius , drawingAPI ) : itse . __x = x itse . __y = y itse . __radius = säde itse . __drawingAPI = piirustusAPI # matalan tason eli Toteutuskohtainen def draw ( itse ): itse . __drawingAPI . PiirräCircle ( itse . __x , itse . __y , itse . __säde ) # korkean tason eli abstraktiokohtainen def resizeByPercentage ( itse , pct ): itse . __säde *= pct def main (): muodot = [ CircleShape ( 1 , 2 , 3 , DrawingAPI1 ()), CircleShape ( 5 , 7 , 11 , DrawingAPI2 ()) ] muodoille : muoto . _ _ _ resizeByPercentage ( 2.5 ) muoto . piirtää () if __name__ == "__main__" : main ()

Kirjallisuus

  • E. Gamma, R. Helm, R. Johnson, J. Vlissides . Olio-suunnittelun tekniikat. Suunnittelumallit = Suunnittelumallit: Uudelleenkäytettävän olio-ohjelmiston elementit. - Pietari. : " Peter ", 2007. - S. 366. - ISBN 978-5-469-01136-1 . (myös ISBN 5-272-00355-1 )

Linkit