Next Previous Contents

6. Design og arkitektur

6.1 Navnerom og moduler

Moduler kan fungere som navnerom (eng: namespace) i tillegg til multippel arv av funksjonalitet.

  1| # Moduler som navnerom
  2| module Text
  3|   class Parser ; end
  4|   module SGML
  5|     class Parser ; end
  6|     module HTML
  7|       class Parser ; end
  8|     end
  9|     module XML
 10|       class Parser ; end 
 11|     end
 12|   end
 13| end
 14| 
 15| # Bruke skop-operatoren
 16| htmlparser = Text::SGML::HTML::Parser.new
 17| 
 18| # Åpne spesifikke navnerom
 19| class Text::SGML::HTML::Parser
 20|   def non_breakable_space
 21|     " "
 22|   end
 23| end
 24| 
 25| # Hente inn navnerom
 26| include Text::SGML
 27| xmlparser = XML::Parser.new

Du kan også bruke klasser som navnerom i de fleste tilfeller, da (Class.ancestors.include? Module) == true, men du kan ikke include en klasse, da Ruby ikke har multippel arv fra klasser.

6.2 Singleton

Singleton-metoder eksisterer kun på et objekt. Dette er gjerne klassemetoder, slik som new.

  1| class EnKlasse
  2|   def self.metode   # Klassemetoder er singleton metoder.
  3|   end
  4| end
  5| 
  6| arr = ['usr', 'local', 'bin']     # Nesten alle objekter kan få 
  7| def arr.to_str                    # kan få singleton metoder
  8|   sep = File::SEPARATOR           # lagt til seg når som helst.
  9|   File::SEPARATOR + self.join(sep)   
 10| end
 11| puts arr.to_str
 12| 
 13| class <<arr              # Du kan også åpne et objekts
 14|   def write( *arg )      # singleton-klasse for å definere
 15|     self.push( *arg )    # metoder.
 16|   end
 17| end
 18| arr.write "irb"
 19| puts arr.to_str

Går vi dypere i metahierarkiet til Ruby, finne vi at singleton-metodene er definert i singleton-klasser.

  1| class MinKlasse
  2|   def self.en_metode   # Klassemetoder kan også defineres...
  3|     puts "en"
  4|   end
  5|   class <<self         # ... i klassens singleton klasse.
  6|     def annen_metode   
  7|       puts "annen"
  8|     end
  9|   end
 10| end
 11| 
 12| MinKlasse.annen_metode #=> "annen"

Singleton-klasser er et "usynlig" mellomledd mellom et objekt og dets klasse, hvor singleton-metodene havner.

(Forventet du noe om et design pattern kalt Singleton her? Titt på modulen singleton i standardbiblioteket.)

6.3 Duck typing

"If it walks like duck..."

Ruby benytter seg av en klar distinksjon mellom klasse og type. Duck typing vil si at det meldingene et objekt reagerer på som angir typen, og ikke klassen.

  1| def list_filer( sti )
  2|   Dir.open( sti ) do |dir|       # Dir#open forventer en String,
  3|     puts dir.entries.join(' ')   # men benytter duck typing.
  4|   end
  5| end
  6| 
  7| class KatalogSti < Array  
  8| end
  9| 
 10| sti = KatalogSti.new
 11| sti << "usr" << "local" << "bin"
 12| 
 13| begin
 14|   list_filer( sti )             # Gir konverteringsfeil:
 15| rescue TypeError=>err
 16|   puts err              
 17| end
 18| 
 19| class KatalogSti                 
 20|   def to_str                    # to_str er en metode som angir
 21|     sep = File::SEPARATOR       # at et objekt kan benyttes som
 22|     sep + self.join(sep)        # en String.
 23|   end
 24| end
 25| 
 26| list_filer( sti )               # Da kan KatalogSti brukes...

6.4 Moduler før klasser

Vi kan bare arve fra en klasse om gangen. Dermed er ofte moduler et bedre valg for å samle funksjonalitet som deles. Dette gjelder spesielt dersom den delte funksjonaliteten konseptuelt ikke utgjør en superklasse.

  1| module NyttigDill
  2|   def foo; "foo"; end
  3| end
  4| module NyttigDall
  5|   def foo; "bar"; end
  6| end
  7| 
  8| class DillDall
  9|   include NyttigDill   # Multippel arv av
 10|   include NyttigDall   # funksjonalitet.
 11| end
 12| puts DillDall.new.foo  #=> "bar"
 13| 
 14| class DillDall
 15|   extend NyttigDill    # Utvider klasseobjektet.
 16| end
 17| puts DillDall.foo      #=> "foo"

 

6.5 Spesiell flytkontroll

throw & catch

For de ganger hvor man ønsker å nøste opp kallstakken og det ikke er et unntak, kan man bruke throw/catch.

  1| def som_kaster( level = 0 )
  2|   if level > 0
  3|     som_kaster( level - 1 )
  4|   else
  5|     throw :bunnen_truffet, level
  6|   end
  7|   puts level # blir aldri kalt
  8| end
  9| 
 10| level = catch(:bunnen_truffet) do
 11|   som_kaster( 10 )
 12| end
 13| puts level

Continuation og callcc

Continuations omtales ofte som kontrollert goto, men er likevel et vanskelig konsept å vri hjernen rundt.

  1| def callcc_eksempel
  2|   callcc{|cont|
  3|     puts "Returnerer Continuation"
  4|     return cont
  5|   }
  6|   puts "Avslutter metoden."
  7|   nil
  8| end
  9| 
 10| cont = callcc_eksempel
 11| puts "Ferdig?."
 12| cont.call if cont

Et mer komplisert eksempel fra Dave Thomas (med kodemassasje gjort at Avi Bryant)

  1| def with_context(params)
  2|   k, name = catch(:context) {yield; return}
  3|   k.call(params[name] || find_in_context(name))
  4| end
  5| def find_in_context(name)
  6|   callcc{|k| throw(:context, [k, name])}
  7| end
  8| 
  9| def update_widget
 10|   name = find_in_context(:name)
 11|   color = find_in_context(:color)
 12|   puts "<#{color}>#{name}</#{color}>"
 13| end
 14| 
 15| with_context(   :name => 'dave', :color => 'red' ) do
 16|   with_context( :name => 'avi',  :color => 'green' ) do
 17|     update_widget               #=> "<green>avi</green>"
 18|   end
 19|   update_widget                 #=> "<red>dave</red>"
 20| end

 


Next Previous Contents