Strukturering av programmer Jfr. læreboka kapittel 4 og 5 Kontrollstrukturer Algoritmer i imperative språk kan bygges opp av de tre grunnleggende programstrukturene Kontrollstrukturer o Sekvens Unntak (Exceptions) o Valg Event-driven computing o Løkke (iterasjon) Modularisering o Skille grensesnitt og implementasjon o Separat og uavhengig kompilering o Modulmekanismer i ulike språk Alle imperative språk tilbyr derfor disse strukturene, men med litt ulik syntaks o if then else, endif elseif fi o for, while, repeat, do, dowhile Se læreboka side 179 186 for detaljer IN211-Strukturereprogram-1 IN211-Strukturereprogram-2 Dijkstras Guarded commands if <Boolsk uttrykk> -> <setning> [ ] <Boolsk uttrykk> -> <setning> [ ] <Boolsk uttrykk> -> <setning> fi Hvis en eller flere av de Boolske uttrykkene er sanne, utføres en av de tilsvarende setningene ikke-deterministisk Hvis ingen av de Boolske uttrykkene er sanne, avsluttes programmet med en kjøretidsfeil Tilsvarende konstruksjon finnes for løkker Hensikten med konstruksjonene er å kunne skrive programmer som ikke er overspesifiserte og som det er lett å bevise er riktige. Eksempel: Finn det største av to tall: if x >= y -> max := x [ ] y >= x -> max := y fi Rutiner Rutiner gjør det mulig å bryte ned et program i mindre enheter Kallende rutine kaller en rutine ved navn, den kalte rutinen returnerer dit den ble kalt fra. I de fleste språk finnes rutiner finnes i to varianter o Prosedyrer, som ikke returnerer noe resultat, men gir tilstandsendringer som utskrift, endring av globale variable, endring av objekttilstand (kan bare endre eksterne verdier gjennom sideeffekter). Kall på prosedyrer brukes som selvstendige setninger. o Funksjoner, som returnerer en verdi, og som derfor kan brukes i uttrykk Sideeffekt (def): Endring av ikke-lokale variabler Språkutformingsspørsmål: Skal funksjoner tillates å ha sideeffekter? IN211-Strukturereprogram-3 IN211-Strukturereprogram-4
Sideeffekter et tveegget sverd Korutiner Effektivt for å kommunisere med rutinens omgivelser Korutiner er rutiner som samvirker med hverandre på like fot. men Typisk oppsett: o Et overordnet program starter opp alle korutinene. For å studere virkningen av rutinen, er det ikke nok å se på signaturen, vi må studere hele rutinen. Programmer med sideeffekter kan derfor være meget vanskelige å lese. o Hver korutine initialiserer seg selv og returnerer til det overordnede programmet. o Det overordnede programmet restarter en av korutinene med resume. o Deretter restarter korutinene hverandre med resume. Allment akseptert at det er uheldig at funksjoner har sideeffekter, og mange programmeringsspråk forbyr det. o Kjøringen avsluttes når en av korutinene utfører sin avsluttende kode. Korutinene utføres i kvasiparallell bare en korutine er aktiv ad gangen. Korutiner er velegnet for simuleringer eksempelvis spill. Korutiner er implementert i SIMULA 67. IN211-Strukturereprogram-5 IN211-Strukturereprogram-6 Exceptions - unntak Språk uten unntakshåndtering Hensikten med unntakshåndtering er at programmet skal gjenspeile det normale programmet skal ikke bli overkomplisert på grunn av alle mulige unntak Hvis man programmerer i et språk som ikke tilbyr unntakshåndtering, må programmereren allikevel forsøke å forutse alle eventualiteter og håndtere mulige unntak Definisjoner: Alternativer: o Unntak ( exception ): En uvanlig, unormal hendelse, feilaktig eller ikke, som kan fanges opp av maskin- eller programvare, og som kan kreve spesiell prosessering o Sende med en ekstra parameter som kan brukes som statusindikator til alle subprogrammer (f.eks. C biblioteksfunksjoner) o Unntakshåndtering ( excepton handling ): Prosesseringen av et unntak o Sende med en label -parameter til alle subprgrammer ved feil returneres til denne (f.eks. FORTRAN) o Unntakshåndterer ( exception handler ): Programkoden for unntakshåndtering o Kasting av unntak ( raise an exception eller throw an exception ) gjøres når den uvanlige hendelsen inntreffer o Sende med en unntakshånderings-subrutine til alle subprogrammer Feilhåndteringskode er kjedelig å skrive og forkludrer programmet Fanges ikke feilen, går kontrollen til operativsystemet som gir en feilmelding og avslutter programmet IN211-Strukturereprogram-7 IN211-Strukturereprogram-8
Språk med unntakshåndtering Utfordringer i språkutformingen I et språk med unntakshåndtering fanges unntakene automatisk bak kulissene, og det gis muligheter for å rette opp problemet og fortsette. Feilforplantningsmekanismer åpner for effektiv gjenbruk av unntakshåndterere. Hvordan blir unntakshåndterere spesifisert og hva er deres skop? Hvordan blr et unntak bundet til en unntakshåndterer? Hvor fortsetter utførelsen (hvis den fortsetter) etter at unntakshåndtereren er ferdig? Hvordan spesifiseres brukerdefinerte unntak? Mange språk har ikke komplett unntakshåndtering, men gir allikevel muligheter for å fange opp I/O-feil (inkludert EOF). Skal det finnes standard unntakshåndterere for programmer som ikke inneholder egne håndterere? Skal det finnes implisitte, innebygde unntak? Skal det være mulig for programmereren eksplisitt å kaste implisitte, innebygde unntak? Skal feil som fanges av maskinvaren betraktes som unntak som skal kunne håndteres? Skal unntakshåndtering kunne settes ut av kraft? IN211-Strukturereprogram-9 IN211-Strukturereprogram-10 Exception is raised Unntakshåndtering kontrollflyt Executing code some statement; Termination Exception to handler binding? Continuation Exception handlers when when when Sebesta Figure 14.1 page 563 Java unntakshåndtering try { farlig kode catch (SomeException) { // flere unntakshåndterere finally { Alle unntak er objekter av subklasser av klassen Throwable Java-biblioteket omfatter to subklasser av Throwable : 1. Error Kastes av JVM for hendelser som f.eks. heap full. Fanges aldri av brukerprogrammer 2. Exception To predefinerte direkte subklasser: 1. IOException 2. RuntimeException (f.eks. ArrayIndexOutOfBoundsException og NullPointerException) Brukerdefinerte unntak er vanligvis subklasser av Exception IN211-Strukturereprogram-11 IN211-Strukturereprogram-12
Java unntakshåndtering try { farlig kode catch (SomeException) { // flere unntakshåndterere finally { Alle unntak er objekter av subklasser av klassen Throwable Error Throwable IOException Exception RuntimeException UserExceptions Java unntakshåndtering Alle catch krever en navnet parameter og alle parametre må være subobjekter av Throwable Unntak kastes med throw, inkluderer ofte new-operatoren for å lage et nytt unntaksobjekt: throw new MyException( ); Et unntak bindes til den første catch med en parameter som er av samme klasse som det kastede objektet eller en superklasse til det Finnes ingen passende catch, forplantes unntaket videre til kallende metode. Fanges ikke unntaket et eller annet sted, avsluttes programmet. En catch(exception) fanger det meste! ArrayIndexOutOfBoundsException NullPointerException Et unntak kan bli håndtert og kastet på nytt ved å inkludere en throw i unntakshåndtereren kan da kaste et annet unntak enn det opprinnelige IN211-Strukturereprogram-13 IN211-Strukturereprogram-14 Java unntaksforplantning Unntak av klassene Error og RunTimeException og deres subklasser blir kalt unchecked exceptions. Kompilatoren bryr seg ikke om disse. Alle andre unntak er checked exceptions. Checked exceptions som kastes av en metode må enten 1. Listes i throws-leddet i metodeklarasjonen 2. Håndteres i metoden En metode som kaller en metode som lister en bestemt checked exception i throws-leddet har tre muligheter: 1. Fange og håndtere unntaket 2. Fange unntaket og kaste et nytt som er listet i dens eget trows-ledd 3. Deklarere den i throws-leddet og la være å håndtere den Korrekt bruk av unntak? enum Ukedager {mandag, tirsdag, onsdag, torsdag, fredag, lørdag, søndag ; imorgen := Ukedager succ(idag); exception when constraint_error => imorgen := mandag; IN211-Strukturereprogram-15 IN211-Strukturereprogram-16
Navn Adresse Hendelsesdrevne programmer Registering Lagre Tøm xxxxxxx xxx xxxxxxx xxx xxxxxxx xxx Modularisering I ALGOL og ALGOL-liknende språk er programmet en monolittisk tekst. OK for små programmer, problematisk for store ( programming in the large ) For programming in the large er det behov for å kunne dele programmet opp i relativt selvstendige moduler, med o Høy kohesjon o Lav kopling Programming in the small : Utvikling av enkeltmoduler Lyttere fanger opp hendelser i skjermbildet og gir kontrollen til ansvarlig rutine. Programming in the large : Dekomponere i moduler Hvorfor modularisering? Skjermbildet kan o Dele noe stort opp i håndterbare biter o settes opp av programmet o Oppdeling av navnerommet, unngå navnekonflikter for rutiner/metoder o settes opp automatisk o Uavhengig utvikling og vedlikehold av moduler IN211-Strukturereprogram-17 IN211-Strukturereprogram-18 Viktige konsepter i modularisering Viktige konsepter i modularisering (forts.) Service og service provider klientemoduler har behov for service som ytes av service providers, dvs. tjenermoduler Innkapsling in the large : Gi klientene tilgang til de tjenestene de behøver, men ikke mer Grensesnitt o Beskriver en mengde med tjenester som eksporteres av en o Implementasjonen er skjult for omgivelsene dermed kan implementasjonen fritt endres, så lenge grensesnittet ikke berøres tjenermodul (typer, rutinekall/signaturer, variabler), og som modulens klienter kan gjøre seg nytte av ved å importere dem. o Rutinene i en modul har behov for å dele hemmeligheter med hverandre, uten at disse blir kjent utenfor modulen. o Hvordan får klientene tilgang til tjenestene? Hva kan eksporteres og importeres? Implementering o Implementerer tjenestene som er beskrevet i grensesnittet Det er hensiktsmessig å skille grensesnitt fra implementasjon Hvordan støtter programmeringsspråkene opp under dette? Disse konseptene er velkjente i den objektorienterte verden, og realiseres der ved hjelp av klasser. En modul er imidlertid som regel mer omfattende enn en enkel klasse. IN211-Strukturereprogram-19 IN211-Strukturereprogram-20
Separat/uavhengig kompilering Logisk vs. fysisk modularisering For programming in the large er det en stor fordel om programmeringsspråket tilbyr separat/uavhengig kompilering. Programmeringsspråket må definere o Kompileringsenheten: Hva kan kompileres uavhengig? Hva kan grupperes inn i en kompileringsenhet? o Kompileringsrekkefølgen: Hvor uavhengig kan fysiske moduler implementeres og kompileres? Må kompileringen skje i en bestemt rekkefølge? Logisk modularisering: Inndeling av programkoden i subrutiner, funksjoner, klasser, pakker o.l. Fysisk modularisering: Fordeling av programkoden på ulike filer som kan kompileres separat eller uavhengig o Innkapsling og synlighet: Hvilke synlighetsmekanismer og tilgangskontrollmekanismer understøttes av språket? o Typesjekking: Hva sjekkes på tvers mellom separatkompilerte moduler? IN211-Strukturereprogram-21 IN211-Strukturereprogram-22 Separat/uavhengig kompilering Fysisk modul: En fil Modularisering i C Separat kompilering (def): De fysiske modulene kan kompileres separat, men i en bestemt rekkefølge: Grensesnittene, så grensesnittets klienter og grensesnittets implementasjon Hvis grensensitt og implementasjon ikke er separate må tjener kompileres før klienter Uavhengig kompilering (def): De fysiske modulene kan kompileres fullstendig uavhengig av hverandre. Umuliggjør typesjekking på tvers av moduler av kompilatoren. (Kan linkeren overta denne oppgaven?) Ingen språkstøtte for å skille grensesnitt fra implementasjon. Men det er sedvane å dele en logisk modul i to fysiske moduler, som omtrent tilsvarer grensesnittet og implementasjonen the header file eller include file (file.h) og the implementation file (file.c). Se eksempel i læreboka side 255. Ingen innkapslingsmuligheter innen den fysiske modulen (filen) Navn som er definert utenfor funksjoner er kjent også utenfor den fysiske modulen. Funksjoner er pr default tilgjengelige for import. Variabler blir tilgjengelige ved å deklarere dem extern. Funksjoner og variable kan deklareres som lokale med static. IN211-Strukturereprogram-23 IN211-Strukturereprogram-24
Modularisering i C++ C++ bygger som kjent på C, derfor mye likt Viktige utvidelser: Klasser o understøtter implementering av abstrakte datatyper o understøtter innkapslingsprinsippet En klasses grensesnitt og implementering kan (men må ikke) adskilles og kompileres hver for seg (grensesnittet først!) Men ikke noe rent skille grensesnitt -definisjonen innholder også private entities, usynlige for klientene Klienter kan kompileres med tilgang bare til grensesnittet, ikke til implementasjonen Navn definert i klassen er lokale med mindre de er deklarert public. Kan gi tilgang til private variable gjennom Friend functions, se side 259 Oppdeling av det globale navnerom med namespaces, side 261 IN211-Strukturereprogram-25 Modularisering i Java Klasser og grensesnitt kan grupperes sammen i en package, som er en innkapslingsenhet for programming in the large. Se oppskrift i ukens øvingsoppgave! Klasser og grensesnitt deklarert som public kan eksporteres til andre pakker Hvordan importere pakker: import pakkenavn.*; Grensesnitt og klasser kan fordeles på filer som kan kompileres separat (men ikke uavhengig!) IN211-Strukturereprogram-26