Implementering sett av objects

I denne artikkelen vil jeg fortsette min se på implementering sett av objects.Concise Custom ConstructorsIn min forrige artikkel jeg satt ut de brede prinsippene bak implementering sett av gjenstander med et problem domene orientert klassen til å fungere som en beholder for Business Objects seg selv . Dette som grunnlag for en utvidet data management klasse som kunne støtte navigering gjennom en database avhengig markør av noe slag. Denne måneden avslutter denne undersøkelsen ved å gi noen flere detaljer om implementering og vi ser at når søknaden uavhengig rammeverket er gitt, selve koden som kreves for en skikkelig system er veldig small.We har allerede sett den foreslåtte grensesnitt for vår klasse som håndterer settet av Business Objects, TPDList. Dette har klassiske Først Neste og IsLast metoder som delegat arbeidet et privat data management objekt, som er konstruert i henhold til behovene til listen i spørsmålet. Dette er koden for vår base TPDList konstruktør: konstruktør TPDList.Create (ListDMObject: TDMObject); begynne arvet Opprett; //Standard konstruktør DMObject: = ListDMobject; //Privat referenceend; Denne koden viser at konstruktøren forventer å bli vedtatt en data management objekt som parameter. Dette forventes å være av riktig type for å støtte listeoperasjoner for klassen i spørsmålet, og vil bli gitt av programspesifikke liste konstruktør. Når dette datastyring objektet har gått til vår liste påtar seg det fulle ansvar for det og derfor må frigjøre den i destructor. Dette bryter en vanlig god tommelfingerregel der en klasse skal bli ødelagt i omfang og i hvilken sammenheng det ble opprettet, men det tillater for noen særlig konsis koding som letter byrden ved implementering av disse klassene i en application.Listing 1 viser selve gjennomføringen av en TCustomerList klasse som har en rekke konstruktører. Som man kan se hver tilpasset konstruktør kaller rett og slett standard arvet konstruktør, dynamisk konstruere data management objektet nødvendig. Et spesielt poeng er at parametrene er gått uendret til andre konstruktøren, holde grensesnittet mellom forretningslogikk og utholdenhet klasser database-nøytral. Bortsett fra eiendommen som returnerer "dagens" problem domene objekt i listen typecast til en av en passende type, er dette hele implementering som forretningslogikk trenger for hver ny type liste nødvendig. Som man kan se, er dette en svært liten mengde med kode, som det meste av arbeidet skjer i våre programuavhengig grunnklasser. Innkapsling databasetilgang
Grunnen til at vår forretningslogikk for håndtering av disse listene er så lett er fordi de delegerer alt arbeidet til sine respektive dataadministrasjon stedene. Avhengig av den spesielle databasen som blir brukt til å vedvare objektdata, kan mengden av arbeid som kreves her varierer enormt. Heldigvis de aller fleste systemer disse dager lagre sine data i en RDBMS, eller i det minste få tilgang til data via en spørrespråk, i så fall gjennomføringen er simple.Our data management klassen vil trenge en egen markør av noe slag til databasen. Jeg antar at dette markøren (i dette tilfellet) har evnen til å utføre en SQL-spørring, og hente dataene. Denne markøren vil selvfølgelig være helt database avhengig; vanligvis jeg velger den raskeste, mest praktiske eller lettest å distribuere alternativ for en gitt database. Dette kan være en generisk API slik som ADO eller ODBC, eller mer vanlig jeg anvende en spesifikk måte, for eksempel som et grensesnitt til en klient-API, kanskje ved hjelp av et tynt omslag (slik som IBExpress for Inter tilgang). Noen kan stille spørsmål hvorfor ikke bygge databasen helt lag rundt en eksisterende database-uavhengig API som ODBC. Det er ingenting å si at dette ikke er en levedyktig løsning, men å holde grensesnittet til databasen laget helt nøytral (og objektbasert) har fordelen av å la alle slike API-er som skal brukes når det er hensiktsmessig. Selve gjennomføringen av databasen laget er selvsagt fritt til å bruke API det så velger, inkludert generiske de som ADO, ODBC eller BDE. Det bør understrekes at det å tilby vår egen database nøytralt grensesnitt for objektet utholdenhet er ikke re-oppfinne hjulet; vår grensesnittet er en svært lett wrapper (ofte implementert som fasaden mønster) rundt et mer funksjonelt API som mesteparten av arbeidet er delegert. Nøkkelen er at vår wrapper omslutter den nødvendige funksjonaliteten, og legger til rette for valg av en alternativ database API bør kravene endres eller trenger dictate.The FirstRecord metoden på vår klasse (kalles av den første metoden i TPDList) bare kansellerer alle utfører spørring i klasse og problemstillinger riktig SELECT spørring. Den NextRecord vil kartlegges på en egnet metode på databasen markøren, så vil IsLast eiendom. Som hver post i markøren er tilgjengelig, bør data management klassen være forberedt på å bruke og fylle en passende virksomhet objekt forventet av ringer TPDList. Befolkningen i denne klassen fra databasen markøren felt bør deles med befolkningen i et enkelt objekt gjennom Load-metoden, og er best oppnås ved å kalle en abstrakt metode, passerer databasen markøren og objektet som skal fylles. Denne metoden må overstyres av våre programspesifikke data styring gjenstander å faktisk oppdatere de kjente egenskapene til en bestemt virksomhet objekt fra navngitte felt i den medfølgende cursor.We har allerede uttalt at våre data management klasser vil ha konstruktører som passer de i deres problem domene liste ekvivalenter. Antar vår database støtter spørringer, alle disse konstruktørene trenger å gjøre er å definere et søkestrengen som velger de nødvendige poster fra tabellen, dynamisk bygge opp spørringen fra parametere som sendes i. For spørsmål som er gjennomført veldig ofte, eller hvor ytelsen er viktig, er det selvsagt mulig for konstruktøren å kalle en tilpasset lagret prosedyre på databasen, sende parametere gjennom til det i noen praktisk form (for eksempel erstatte objekt-ID-er for refererte objekter sendt som en parameter). Den FirstRecord metoden i klassen vår faktisk vil være ansvarlig for å initiere gjennomføring av spørringen (eller ringer en lagret prosedyre); Dette er god praksis som det holder konstruktør av en klasse lett. Oppføring 2 viser tilsvarende konstruktører for CustomerDM (data management) objekt. hierarkier ansvarsområder
jeg skulle endelig passere kommentar på klassehierarkiet involvert på data management side. Vår base TDMObject bør holdes database-uavhengig, men det må gi et grensesnitt som problemdomeneklassene kan bruke. Dette oppnås ved å tilveiebringe et antall av abstrakte metoder på basisklassen. Faktiske databaseavhengigheter er innført i tilpassede etterkommere av TDMObject (som TSQLServerDMObject eller TOracleDMObject) som gjennomfører abstrakte metoder, tilby tjenester som er nødvendige for å støtte databasetilgang mot sine mål plattform, og kan gi ytterligere abstrakte metoder som bør iverksettes av etterkommere. disse databasespesifikke etterkommere er perfekt rett til å innføre avhengigheter på noen andre klasser de trenger for å kommunisere med sine utvalgte database ved hjelp av en egnet API. Vanligvis vil de gi en rimelig mengde funksjonalitet som støtter slike spørsmål som å etablere en forbindelse til motoren, velge en bestemt database, logge inn som en navngitt bruker og så videre. Hvordan de oppnår slike behov er helt under sin egen kontroll: Husk at vår base TDMObject grensesnittet er en kontrakt utelukkende for tjenestene som kreves av TPDObject og TPDList, og ikke diktere noen detaljer som for eksempel en database kontekst. Dette betyr også at våre databasespesifikke objekter kan dra nytte av eventuelle funksjoner i den valgte databasen eller API for å maksimere brukervennlighet eller ytelse, mens du hviler lett i visshet om at en overgang til en annen database er fortsatt mulig uten omfattende endringer i hoved legeme av applikasjonskode. Faktisk, så disse klassene er programuavhengig, er det mulig for en database ekspert for å innlemme muligens høye nivåer av kompleksitet (for å hente ut maksimal ytelse fra API), mens utsette et enkelt grensesnitt for applikasjonsspesifikke brukere av disse klassene. en bieffekt av dette hierarkiet er at våre programdata styring gjenstander vil bli sterkt knyttet til den funksjonaliteten som kreves av en bestemt database lag, og derfor en endring av databasen vil kreve en re-implementering av data management stedene. Denne effekten kan bli redusert eller muligens også fjernes helt hvis alle databasespesifikke klasser er utformet med identiske tjenester; erfaring tilsier at mens denne tilnærmingen er praktisk for tilsvarende database API-tallet, og gir en helt database-uavhengig klassehierarkiet begrenser mulighetene for å dra nytte av en bestemt plattform. Det er selvsagt fullt mulig å innføre under hierarkier med identiske grensesnitt, de vanligste er et sett med databaseobjekter som alle bruker SQL som sitt primære kommunikasjonsmiddel. Dette gir seg til hierarkier som følgende, hvor de tre første klassene ligge innenfor rammen (og inkluderer mesteparten av koden), og de to siste klassene er programspesifikke: TDMObject → TSQL_DMObject → TOracleDMObject → TMyAppDMObject → TCustomerDM Siste artikkelens spørsmålet
jeg spurte hvordan vi kan bruke vår nye liste klasser for å representere objektrelasjoner som en-mange. Et klassisk eksempel på en slik forbindelse ville være Customer.Orders, som representerer det sett av ordre stadig er lagt inn av en kunde. Som med de andre 1-1 og mange-1 forhold, utsette dem som en eiendom på den relaterte klassen er en behagelig og naturlig måte å gjøre det. Det første spørsmålet er, hvordan kan de bli implementert? Svaret på dette er veldig enkel; vi vil bruke standard lat konstruksjon teknikker på vår eiendom tilbehør funksjon å bare lage en liste første gang det er nødvendig. Den faktiske listen som skal bygges vil være aktuell liste basert på forholdet vi uttrykker, i dette tilfellet ville vi ønsker å bygge listen over bestillinger for en kjent Kunden. Listing 3 viser en fullstendig implementering for en slik sammenheng. Den mest talende funksjonen er bruken av "Self" for å lage en liste over bestillinger for en bestemt kunde instance.The andre delen av spørsmålet var: vi kan håndtere slike operasjoner generelt som vi gjør med de enklere relasjoner (husker vi implementert en generisk GetObject metode i vår base TPDObject klasse)? På forsiden av det kan vi være i stand til å gjennomføre en generisk GetList metode, men ved nærmere ettersyn er dette ikke tilfelle. Grunnen til denne manglende evne til å håndtere lister generelt er at vi ønsker å ringe et bestemt konstruktør med bestemte parametre på listen nødvendig. Dette ville kreve en slags referanse til en konstruktør type og det er ingen konsis måte å uttrykke dette samtidig som parameter håndtering enkel. Så selv om vi kan håndtere enkelte objektrelasjoner generelt vi ikke kan gjøre lignende for settet objektrelasjoner, og de må hver håndteres eksplisitt som sett i Oppføring 1. Som man kan se, er mengden av koden som er involvert minimal og det er derfor ikke altfor tung . en oppgave (((Listing 1 - Custom konstruktører for TCustomerList))) konstruktør TCustomerList.CreateAll, begynner arvet Opprette (TCustomerDM.CreateAll); end; konstruktør TCustomerList.CreateByName (konst Navn: String); begynne arvet Opprette (TCustomerDM.CreateByName (navn)); end; konstruktør TCustomerList.CreateByStockOrder (Sak: TStockItem), begynner arvet Opprette (TCustomerDM.CreateByStockOrder (Element)); end; (((End Oppføring 1))) (((Oppføring 2 - matchende konstruktører for Customer data Management objekt))) konstruktør TCustomerDM.CreateAll, begynner arvet Opprett; Query: = 'SELECT * FROM Kunde ORDER BY navn'; ende, konstruktør TCustomerDM.CreateByName (konst Navn: String); begynne arvet Opprett; Query: = 'SELECT * FROM Kunde WHERE navn som "' + navn +"% "; ende, konstruktør TCustomerDM.CreateByStockOrder (Sak: TStockItem), begynner arvet Opprett; Query: = 'SELECT * FROM Kunde WHERE Itemid =' + IDToStr (Item.ID); end; (((End Oppføring 2))) (((Oppføring 3 - Implementering av en-mange relasjoner))) skriver TCustomer = klasse ( TPDObject) private FOrders: TOrderList; funksjons GetOrders: TOrderList; offentlig destructor Destroy; styre; eiendoms Ordre: TOrderList lese GetOrders; enden, destructor TCustomer.Destroy, begynner FOrders.Free; arvet, end, funksjons TCustomer.GetOrders: TOrderList; begynne hvis FOrders = nil deretter FOrders: = TOrderList.CreateByCustomer (Selv); Resultat: = FOrders, slutten; (((End Listing 3)))