UML og Klasser og Objekter i Python Uge 47 Computer science, kap 7 Martin Fowler: UML distilled, kap. 3. Addision-Wesley, 2004. Learning Python: kap 15-16, 19-22.
Diagrammer Diagrammer er visuelle fremstillinger af beskrivelser der består af en lang række simple ens sætninger
Entity-relation Entitet har Egenskab Entitet A står-i-relation-til Entitet B Relation Entitet Egenskab
ER-diagram Et kæledyr har et navn Et kæledyr har en fødselsdag Et kæledyr er ejet af en person
Kald-struktur Structure chart Funktion A kalder funktion B A B
HTML-tabellen def makeHtmlPage(tablecontents): return makeStart() + makeTable(tablecontents) + makeEnding() def makeTable(tablecontents): …. htmltabel += makeRow(row) … return htmltabel def makeRow(rowcontents): htmlrow += makeCell(cell) return htmlrow
HTML-tabellen makeHtmlPage(tablecontents): makeEnding() makeStart() makeTable(tablecontents) makeRow(rowcontents): makeCell(cell)
Brugsscenarier Use case diagrams Defineret ved formål Aktør interagerer med Systemmodul Formål her: brugerne og udvikleren retter simuleringen så den passer med brugernes teori Hvem har hvilke ansvarsområder
UML klassediagrammer Klasse A er en underklasse/overklasse af klasse B Klasse A er en del af/har delen klasse B Klasse A står i relation til klasse B Klasse A afhænger af klasse B Klasse A har attributten B Klasse A kan udføre metoden B
Konventioner for notationsform Klasser starter altid med stort begyndelsesbogstav og efterfølgende ord ligeledes med stort Attributter og metoder starter med lille begyndelsesbogstav og efterfølgende ord med stort Klassenavn MinKlasse -attrNummer1 -attrNummer2 +metodeNr1 +metodeNr2 +metodeNr3 Attributter Access modifiers Metoder
Generalisering/specialisering Klasse A er en underklasse/overklasse af klasse B
Aggregering/dekomponering Klasse A er en del af/har delen klasse B
Organisationer: Århus Universitet TAP=Teknisk-Administrativt Personale VIP=Videnskabeligt Personale
Metoder og attributter Klasse A kan udføre metoden B Klassen A har attributten B Primitive attributter defineres i klassen, komplekse bliver defineret som en ny klasse med et tilhørsforhold class Ledelse: def __init__(self, medlemmer, organisation): self.medlemmer = medlemmer self.organisation = organisation def udnaevn(self, medlem): self.medlemmer.append(medlem) def fjern(self, medlem): self.medlemmer.remove(medlem)
Delvis implementering class Institut(Organisation): def __init__(self, navn,addresse, ledelse,fagområde): Organisation.__init__(self, navn,addresse, ledelse) self.fagområde = fagområde class Ledelse: def __init__(self, medlemmer, organisation): self.medlemmer = medlemmer self.organisation = organisation def udnaevn(self, medlem): self.medlemmer.append(medlem) def fjern(self, medlem): self.medlemmer.remove(medlem) class Ansat(Entitet): def __init__(self, navn,addresse, lønramme,cpr,bankkonto): Entitet.__init__(self,navn, addresse) self.lønramme = lønramme self.cpr = cpr self.bankkonto = bankkonto class Entitet: def __init__(self, navn,addresse): self.navn = navn self.addresse = addresse def verbalize(self): print vars(self) class Organisation(Entitet): def __init__(self, navn,addresse, ledelse): Entitet.__init__(self,navn, addresse) self.ledelse = ledelse class Uddannelse(Organisation): def __init__(self, navn,addresse, ledelse, tilknytning): Organisation.__init__(self, navn,addresse, ledelse) self.tilknytning=tilknytning class Universitet(Organisation): def __init__(self, navn,addresse, ledelse, fakulteter): self.fakulteter = fakulteter class Fakultet(Organisation): def __init__(self, navn,addresse, ledelse, fagområde,institutter,uddannelser): self.fagområde = fagområde self.institutter = institutter self.uddannelser = uddannelser
Implementering Aggregering: en liste af komponenterne (en beholder) Specialisering: subklasser Attributter: variable knyttet til klassen Metoder: funktioner knyttet til klassen
Patterns En gennemtestet metode til at løse et bestemt problem (GOF) State pattern Løser problemet at en instans ikke kan skifte klasse Når man bliver lektor skal man oprette en helt ny ansat
Løs opgave Diagrammet efter diskussion
Software: OOogSql
Software: Beggars and philanthropists Modellering af et simpelt kommunikationssystem for autonome agenter til brug i simuleringer af, hvordan forskellige økonomiske instanser fordeler penge ud fra de ansøgninger de modtager, og ligeledes hvordan de forskellige ansøgere udvælger hvor de skal ansøge.
Beggars and philanthropists demo Prompten viser hvordan de enkelte agenter kommunikerer med hinanden Hele kildekoden kan ses bagefter hvis der er nogen der har interesse, består af ca. 2600 linjers kode
Kobling Coupling Skal være lav. Afhængigheder mellem moduler Kan man rette i eet modul uden at skulle rette i andre moduler? Skal være lav. Skal helst kun være i nedadgående retning Øger genbrugelighed GUI Model Persistens
Sammenhæng Cohesion Skal være høj. Sammenhæng indenfor det enkelte modul Logical cohesion: en klasse repræsenterer en sammenhængende verden. En person klasse kan ikke have funktioner til at køre en bil. Skal være høj. Øger også genbrugelighed, vi kan bruge en rigtigt defineret person klasse mange gang
Engineering og reverse-engineering Fra diagrammer til kode, kan automatiseres Fra kode til diagram kan også automatiseres Diagrammet fra B&P var reverse-engineered
Pause Pause
Klasser og objekter
Klasser og objekter En klasse beskriver en klump af samhørende funktioner og variable En klasse er en beskrivelse. En kage form Klassens objekter er instanser af klassen. De enkelte kager En programudførelse indeholder (for det meste (klasse reference)) objekter, ikke klasser
Samlingsmønstret (composite) Behandl helheder og dele på samme måde (her print) Rekursiv definition Samling ::= {Del}* Del ::= Simpel |Samling Samling Del Del Samling Del Del
Nedarvning Subklassen arver variable og funktioner fra superklassen Simpel: navn __str__ Samling: dele tilføj fjern hent Begge arver navn fra overklassen Simpel bevarer superklassens __str__’ Samling tilføjer nye ting Samling overskriver superklassens ’ __str__’
__init__ og andre funktioner class Del: def __init__(self, navn): self.navn = navn def __str__(self, niveau = 1): text = '\n' + niveau *' '+'(' + self.navn + ')' return text class Samling(Del): def __init__(self, navn, dele = None): Del.__init__(self, navn) if dele == None: self.dele = [] else: self.dele = dele def tilfoej(self,enDel): self.dele.append(enDel) def fjern(self,enDel): if enDel in self.dele: self.dele.remove(enDel) def hent(self,delNavn): for d in self.dele: if d.navn == delNavn: return d return None text = '\n' + niveau *' '+'(' + self.navn for enDel in self.dele: text += enDel.__str__(niveau+1) text += '\n' + niveau *' '+')' class Simpel(Del): superklasse initialisering subklasse metoder subklasse
Dannelse af objekter Inits parametre bil = Samling('bil',[Samling('karosseri'),Samling('hjul'),Samling('motor')]) bil.hent('karosseri').tilfoej(Simpel('Tag')) bil.hent('karosseri').tilfoej(Simpel('Doere')) bil.hent('karosseri').tilfoej(Simpel('Bund')) bil.hent('hjul').tilfoej(Simpel('venstre forhjul')) bil.hent('hjul').tilfoej(Simpel('hoejre forhjul')) bil.hent('hjul').tilfoej(Simpel('venstre baghjul')) bil.hent('hjul').tilfoej(Simpel('hoejre baghjul')) bil.hent('motor').tilfoej(Simpel('stempler')) bil.hent('motor').tilfoej(Simpel('karburator')) print bil Kald af hjulsamlingens tiltøj-funktion
Output (bil (karosseri (Tag) (Doere) (Bund) ) (hjul (venstre forhjul) (hoejre forhjul) (venstre baghjul) (hoejre baghjul) (motor (stempler) (karburator)
Definition af klasser: superklasse Class <klassenavn>(<evt. superklasse>): <init-funktionen> <funktioner der tilhører klassen> class Samling(Del): '''repræsenterer en samling af dele, enten samlinger eller simple''' def __init__(self, navn, dele = None): Del.__init__(self, navn) self.dele = dele def tilfoej(self, enDel): if self.dele == None: self.dele = [] self.dele.append(enDel) def fjern(self,enDel): if enDel in self.dele: self.dele.remove(enDel) def hent(self,delNavn): for d in self.dele: if d.navn == delNavn: return d return None def __str__(self, niveau = 1): '''giver en textuel repræsentation af klassen __str__ gør det muligt at printe klassen direkte''' text = '\n' + niveau *' '+'(' + self.navn for enDel in self.dele: text += enDel.__str__(niveau+1) text += '\n' + niveau *' '+')' return text klassenavn Init metoden (Constructor) metoder der tilhører klassen
Check om det er rigtigt: Introspektion Python indeholder en række funktioner der giver direkte adgang til dens indvolde Objekt.__dict__: en dictionary der gemmer objektets attributter og værdier Dir(object): returnerer alle de attributter og metoder der er knyttet til objektet.
Introspection def introspect(self): text = 'Dictionary: ' text += '\n'+ str(self.__dict__) text += '\n'+'Attributes and functions: ' text += '\n'+ str(dir(self)) return text
Eksempel Bil (Samling): Tag (Simpel): Dictionary: {'dele': [<__main__.Samling instance at 0x00FC2BE8>, <__main__.Samling instance at 0x00FC2CD8>, <__main__.Samling instance at 0x00FC2D00>], 'navn': 'bil'} Attributes and Functions : ['__doc__', '__init__', '__module__', '__str__', 'dele', 'fjern', 'hent', 'introspect', 'navn', 'tilfoej'] Tag (Simpel): {'navn': 'Tag'} '__str__', 'introspect', 'navn'] Nedarvede fra Python Brugerdefinerede
Generering af klasser Klasse definition Generering af klasse class Simpel(Del): def __init__(self, navn): Del.__init__(self, navn) Generering af klasse <klassenavn>(<inits parametre undtagen self>) Forhjul = Simpel('venstre forhjul') __init__kaldes automatisk når klassen genereres Simpel’s init kalder superklassens init Husk at give den parametren self med!!
Self self henviser til det nydannede objekt Når man i klassedefinitionen vil henvise til objektets egne funktioner eller variable skal det ske via self def getName(self): return self.name
Med og uden self class Samling(Del): def __init__(self, navn, dele): Del.__init__(self, navn) self.dele = dele stelnummer = 100 ’Stelnummer’ er blot en lokal variabel i __init__ der forsvinder når funktionen har kørt. ’Dele’ er derimod fast knyttet til objektet >>> bil.dele [<__main__.Simpel instance>, <__main__.Samling instance >, <__main__.Simpel instance >] >>> bil.stelnummer Traceback (most recent call last): File "<pyshell#7>", line 1, in ? bil.stelnummer AttributeError: Samling instance has no attribute 'stelnummer' >>>
Self bruges også til at referere til objektets egne funktioner class Eksempel: def __init__(self,enListe): self.minListe = enListe def summer(self): resultat = 0 for i in self.minListe: resultat += i return resultat def udskrivSum(self): print self.summer() etElement = Eksempel([1,2,3,4]) etElement.udskrivSum() Resultat: 10 >>>
Udeladelse af self def udskrivSum(self): print summer() Python ved ikke hvad ’summer’ refererer til:’ NameError: global name 'summer' is not defined
Klasse variabler Det er også muligt at referere direkte til parametre defineret i klassen i stedet for objektet. Dette gøres uden self Brug af konstante til at gøre koden renere og lettere at omstrukturere, vi bruger A.meters alle steder i stedet for tallet 100, hvis det skulle ændre sig skal vi kun ændre et sted Er defineret uden for klassens metoder! Derfor ydre scope class A: meters = 100 def printer(self): self.meters = 50 print "i objektet: " + str(self.meters)+", i klassen: "+str(A.meters) print "i klassen: "+ str(A.meters) instance = A() instance.printer() >>> >>>i klassen: 100 >>>i objektet: 50, i klassen: 100
Hele Python består af objekter >>> L.append(4) >>> L [1, 2, 3, 4] >>>
Relationer mellem objekter Der er en én-mange relation mellem en samling og dens dele En relation er et abstrakt begreb der kan implementeres på mange måder. SQL: som en relation Python: som en liste af objekter
SQL Helhedens primærnøgle benyttes som fremmednøgle i delen Del Samling
Python class Samling(Del): def __init__(self, navn, dele): Del.__init__(self, navn) self.dele = dele Helheden har en liste over de objekter der repræsenterer dens dele
OOogSQL
Persistent class Persistent(SQL): '''Abstract class''' Omdefinering af overklassens funktion Reference til ’mig selv’ class Persistent(SQL): '''Abstract class''' def __init__(self, tableName, values): self.primaryKey = database.getPrimaryKey(tableName) self.tableName = tableName columnNames = database.getColumnNames(tableName) SQL.__init__(self, columnNames, values) def printContents(self): '''prints the data of the object''' print 'Table name: '+self.tableName print 'Primary key: ' + self.primaryKey SQL.printContents(self) def getPrimaryKey(self): '''returns the primary key''' return self.primaryKey Overklassens navn Genbrug af overklassens funktioner
Table Self.getPrimaryKey() er nedarvet fra overklassen class Table(Persistent): def __init__(self, tableName): values = database.findRecords(tableName,[]) Persistent.__init__(self,tableName, values) #insert a list of Row-instances corresponding to the values rows =[] primaryKey = self.getPrimaryKey() i = self.columnNames.index(primaryKey) for v in self.values: theKey = v[i] rows.append(Row(tableName,theKey)) self.rows = rows def getRow(self,theKey): '''returns a row-instance whose primary key = theKey if none exists, returns None''' for r in self.rows: if r.get(primaryKey) == theKey: return r return None def printContents(self): '''prints the data of the object''' print 'Table name: '+self.tableName print 'Primary key: '+ self.primaryKey print ’Column names : \n' + str(self.columnNames) print 'Components: ' for c in self.rows: c.printContents() Self.getPrimaryKey() er nedarvet fra overklassen
Brug af OOogSQL 1 fetch a row from Child with primary key 1111111111, change the first name to Jeppe and store it, fetch the row again to see if the change worked aRow = Row('Child','1111111111') aRow.put('firstname','Jeppe') aRow.update() aRow.printContents() Resultat: Table name: Child Primary key: cpr Column names: ['cpr', 'firstname', 'lastname', 'address', 'city', 'gender', 'email', 'phone', 'insurance', 'hasFather', 'hasMother', 'hasDoctor'] Values: [['1111111111', 'Jeppe', 'Andersen', 'Thorsgade 20', '8410', 'dreng', '', '86379790', 'Baltica', '1111111112', '1111111113', '1111111114']]
Brug af OOogSQL 2 restore the first name, print a html version of the row aRow.put('firstname','Jeppe Boegh') aRow.update() print aRow.makeHTMLTable()
Resultat <table width="75%" border="1"> <tr> <td>cpr</td> <td>firstname</td> <td>lastname</td> <td>address</td> <td>city</td> <td>gender</td> <td>email</td> <td>phone</td> <td>insurance</td> <td>hasFather</td> <td>hasMother</td> <td>hasDoctor</td> </tr> <td>1111111111</td> <td>Jeppe Boegh</td> <td>Andersen</td> <td>Thorsgade 20</td> <td>8410</td> <td>dreng</td> <td></td> <td>86379790</td> <td>Baltica</td> <td>1111111112</td> <td>1111111113</td> <td>1111111114</td> </table>
Brug af OOogSQL 3 make a new empty row-object, insert values into the object aRow = Row('Child') valueList = ['1111111111', 'Jeppe Boegh', 'Andersen', 'Thorsgade 20', '8410', 'dreng', '', '86379790', 'Baltica', '1111111112', '1111111113', '1111111114'] aRow.putAllValues(valueList) aRow.printContents() Resultat Table name: Child Primary key: cpr Column names: ['cpr', 'firstname', 'lastname', 'address', 'city', 'gender', 'email', 'phone', 'insurance', 'hasFather', 'hasMother', 'hasDoctor'] Values: ['1111111111', 'Jeppe Boegh', 'Andersen', 'Thorsgade 20', '8410', 'dreng', '', '86379790', 'Baltica', '1111111112', '1111111113', '1111111114']
Brug af OOogSQL 4 create a selection of all children that live at the same address as their mother' SQLordre = '''SELECT Child.cpr, Child.firstname, Child.lastname FROM Child, Person WHERE Child.hasMother = Person.cpr AND Child.address = Person.address''' aSelection = Temporary(['cpr','firstname','lastname'],SQLordre) aSelection.printContents() Resultat Column names: ['cpr', 'firstname', 'lastname'] Values: (('1111111115', 'Stine', 'Jacobsen '),)
Brug af OOogSQL 5 print aSelection.makeHTMLTable() Resultat <table width="75%" border="1"> <tr> <td>cpr</td> <td>firstname</td> <td>lastname</td> </tr> <td>1111111115</td> <td>Stine</td> <td>Jacobsen </td> </table>
Fordelen ved UML Vi kan beskrive de logiske forhold i problemområdet uden at skulle tage beslutninger om implementeringen At kunne dette er jeres hovedkvalifikation. Har vi et godt UML diagram er der ikke lang vej til implementering for dygtige programmører.
Databse.py Ny udgave på nettet, har foretaget nogle ændringer, idet at en session førhen blev lukket efter hvert kald. Dette gav problemer med nogle ting Husk at lukke connection nu! database.closeConnection() Eller noget der nedarver SQL klassen, ligeledes objekt.closeConnection()
Opgave 9 Øvelse 9 ligger på hjemmesiden. Udfyld de steder hvor der står ”insert code here” så det passer til beskrivelsen af metoden eller funktionen Test at alting virker som det skal