1. Introduktion

1.1. Vad är OOP?

Objektorienterad Programmering ("OOP") är en programmeringsparadigm - det vill säga ett sätt att strukturera hur man skriver kod.

I objektorienterad programmering skriver man så kallade Klasser. I klasserna man kan lagra data (attribut) och beteenden (metoder) som interagerar med datan.

När man skapat en klass, kan man sen skapa objekt (instanser) av klassen som man sedan kan anropa metoderna på. Datan (attributen) i varje instans är separat från alla andra instansers data.

En liknelse

En liknelse kan vara att alla människor har ett namn (attribut), och har beteendet att säga sitt namn (en metod som säger "Hej, jag heter <<namn>>"), men alla människor är unika varelser med egna, unika namn.

I liknelsen skulle det alltså finnas en klass som heter Människa. Människa-klassen har instans-attributet namn, och instans-metoden hälsning. I en familj skulle alla (mänskliga) medlemmar vara instanser av klassen Människa.

Rätt använt gör OOP det enklare att skriva större, mer komplex mjukvara eftersom man då kan

  1. Återanvända kod - för att skapa en ny människa gör man bara en ny instans av människo-klassen och ger hen ett namn.

  2. Lagra data för en instans på samma ställe som metoder för att manipulera datan

  3. Förhindra att (andra) utvecklare manipulerar datan på felaktigt sätt genom så kallad inkapsling.

  4. Använda arv och göra varianter av klasser genom att skapa subklasser som återanvänder all kod från en annan klass, men med mindre ändringar eller tillägg.

Ruby är ett extremt objektorienterat språk - faktum är att det ni i Programmering 1 kallat för datatyper (t.ex strängar, flyttal och arrayer) egentligen är klasser - i det här fallet klasserna String, Float och Array

Example 1. Några klasser i Ruby
1
2
3
4
5
6
irb(main):001:0> en_string = "Hello OOP" #=> "Hello OOP" (1)
irb(main):002:0> en_float = 13.37        #=> 13.37 (1)
irb(main):003:0> en_array = [1,2,3]      #=> [1, 2, 3] (1)
irb(main):004:0> en_string.class         #=> String (2)
irb(main):005:0> en_float.class          #=> Float (2)
irb(main):006:0> en_array.class          #=> Array (2)
1 Här skapas instanser (objekt) av klasserna String/Float/Array. Det som tilldelas variablerna är alltså en instans av respektive klass.
2 Här anropas instans-metoden class på respektive instans. Metoden class finns fördefinerad i alla klasser.

1.2. Varför används OOP?

Objektorienterad programmering används av flera olika anledningar som mer eller mindre flyter ihop.

1.2.1. Struktur

OOP kan användas för att strukturera kod - t.ex ligger alla metoder (funktioner) som har med matematik att göra i Math-klassen, t.ex Math.sqrt och Math.sin och allt som har med tid att göra i Time-klassen, t.ex Time.now.

Att gruppera kod på detta vis gör det enklare för en utvecklare att veta var hen ska kolla för att hitta en viss metod, eller var hen ska skriva en ny metod.

Example 2. Ett par klassmetoder
1
2
3
Math.sqrt(16) #=> 4 (1)
Math.cos(0)   #=> 1.0 (1)
Time.now    #=> 2022-08-22 10:42:05.348957 +0200 (2)
1 Två anrop till klass-metoder (sqrt och cos) på Math-klassen
2 Ett anrop till klass-metoden now som finns definerad i Time-klassen.

1.2.2. Mall

Man kan se klasser som mallar för att skapa objekt (instanser) som innehåller attribut (data) och metoder som interagerar med objektets data

1.2.3. Modellering

När man ska skapa större program kan man först skapa en "domänmodell" för hur problemområdet (brukar ofta kallas problemdomänen eller enbart domänen) som programmet verkar inom fungerar. Modellen är alltså inte skriven i kod, utan består av text som beskriver hur området fungerar.

T.ex kan en domän vara "Bankväsende". För att skapa modellen för domänen brukar man prata med domänexperter, som vet hur området fungerar.

I domänmodellen brukar man sen kunna identifiera substantiv - t.ex Account och Transfer om domänen är "Bankväsende", eller Student, Grade och Lesson om domänen är "Skola och betygssättning" (tänk SchoolSoft), eller Song och Artist om domänen är "musik" (tänk Spotify). Dessa substantiv kan sen bli klasser när man implementerar modellen i kod.

Om man vidare i domänmodellen ser att det finns vissa verb som är kopplade till ett substantiv kan detta sen bli metoder på substantivets klass.

Om domänen är "musik" kanske domänexperten säger "You can play a song", vilket indikerar att det ska finnas en play-instans-metod på Song-klassen.

Om domänen är "Skola och betygssättning" kanske domänexperten säger "a student can enroll in a course", vilket indikerar att det ska finnas en enroll-metod på antingen Student- eller Course-klassen.

2. Dokumentation

I Programmering 1 förväntades ni skapa funktioner för grundläggande funktionalitet (t.ex sorteringsalgoritmer) själva.

I kursplanen för Programmering 2 står det att ni ska kunna använda språkets inbyggda klasser och metoder (dvs, istället för att skriva en egen sorteringsalgoritm bör ni använda Array-klassens #sort-metod)

Ett språks inbyggda klasser och/eller metoder brukar kallas för dess standardbibliotek.

Rubys standardbibliotek är enormt och det är i det närmaste omöjligt för någon att kunna allt utantill.

Ni behöver därför kunna hitta och läsa dokumentationen för Rubys standardbibliotek.

Rubys standardbibliotek finns dokumenterat på ruby-doc.org

Rubys standardbibliotek är helt uppbyggt kring klasser (eftersom Ruby är ett objektorienterat språk). För att hitta i dokumentationen måste man alltså veta namnet på klassen man vill läsa på om.

För att hitta dokumentationen är det enklaste att använda en sökmotor som Google.

I google är det bäst att skriva klassensNamn -site:ruby-doc.org

(-site:ruby-doc.org) gör att man endast får träffar från just ruby-doc.org

När man väl hittat dokumentationen behöver man förstå hur man ska läsa dokumentationssidan:

string documentation 1
String-klassen på ruby-doc.org

I listan till vänster syns alltså alla metoder som är definerade på klassen. I String-klassens fall är det väldigt många. Alla klasser har inte fullt så många, men det finns säkert några som har ännu fler metoder.

  • Metoder som börjar med :: (som ::new) är klass-metoder.

  • Metoder som börjar med # (som #chomp) är instans-metoder

Klickar man på en metod i listan kommer man till dokumentationen för metoden:

string documentation 2
String#chomp på ruby-doc.org

Ofta skummar jag bara beskrivningen och hoppar direkt till exemplen - det är ofta lättare att förstå för mig.

3. Klasser, objekt och metoder

Det finns många begrepp i objektorienterad programmering, och alla begrepp är sammankopplade. Därför är det svårt att förklara begreppen utan att nämna begrepp som man inte introducerat än.

Det är därför helt förståeligt om du blir frustrerad när du läser nedanstående avsnitt som ofta refererar till och använder begrepp som inte förklarats än. Förhoppningen är att ni, när ni läst hela kapitlet ska kunna läsa om det med förnyad förståelse.

3.1. Klass

Klasser skapas med class-nyckelordet, följt av namnet på klassen. - obs, klassens namn måste börja med en versal (stor bokstav) och använder PascalCase

Example 3. En (tämligen menlös) Person-klass
1
2
3
class Person (1)

end
1 Klassdefinition. Avslutas med end på rad 3.

3.2. Objekt/Instans

Ett objekt är en instans av en klass. Visst är det en hjälpfull förklaring?

Förenklat kan man säga att allt du kan lagra i en variabel i Ruby är ett objekt (en instans) av en klass.

En liknelse kan vara att när man bygger ett hus utgår man från en ritning. Ritningen skulle i så fall vara en klass, och huset som byggs en instans av klassen. Och på samma sätt som man från en ritning kan bygga flera liknande hus kan man från en klass skapa flera liknande objekt.

Example 4. Några Objekt
1
2
3
4
number = 42 (1)
word = "forty-two" (2)
a_big_number = 9001 (3)
an_array_of_stuff = [3,1,2,4] (4)
1 number tilldelas 42 - som är en instans av klassen Integer
2 word tilldelas en sträng, dvs en instans av klassen String
3 a_big_number tilldelas en annan instans av klassen Integer
4 an_array_of_stuff tilldelas en instans av klassen Array innehållandes 4 instanser av klassen Integer.

3.3. Metoder

En metod är en funktion som är definerad i en klass.

Example 5. En metod
1
2
3
4
5
6
7
8
class Person

  def greet (1)
    "Hello, my name is #{@name}"
  end

end
1 Funktionen greet är definerad inne i klassen Person och är alltså inte egentligen en funktion, utan en metod.

3.3.1. Konstruktor

För att kunna skapa instanser/objekt av en klass används en speciell metod som kallas för konstruktor (eftersom den konstruerar objektet). I Ruby heter konstruktorn alltid initialize.

För att använda konstruktorn anropar man förvirrande nog metoden new på klassen, inte initialize. Detta beror på att new bland annat allokerar någonstans i minnet att lagra objektet först, innan initialize kan anropas.

Vissa vanliga klasser (t.ex. Integer, String, Array kan skapas genom så kallade literals, som tex x = 1, word = "word", numbers = [1,2,3])

För att lagra data i ett objekt används instansvariabler. Instansvariabler har ett @ före variabelns namn - tänk attribut (@ribut)

Till skillnad från vanliga variabler som försvinner när metoden körts finns datan i en instansvariabel kvar så länge objektet finns kvar. Man kan därför komma åt samma instansvariabel från olika metoder utan att behöva skicka runt dem manuellt.

Example 6. En (något mindre menlös) Person-klass
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Person

  def initialize(name) (1)
    @name = name (2)
  end

end

person1 = Person.new('Agaton Sax') (3)
=> #<Person:0x000000010467b1a0 @name="Agaton Sax"> (4)
person2 = Person.new('Ture Sventon') (5)
=> #<Person:0x000000010463a880 @name="Ture Sventon"> (6)

person1 == person2 #=> false (7)
1 Konstruktor - den här konstruktorn tar ett argument (name).
2 @name är en instansvariabel som sparar värdet av name internt i objektet.
3 Genom att anropa Person.new, och skicka in ett namn, anropas konstruktorn (initialize) och en instans/objekt av klassen skapas och tilldelas variabeln person1.
4 En representation av person1-objektet. Först står klassen, sen ett unikt id (minnesadress) och sen följer instansvariablerna
5 En annan instans av Person-klassen skapas och tilldelas variabeln person2
6 En representation av person2-objektet. Observera hur minessadress och instansvariabel skiljer sig från person1-objektet.
7 Jämförelsen visar att de är två olika instanser (även om de är av samma klass).

3.3.2. Instansmetod

Instansmetoder är metoder som är definerade på "instans-nivå". Detta innebär att man behöver skapa instanser av klassen för att interagera med metoden. Instansmetoder har tillgång till objektets olika attribut och andra instansmetoder.

Genom att använda instanser, instansmetoder och instansvariabler kan man på ett smidigt sätt spara data (attribut) och metoder att hämta, spara, eller bearbata denna data på stamma ställe i kodbasen.

Example 7. En marginellt användbar Contact-klass med konstruktor och instansmetoder
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Contact

  def initialize(name) (1)
    @name = name  (2)
    @phone_numbers = [] (2)
  end

  def add_phone_number(new_number) (3)
    @phone_numbers << new_number
  end

  def delete_phone_number(number) (3)
    @phone_numbers.delete {|num| num == number}
  end

  def list_numbers (2)
    puts "#{@name}:"
    @phone_numbers.each do |number|
      puts "\t#{number}"
    end
  end

end

c1 = Contact.new("Hermione Granger") (4)
=> #<Contact:0x0000000104bd7ba8 @name="Hermione Granger", @phone_numbers=[]>
c2 = Contact.new("Ron Weasly") (4)
=> #<Contact:0x0000000104cc58a8 @name="Ron Weasly", @phone_numbers=[]>

c1.add_phone_number("555-123 45 67") (5)
c1.add_phone_number("555-765 43 21") (5)
c2.add_phone_number("555-123 45 67") (5)

c1.list_numbers (6)
Hermione Granger
  555-123 45 67
  555-765 43 21

c2.list_numbers (6)
Ron Weasly
  555-123 45 67
1 Konstruktor.
2 Instansvariabler
3 Instansmetoder - kan interagera med instansvariabler
4 Två olika instanser skapas
5 Anrop till instansmetoden add_number på de olika objekten.
6 Anrop till instansmetoden list_numbers

3.3.3. Klassmetod

Klassmetoder är metoder som är definerade på "klass-nivå". Detta innebär att man inte behöver skapa instanser av klassen för att interagera med metoden. Klassmetoder har inte tillgång till klassens olika instanser eller deras metoder eller attribut.

Klassmetoder används när det inte är vettigt att först skapa en instans för att använda en metod, och kan primärt ses som ett sätt att organisera kod. Se tillexempel Math.sin eller Time.now.

För att visa att något är en klassmetod brukar man skriva :: före metodens namn i dokumentation (Math::sin, Time::now)

För att skapa en klassmetod skriver man def self.metodens_namn

Example 8. En Greeting-klass med två klassmetoder
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Greeting

  def self.good (1)
    "Hello, what a wonderful day"
  end

  def self.bad (1)
    "What do you want?"
  end
end

puts Greeting.good #=> "Hello, what a wonderful day" (2)
puts Greeting.bad #=> "What do you want?" (2)
1 Definition av klassmetoderna good och bad.
2 Anrop av klassmetoderna. Observera att ::new aldrig anropas.

4. Inkapsling

Inkapsling är ett sätt att förhindra att utvecklare som använder klasser och objekt på fel sätt genom att göra så att klassens attribut och utvalda metoder enbart kan nås inifrån metoder definerade i klassen.

Ett system för att sälja kaffe utan inkapsling. Personen som ska köpa kaffe får själv fri tillgång till växelkassan, och det är upp till köparen att se till att de betalar, betalar rätt belopp, och att de får rätt växel tillbaka, och att de tar rätt mängd kaffe. Det är lätt att det blir fel - för både köpare och säljare.

Ett system för att sälja varor med inkapsling.

Varorna är fysiskt inkapslade, och det enda sättet att komma åt varorna är genom det gränssnitt (knappar, betalningssystem, luckor etc) som finns på maskinen. Det är inte möjligt att komma åt varorna på något annat sätt (åtminstone inte utan att medvetet bryta mot reglerna).

På ett liknande sätt som varuautomaten ovan behöver skyddas från att användare använder den på fel sätt, behöver programmerare skydda sina klasser från att användas på fel sätt.

I Ruby kan ett objekts attribut (instansvariabler) endast kommas åt (läsas eller bearbetas) av instansmetoder definerade i klassen. Man brukar säga att instansvariablerna är privata medans instansmetoderna är publika. Detta är ett exempel på inkapsling.

Example 9. Instansvariabler är privata
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Person

  def initialize(name)
    @name = name
  end

end

person = Person.new("Agaton Sax") (1)
#<Person:0x0000000106a388e0 @name="Agaton Sax">
puts person.name (2)
# => undefined method `name' for #<Person:0x0000000106a388e0 @name="Agaton Sax"> (NoMethodError) (3)
1 En instans av klassen Person skapas. Instansvariabeln @name sätts till "Agaton Sax".
2 Ett försök att komma åt instansvariabeln @name på objektet person. Detta kommer att ge ett felmeddelande.
3 Felmeddelandet säger att det inte finns någon metod som heter name på objektet person.

4.1. Getters och Setters

Eftersom instansvariabler är privata, och instansmetoder är publika, innebär det, att om vi vill komma åt instansvariablerna, måste skapa publika metoder som gör det. En getter är en metod som hämtar värdet av en instansvariabel, och en setter är en metod som sätter värdet av en instansvariabel.

Getters och setters har inga speciella egenskaper, de är bara vanliga instansmetoder som "råkar" returnera eller ändra instansvariabler med samma namn som metoderna.

Example 10. En klass med en getter
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Person

  def initialize(name)
    @name = name
  end

  def name (1)
    @name
  end

end

person = Person.new("Agaton Sax") (2)
#<Person:0x0000000106a388e0 @name="Agaton Sax">
puts person.name #=> "Agaton Sax" (3)
1 instansmetoden name är en getter då den hämtar instanvariabeln @name.
2 En instans av klassen Person skapas. Instansvariabeln @name sätts till "Agaton Sax".
3 Ett anrop till gettern name
Example 11. En klass med en setter
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Person

  def initialize(name)
    @name = name
  end

  def name=(new_name) (1)
    @name = new_name
  end

end

person = Person.new("Agaton Sax") (2)
#<Person:0x0000000106a388e0 @name="Agaton Sax">
puts person.name = "Ture Sventon" (3)
1 instansmetoden name= är en setter då den modifierar instanvariabeln @name.
2 En instans av klassen Person skapas. Instansvariabeln @name sätts till "Agaton Sax".
3 Ett anrop till settern name=, vilket kommer modifiera instansvariabeln @name.

Det går inte att ändra på värdet av en instansvariabel med en getter.

Example 12. Getters kan bara hämta värden
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Person

  def initialize(name)
    @name = name
  end

  def name
    @name
  end

end

person = Person.new("Agaton Sax")
person.name = "Ture Sventon" (1)
#=> undefined method `name=` for #<Person:0x0000000104750580 @name="Agaton Sax"> (NoMethodError) (2)
name = person.name (3)
name = "Ture Sventon" (3)
puts person.name => "Agaton Sax" (4)
1 Ett försök att använda gettern name för att ändra på instansvariablen
2 Felmeddelandet säger att det inte finns någon setter
3 Ett försök att "lura" gettern
4 Det gick inte att lura gettern

Setters kan bara ändra värden, inte hämta dem

Example 13. Setters kan bara ändra värden
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Person

  def initialize(name)
    @name = name
  end

  def name=(new_name)
    @name = new_name
  end

end

person = Person.new("Agaton Sax")

puts person.name (1)
#=> p.rb:15:in `<main>': undefined method `name' for #<Person:0x0000000106c301e8 @name="Agaton Sax"> (NoMethodError) (2)
1 Ett försök att använda settern name= för att hämta värdet av instansvariabeln @name
2 Felmeddelandet säger att det inte finns någon getter

I många språk (exempelvis Java) döps getters till get<<AttributetsNamn>> och setters till set<<AttributetsNamn>>. I Ruby är namngivningsstandarden <<attributets_namn>> för getters och <<attributet_namn>>= för setters.

I ruby finns det tre macron som skapar getters och setters automatiskt:

  • attr_reader skapar getters.

  • attr_writer skapar setters.

  • attr_accessor skapar både getters och setters.

Example 14. De olika attr-macrona
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Contact

  attr_reader :name, :email (1)
  attr_writer :name (2)
  attr_accessor :phone_number (3)

  def initialize(name, email, phone_number)
    @name = name
    @email = email
    @phone_number = phone_number
  end

end
c = Contact.new("Agaton Sax", "agaton@sax.nu", "123 45")
p c.name (4)
c.name = "Ture Sventon" (5)
c.phone_number = "123 46" (6)
p c.phone_number (7)
1 Skapar en getter för name och email.
2 Skapar en setter för name.
3 Skapar en getter och en setter för phone_number.
4 Använder den autogenererade gettern name
5 Använder den autogenererade settern name=
6 Använder den autogenererade settern phone_number=
7 Använder den autogenererade gettern phone_number

När man skapar sina klasser är det viktigt att fundera på vilka attribut som ska vara publika (med getters och/eller setters), och vilka som ska vara privata.

4.2. Validering och logik

Ofta vill man att en getter eller setter ska innehålla validering eller annan logik. Då kan man inte använda de olika attr_xxx macrona för att skapa dem, utan måste koda dem för hand.

Example 15. Validering/logik i en setter
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Highscore

  def initialize
    @score = 0
  end

  def score=(new_score)
    if new_score > @score (1)
      @score = new_score
    end
  end
end

hs = Highscore.new
hs.score = 100 (2)
hs.score = 50 (3)
1 Settern ändrar enbart värdet om det nya värdet är större än det gamla
2 Valideringen i settern tillåter ändringen
3 Valideringen i settern hindrar ändringen
Example 16. Logik i en getter
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class BankAccount

  def initialize(oren)
    @balance = oren
  end

  def balance
    @balance / 100.0 (1)
  end

end
ba = BankAccount.new(1000)
p ba.balance => 10.0
1 Gettern omvandlar värdet till kronor och ören

5. Arv

Arv är ett sätt att återanvända kod. Genom att skapa subklasser av en befintlig klass får subklassen tillgång till superklassens attribut och metoder. Det går sen att modifiera ärvda metoder och eller lägga till nya (som inte är tillgängliga i superklassen).

Example 17. Arv i Ruby
1
2
3
4
5
6
7
class A (1)
  ... (2)
end

class B < A (3)
  ... (4)
end
1 Vanlig klassdefinition
2 Resten av klassens metoder etc
3 < anger att klassen ärver från en annan klass. I detta fall ärver klassen B från klassen A.
4 Eventuella metoder som klassen B ska ha, som inte finns i klassen A, eller som skiljer sig i hur motsvarande metod fungerar från klassen A.

Om klassen Student ärver från klassen Person så kan man säga att en Student är en Person.

Example 18. Arv är en är-en-relation mellan två klasser.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Person
  ...
end

class Student < Person (1)
end

person1 = Person.new
student1 = Student.new

puts person1.class #=> Person
puts student1.class #=> Student

puts person1.is_a?(Person) #=> true (2)
puts student1.is_a?(Person) #=> true (3)
1 Student är en Person.
2 Metoden is_a? returnerar true om objektet är av klassen som skickas in som argument, eller en av dess subklasser.
3 Student är en Person.

I Ruby ärver alla klasser från klassen Object (om du inte anger någon annan superklass). Det är därför alla objekt har tillgång till metoder som #is_a?, #class. och #methods - de är nämligen definerade i klassen Object. Se Object på ruby-doc.org.

Example 19. Subklasser ärver metoder från superklasser
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class A

  def hello
      "Hello"
  end

end


class B < A

end

b = B.new
puts b.hello #=> "Hello" (1)
1 Klassen B ärver instansmetoden #hello från klassen A.
Example 20. Subklasser kan modifiera ärvda metoder
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class A

  def hello
      "Hello from A"
  end

end

class B < A

  def hello (1)
      "Hello from B"
  end

end

a = A.new
b = B.new
puts a.hello #=> "Hello from A"
puts b.hello #=> "Hello from B" (1)
1 Klassen B modifierar den ärvda instansmetoden #hello från klassen A.
Example 21. Subklasser kan definera egna metoder som inte finns i superklassen
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class A
end

class B < A

  def hello (1)
      "Hello from B"
  end

end

a = A.new
b = B.new
puts a.hello #=> undefined method `hello' for #<A:0x00000001067585a8> (NoMethodError) (2)
puts b.hello #=> "Hello from B" (3)
1 B definerar instansmetoden #hello
2 A har inte instansmetoden #hello.
3 B har instansmetoden #hello.
Example 22. Nyckelordet super låter subklasser anropa motsvarande metod i subperklassen, och eventuellt utveckla den
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class SmallCat

  def speak
      "meow"
  end

end

class LargeCat < SmallCat

  def speak (1)
      super.upcase + "!" (2)
  end

end

small_cat = SmallCat.new
large_cat = LargeCat.new
small_cat.speak #=> "meow"
large_cat.speak #=> "MEOW!"
1 LargeCat modifierar den ärvda instansmetoden #speak från klassen SmallCat.
2 super anropar metoden #speak i klassen SmallCat.

super anropar i superklassen metoden med samma namn som den aktuella metoden. Om metoden heter #speak så anropas #speak i superklassen. Om metoden heter #hello så anropas #hello i superklassen.

Genom att kombinera alla de ovanstående "verktygen" arv ger oss kan man snabbt skapa varianter av klasser som fungerar liknande, med minimalt antal rader kod som behöver skrivas.

Example 23. Arv i ett spel(?)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Orc

  attr_reader :name

  def initialize(health = rand(10..20), name="Orc")
    @health = health
    @name = name
  end

  def attack
    puts "#{@name} Attacked!"
  end

  def growl
    puts "#{@name} growled!"
  end

  def alive?
    @health > 0
  end

end

class OrcWarrior < Orc (1)

  def initialize(health = rand(20..50), name="Orc Warrior")) (2)
    super(health, name) (3)
  end

  def war_cry (4)
    puts "WAAAAAAAGH!"
  end

  def growl (5)
    puts "#{@name} growled in a menacing way!"
  end

end

orc = Orc.new
orc_warrior = OrcWarrior.new

orc_warrior.attack  #=> "Orc Warrior Attacked!" (6)

orc_warrior.war_cry #=> "WAAAAAAAGH!"
orc.war_cry #=> NoMethodError: undefined method `war_cry' for #<Orc:0x007f9b0a0b8c18> (7)

orc.growl #=> "Orc growled!" (5)
orc_warrior.growl #=> "Orc Warrior growled in a menacing way!" (5)
1 OrcWarrior är en subklass till Orc. Orc är alltså en superklass till OrcWarrior.
2 OrcWarrior har en egen konstruktor, med andra standardvärden.
3 super låter subklassen anropa superklassens metod med samma namn (som super anropas i). I detta fall initialize.
4 Subklasser kan definera nya metoder som inte finns i superklassen.
5 Subklasser kan modifiera metoder som finns i superklassen. I detta fall growl.
6 OrcWarrior har tillgång till alla metoder definerade i Orc eftersom den ärver från den.
7 Superklasser har inte tillgång till metoder definerade i subklasser.

6. Klassdiagram

För att snabbt kunna visualisera vad en klass har för metoder och attribut, och vad den har för relationer till andra klasser kan man använda klassdiagram

Klassdiagram består av en rektangel med tre rutor.

Den översta rutan innehåller klassens namn.

Nästa ruta innehåller klassens attribut.

Den tredje rutan innehåller klassens metoder.

I exemplet ovan har vi en klass som heter Orc, och den har attributen name och health. health är privat, men name är publik (i kod innebär det antagligen att name har en getter, men det har inte health)

Orc har också följande metoder: initialize (konstruktorn) som tar två argument, och attack, growl och alive?.

Klassen har också en relation till klassen OrcWarrior, och det visas med en pil från OrcWarrior till Orc. Detta betyder att en OrcWarrior ärver från Orc.

Klassdiagram är bra när man vill förmedla hur en klass ska se ut, eller när man vill dokumentera ett befintligt system.

Ett klassdiagram över rubys inbyggda klasser
Ett klassdiagram över rubys inbyggda klasser (utan metoder och attribut)

7. Ordlista

arv

Ett sätt att återanvända kod genom att skapa subklasser. En klass som ärver från en annan klass ärver dess metoder och attribut.

attribut

Data som lagras i en instans, skapas med instansvariabler (variabler som börjar med @, t.ex @phone_number).

camelCase

Ett sätt att namnge kod (metoder/klasser/variabler etc), där första ordet i namnet börjar med en gemen, och om namnet innehåller flera ord börjar följande ord med en versal points, healthPoints, currentUser. Används inte i Ruby (men i t.ex javascript).

duck typing

Används i dynamiskt typade språk. Om en variabel har de efterfrågade metoderna på sig, spelar det då någon roll vad det är för klass? "If it walks like a duck and quacks like a duck - then it must be a duck"..

getter

En metod som returnerar värdet på en instansvariabel

metod

En funktion som är definerad inne i en klass.

instansmetod

En metod som anropas på instanser av klassen. Anropas genom instansens_variabel.metodens_namn. Skapas genom def metodens_namn.

instansvariabel

Se attribut.

kebab-case

Ett sätt att namnge kod (metoder/klasser/variabler etc), där varje ord i namnet börjar med en gemen, och om namnet innehåller flera ord länkas de ihop med bindestreck (-). T.ex velocity, current_user, total_time_in_seconds. Används inte i Ruby (men i t.ex css).

klass

En konstruktion i objektorienterade språk som låter dig skapa en mall för objekt eller samla metoder under ett och samma "tak".

klassmetod

En metod som är definerad på klassen. Anropas genom KlassensNamn.metodens_namn. Skapas genom def self.metodens_namn.

konstruktor

En speciell metod som används när man skapar instanser/objekt av en klass. Anropas genom KlassensNamn.new.

macro

Kod som genererar kod.

nyckelord

Ett ord som är reserverat och betyder något speciellt i programmeringsspråket, och därför inte kan användas till något annat. T.ex class, if, end, def.

PascalCase

Ett sätt att namnge kod (metoder/klasser/variabler etc), där varje ord i namnet börjar med en versal. T.ex User, BankAccount eller HTTPRequest. Används i Ruby för klassnamn.

setter

En metod som kan ändra på en instansvariabels värde.

snake_case

Ett sätt att namnge kod (metoder/klasser/variabler etc), där varje ord i namnet börjar med en gemen, och om namnet innehåller flera ord länkas de ihop med understreck (_). T.ex amount, current_user, total_time_in_seconds. Används i Ruby för variabler och metoder.

subklass

En klass som ärver från en annan klass. T.ex class Student < Person gör att klassen Student ärver från klassen Person.

superklass

En klass som andra klasser ärver från. T.ex kan Person vara en superklass till klassen Student.

state

Alla variabler och värdet på dessa vid ett givet tillfälle under tiden programmet körs.