Simple Object Relationships

Business Objects liker å eksponere seg selv og sine relasjoner. I denne artikkelen viser jeg deg how.Simple Objekt RelationshipsIn den siste artikkelen har vi sett på hvordan den klassiske Stock, kunne bestille og kundekrav modelleres som tre separate Business Objects, hver utsette deres tilstand gjennom veldefinerte egenskaper. Selv om disse objektene kan nå brukes som den er (selv i en svært begrenset måte - vi ennå ikke har definert hvordan å gjøre sine opplysninger vedvarende), de mangler noe begrep om relasjoner. Med andre ord, hvordan kan vi vite hvilke kunde plassert en gitt rekkefølge, og som StockItem? Det er belyst på dette stadiet å ta en pause og tenke på hvordan dette ville tradisjonelt bli håndtert på en ikke-OO, data-sentriske mote. Vanligvis ville en database posten representerer Bestill utsette ytterligere to felt, en lagring av en fremmednøkkel til Kunden bordet, og en annen til StockItem tabellen. En av manglene ved denne tilnærmingen har allerede blitt demonstrert: alle database felt er utsatt for den samme grad; det er ingen begrep om ett felt blir mer "privat" enn en annen. Selvfølgelig er det mulig å uttrykke dette til en viss grad med en database visning, men på rådata nivå felt ikke kan skilles i form av synlighet eller rolle unntatt ved navn, og på den måten galskap lies.Let oss nå vurdere hvordan vi ville få tilgang til informasjon om kunden som plasserte en gitt rekkefølge i denne tradisjonelle modellen. Antar vi har fått informasjon om rekkefølgen i en slags datasettet (kanskje en TQuery), ville vi undersøke verdien av kundens fremmednøkkelen i feltet konstruere en ny spørring velge fra Kunden tabell basert på denne verdien, og deretter inspisere data returnert. Merk at dette er faktisk ganske mye kode for å skrive, som krever minst ett lokalt objekt (kunden settet) som skal bygges, og slik kode proliferates raskt hvis vi trenger å få tilgang til lagervare eller andre relaterte data. Et alternativ til denne tilnærmingen er å velge alle data i en enkelt spørring ved hjelp av en multi-table delta. Dette over mengden med kode som skal skrives, men introduserer et annet problem - den harde koding av data relasjonene i søkestrengen. I noe annet enn en triviell søknad slike uttrykk blir gjentatt i mange forskjellige steder over hele kilden - og det er verdt å merke seg at kompilatoren kan ikke gjøre noe for å hjelpe til riktigheten av slike spørsmål. Hvis du endrer databasegrunnleggende (kanskje endre navnet eller type fremmednøkkelfeltet i en tabell) så er det nødvendig å ty til program bredt søk og erstatt teknikker for å finne forekomster som kanskje trenger oppdatering. I denne situasjonen er det meget mulig at noen sjelden vist skjemaet vil bli falle gjennom nettet og vil beholde en utdatert referanse til et felt som ikke lenger eksisterer. Dette er en runtime error venter på å skje. Det er mulig å plassere slike disse søkene i en sentralisert datamodul, men det er bare geografi, og krever aktsomhet, slik at to separate deler av søknaden må du ikke forsøke å bruke den samme spørringen concurrently.Back i en ordnet verden av velkonstruert virksomhet gjenstander utsette tilsvarer en fremmednøkkel (i form av vår TObjectID) som en offentlig eiendom er bannlyst. Oppføring 1 viser hvordan dette vil se ut, og et raskt blikk viser at vi har forfalsket vår felles grensesnitt med detaljer som vi vil heller ikke utsettes. Tross alt, hvis slike egenskaper blir offentliggjort så alle kan anta at de har rett til å bruke dem. Gjør det med gjenstander
Objektorientering gir oss en langt mer naturlig måte å uttrykke disse forholdene snarere enn gjennom implisitte utenriks sentrale felt - vi kan faktisk utsette relatert objekt som en eiendom. Dette gir oss muligheten til å bruke slike konstruksjoner som Order.Customer og Order.StockItem å få tilgang til de aktuelle objektene for vår bestilling. Merk at dette er en naturlig og konsis måte å uttrykke relasjoner og fullstendig skjuler gjennomføringen detaljer. Hvis våre sekundære objektene selv utsett relasjoner (for eksempel selskapet for en kunde) deretter finne utsendelse adressen for en vare kan være så enkelt som å bruke Order.Customer.Company.Address, en ekstremt kraftig konstruksjon. Det er verdt å påpeke at hvis vi senere velger å fjerne selskapet forholdet (eller faktisk endre måten den er implementert), så vil kompilatoren registrerer hvert tilfelle gjennom hele programmet der det blir brukt på feil måte, og kompilere vil mislykkes inntil alle problemene er løst. Allerede objektorientering har hjulpet oss til å bidra til å levere en mer pålitelig program: vi kan gjøre påstander om at alle relevante endringer har vært made.Assuming vi kommer til å eksponere våre relaterte objekter som egenskaper, hvordan dette kommer til å bli gjennomført? Selvfølgelig, vi kommer til å trenge til en privat "plassholder" -feltet for å lagre en referanse til den relaterte objekt. En naiv tilnærming ville være å lage dette relatert objekt i konstruktøren og bare har den egenskapen som peker til den private feltet. Denne tilnærmingen har to hovedspørsmål - en uønsket og andre katastrofale. Den uønskede problemet er at vi kommer til å pådra seg byrden med å bygge dette relatert objekt hver gang vi konstruere den overordnede. Antar vi befolker egenskapene til den tilknyttede objektet fra vedvarende lagring, har vi påvirket runtime resultatene av vår søknad med overhead med å bygge en ny forekomst og gjør en databasetilgang. Generelt er det verdt å ta litt omsorg for å holde konstruktøren så lette som mulig. Utvide denne situasjonen, kan det ses at hvis konstruere objektet A fører til oppretting av objektet B, som i egen konstruktør fører til oppretting av samme objekt A så vi kommer til å gå inn i verden av uendelig rekursjon og det første en bruker vil se er en ut av minnet feilmelding. Denne situasjonen er ikke mer enn svært vanlig 1-1 enhet relationship.The løsning på dette problemet er en teknikk som kalles konstruere på forespørsel, eller lat konstruksjon. Dette bruker ideen om å forsinke bygging av objektet inntil like før den første gang det er nødvendig. For fremtidig tilgang til allerede instansiert objektet er rett og slett returnert. Dette forsinker noen dyre operasjoner til siste mulige øyeblikk, mens resterende usynlig for alle brukere av vår klasse. Listing 2 viser hvordan dette kan gjennomføres. For tiden blir, vil vi anta at alle våre TPDObjects tilbyr en Load metode som fyller objektegenskapene fra noen form for vedvarende lagring, gitt en relevant unikt objekt-ID. Utnytte kraften av objekter
Dette synes å løse vårt problem av brusende konstruksjon, og er en teknikk som kan brukes til stor effekt med eventuelle eiendoms (ikke bare forretningsobjekter) som er potensielt dyrt å bygge. Men la oss tenke på hva som vil skje når vi bruker denne teknikken til andre objekter. Hva vil koden for tilgangsfunksjonen av Order.StockItem ser ut, eller faktisk det generelle tilfellet av x; y? Resultatet vil se veldig lik Listing 2, erstatte nye referanser for de private felt og, viktigst, type klassen blir bygget. Snarere enn å gjenta en slik lignende kode (og risikere å ikke implementere alle slike aksessoregenskaper funksjoner identisk), er det noen måte vi kan bruke kraften til vår objekt hierarki for å redusere denne koding byrde? Ikke overraskende, er svaret et ettertrykkelig yes.What vi ønsker å gjøre er å gi noen form for nyttefunksjonen i vår base TPDObject klasse (en del av vår applikasjonsuavhengig rammeverk) som utfører samme funksjon, men kan parametreres på noen måte. De viktigste detaljene som vi trenger for å passere som parametre er plassholderen for objektet som blir konstruert, den ID som skal legges, og noen måte å konstruere riktig klasse. For å oppnå dette siste parameteren vil vi bruke en konstruksjon som kalles en klasse referanse, som er en variabel som har en bestemt type som en del av en klassehierarkiet. I Delphi, er dette definert med klassen av søkeordet. Listing 3 viser endringer i vår Work enhet for å gi slik funksjonalitet. Legg merke til at objektet forekomsten faktisk vil bli oppdatert i rutine, og dette er derfor en av de få anledninger hvor et objekt skal sendes som en VAR parameter.One annet element av notatet er at konstruktøren for TPDObject er nå definert som virtuelle. Uten denne vår generiske rutine ville ikke være i stand til å ringe et bestemt klasse konstruktør, og vil alltid konstruere klasser av typen TPDObject. Dette er den samme tilnærmingen som brukes for komponenter innenfor Delphi, og konsekvensen er den samme: alle våre konstruktører for våre TPDObject etterkommere må nå bruke overstyring
keyword.How vil våre aksessoregenskaper funksjoner på våre forretningsobjekter nå bruke denne tilgjengelig metode? Deres grensesnitt må forbli den samme, parameterless. Men vi kan gjennomføre samtalen i en eneste linje med kode som vist i Listing 4. To faktorer skal være uthevet: resultatet av samtalen må typecast til TCustomer som den returnerer en type TPDObject, selv om den faktiske forekomsten vil være av riktig type. For det andre må den private FCustomer feltet endres til å skrive TPDObject. Dette er fordi Delphi insisterer på at Var parametere er nøyaktig den forventede typen. I begge tilfeller er trygge operasjoner som detaljene er privat og vi er garantert å bli returnert en klasse av forventet type, selv om stamfar klassen har ingen kjennskap til dem. Vi er nå i stand til å gjennomføre alle slike relasjoner i en linje med kode, ved hjelp av et sentralisert applikasjonsuavhengig rutine. Fordelene med en ekte OO utvikling begynner å gjøre seg kjent. Etter å ha bygget disse relaterte forretningsobjekter, må vi sørge for at de blir ødelagt. En enkel metode er å plassere vanlige samtaler til Free i destructor: beskyttelse rundt Free vil sikre at ingenting untoward vil skje hvis objektet ikke har faktisk blitt bygget (Free på en null objekt er et tillatt drift) Siste artikkelen. problemet
Tidligere antydet jeg til det settet med klasser som ikke bør brukes i en "ekte" OO-programmet. Figur 1 fra forrige måneds kolonnen viser at grensesnittelementer ikke kan samhandle med utholdenhet laget, bare problemet domenet. Data-aware komponenter bryte denne regelen, sende endringer direkte i databasen tilkobling, og derfor (kontroversielt) ikke bør brukes. Det er mange, bedre, alternativer og disse vil undersøkes i fremtidige kolonner (((Oppføring 1 - forfalsker en klasse med egenskaper for å støtte relasjoner))) skriver TOrder = klasse (TMyAppPDObject) private FCustomerID. TObjectID; offentlig eiendom CustomerID: TObjectID lese FCustomerID; ... //Andre egenskaper følg ... ende; (((End Oppføring 1))) (((Oppføring 2 - Bruke lat konstruksjon for relaterte objekter))) skriver TOrder = klasse (TMyAppPDObject) private FCustomerID: TObjectID; FCustomer: TCustomer; funksjon GetCustomer: TCustomer; offentlig eiendom Kunde: TCustomer lese GetCustomer; ... //Andre egenskaper følge ... enden, funksjon TOrder.GetCustomer: TCustomer; begynne hvis FCustomer = null da begynne FCustomer: = TCustomer.Create; FCustomer.Load (FCustomerID); slutt; Resultat: = FCustomer; ende; (((End Oppføring 2))) (((Oppføring 3 - Generisk lat bygging av relaterte objekter))) skriver TPDClass = klasse TPDObject; TPDObject = klasse beskyttet funksjon GetObject (konst ClassType: TPDClass; Var PDObject: TPDObject; konst ID: TObjectID): TPDObject; offentlig konstruktør Opprett; virtuell; Prosedyren Load (ID: TObjectID); enden, funksjon TPDObject.GetObject (konst ClassType: TPDClass; Var PDObject: TPDObject; konst ID: TObjectID): TPDObject; begynne hvis PDObject = null da begynne PDObject: = ClassType.Create; hvis ID < > NotAssigned deretter PDObject.Load (ID); slutt; Resultat: = PDObject; end; (((End Listing 3))) (((Oppføring 4 - Vår nye TOrder.Customer tilbehør funksjon))) -funksjonen TOrder.GetCustomer: TCustomer, begynner Resultat: = TCustomer (GetObject (TCustomer, FCustomer, FCustomerID)); end; (((End Oppføring 4))) Neste i serien