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 .
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