Det första objektorienterade programspråket var Simula-67. Språket konstruerades av Ole-Johan Dahl och Kristen Nygaard vid Norsk Regnesentral. Programspråket byggde på Algol 60 och var avsett för simulering. Det hade de flesta av de egenskaper som man idag förknippar med objektorienterad programmering; till exempel garbage collection (automatisk minneshantering), arv och klasser.
Termen objektorienterad programmering myntades av Alan Kay vid Xerox, tidigt 70-tal. Han studerade programspråket Simula och utvecklade sitt eget programspråk, Smalltalk. Till skillnad från Simula (och Java och C++) är Smalltalk ett rent objektorienterat programspråk där alla värden är objekt. De första grafiska användargränssnitten skrevs i Smalltalk (och i Lisp).
Ett av de mest kända objektorienterade programspråken är C++. Det konstruerades av Bjarne Stroustrup vid Bell Labs och fick sitt namn 1983. Programspråket bygger på C med idéer från Simula och Algol-68. En del begrepp är direkt tagna från Simula-67, till exempel nyckelordet 'virtual'. C++ skiljer sig från de flesta andra objektorienterade programspråken i att det saknar garbage collection—programmeraren måste se till att minnesutrymme som ej används längre avallokeras.
Jag kommer berätta mer om Java senare, men kortfattat: Java utvecklades vid Sun Microsystems av James Gosling och presenterades 1995.
Vi talar om
De här begreppen är förstås sammankopplade: objektorienterade programspråk är konstruerade för att göra det lättare att använda OOP i sitt program. Man kan använda ett objektorienterat programspråk för att skriva program som inte följer principerna för OOP, å andra sidan är det också möjligt att skriva ett objektorienterat program i ett programspråk som inte har utformats för att stöda OOP.
Vad vill vi åstadkomma med programspråk och nya utvecklingsmodeller? Alltså: varför håller vi överhuvud taget på och diskuterar de här sakerna?
Förhoppningen är förstås att nya programspråk och nya utvecklingsmodeller ska hjälpa oss när vi skriver program. Några allmänna (och ganska självklara) önskemål är att
Det kan vara bra att ha de här sakerna i åtanke när du läser om olika programspråk, utvecklingsmodeller eller principer för hur man utformar program. När man tar fram ett programspråk eller utvecklar olika principer för programutveckling är ju målet att de ska hjälpa oss att skriva program.
Här är ett bra tillfälle att reflektera över egna erfarenheter. Har du själv deltagit i nåt större projekt?
Några ideér som fanns före OOP men blivit en del av OOP:
Metoder för analys, design
Låt oss börja med några exempel. I ett programspråk har vi konstruktioner för aritmetik, proceduranrop, hantering av filsystem, I/O etc.
Vi kan till exempel använda oss av och resonera om en operation som:
x + y
utan att veta något om hur den implementeras. Beroende på processor, kompilator och typ hos variablerna kan den här operationen implementeras på en lång rad olika sätt.
Från datorns synvinkel finns ju inga tal, bara ettor och nollor—men egentligen finns ju inte de heller, det enda vi har är ju elektriska signaler på olika spänningsnivåer...
På samma sätt talar vi om filer som om de var verkliga fysiska objekt, men i själva verket har vi ju bara några magnetiserade skivor (om filen lagras på en hårddisk).
Så en abstraktion består av två saker:
Man kan säga att en abstraktion är ett begrepp som man kan resonera om oberoende av den underliggande implementationen.
En abstraktion kan vara baserad på begrepp som kommer från applikationsdomänen, på matematiska koncept, eller något som man tagit fram som en del av implementationsarbetet.
Vid utveckling av komplexa system är det ofta lämpligt att introducera abstraktioner på olika nivåer.
Under kursens gång kommer vi att stöta på ett antal exempel på abstraktioner.
Vi kan titta på ett program och fundera på hur väl de tre sista punkterna är uppfyllda. Måste man studera hela programmet för att förstå det, eller kan en del av programmet förstås utan att man tittar på resten? Vad händer när problemspecifikationen ändras? Hur svårt är det att lokalisera fel?
Inkapsling är mekanismer i programspråket för att dölja implementationen av en komponent. Jag kommer att prata om inkapsling i Java.
Begreppet myntades i en NATO-konferens 1968.
Man tyckte sig se ett problem ("Software crisis") som yttrade sig i att programutvecklingsprojekt ofta tog för lång tid att genomföra och att resultaten var ofta mindre lyckade.
Man uppfattade att traditionella ingenjörsprojekt (tex brobyggen, tunnelbyggen, mekaniska konstruktioner) fungerade bättre och ville utnyttja liknande principer i programutveckling.
Alan Kay som uppfann programspråket Smalltalk var den förste som pratade om objektorienterad programmering. Han definierade OOP med dessa punkter:
(Alan Kay hade en ytterligare punkt som jag har valt att inte ta med.)
Alla utom den första punkten gäller även Java. Jag skulle även vilja lägga till dessa:
En klass kan ärva beteende och datastrukturer från tidigare klasser
En person har två egenskaper, namn och ålder. Vi definierar en metod
hello()
som skriver ut en kort hälsning med personens namn och
ålder.
Konstruktorn (som har samma namn som klassen) skapar och initialiserar objekt.
(Var inte orolig om du inte förstår alla detaljer i exemplet; allt kommer att förklaras noggrannare senare.)
public class Person { private String name; private int age; public Person (String n, int a) { name = n; age = a; } public void hello() { System.out.println("Jag heter "+ name +" och är "+ age +" år gammal."); } public void setName (String n) { name = n; } public String getName () { return name; } public static void main (String arg[]) { Person olle = new Person ("Olle", 7); olle.hello(); } }
Metoden main
kommer att köras när man startar programmet. Vi börjar
med att skapa en person Olle (personen heter "Olle"
och är bunden till
en variabel olle
. Vi skickar meddelandet hello()
till Olle.
(metoder liknar funktioner i C)
Exempel: Skapa en person.
Person olle = new Person ("Olle", 7);
Skicka ett meddelande till personen:
olle.hello();
Detta ger följande utskrift:
Jag heter Olle och är 7 år gammal.
struct
i C (dvs skapa mycket enkla
klasser som lagrar data men inte gör nåt annat)Skapa en klass Employee
som representerar anställda. Denna klass ärver
av klassen Person
.
public class Employee extends Person { private int salary; public Employee (String n, int a, int s) { super(n, a); salary = s; } public void setSalary (int s) { ... } public int getSalary () { ... } }
Ett enkelt exempel. En anställd som heter John, är 23 år gammal och tjänar 123456 (vi vet dock inte i vilken valuta).
Employee john = new Employee("John", 23, 123456);
Skicka meddelandet hello()
.
john.hello();
Höj hans lön till 1234567
.
john.setSalary(1234567);
Vi kan ändra klassen Employee
och ge metoden hello()
en definition i
Employee
(naturligtvis vill varje anställd tala om vad han eller hon tjänar):
public void hello() { super.hello(); System.out.println("Jag tjänar "+ salary); }
Denna metod anropar först super.hello()
dvs metoden med samma namn i
Person
. Sedan skriver vi ut den anställdes inkomst.
Med denna definition ger meddelandet
john.hello();
utskriften
Jag heter John och är 23 år gammal. Jag tjänar 123456
Anta att vi har fler klasser (Student
, Teacher
, ...) som alla ärver av
klassen Person
samt en array av personer:
Person[] alla = new Person[10]; alla[0] = olle; alla[1] = john; ... for(int i = 0; i<10; i++){ alla[i].hello(); }
Vad händer när man skickar meddelandet hello()
till de olika objekten?
Den allmänna regeln är: Objektet "vet" vilken klass det tillhör.
Därmed vet objektet också vilken metod som ska anropas. Detta gör att
en medlem av klassen Employee
utför metoden som är definierad för den
klassen (och berättar om vad han eller hon tjänar).
Vi kan ha olika definitioner av hello()
i de olika klasserna. När ett
visst objekt tar emot meddelandet utförs motsvarande metod.