Ruby - Object Oriented

Ruby è un puro linguaggio orientato agli oggetti e tutto appare a Ruby come un oggetto. Ogni valore in Ruby è un oggetto, anche le cose più primitive: stringhe, numeri e anche vero e falso. Anche una classe stessa è un oggetto che è un'istanza della classe Class . Questo capitolo ti guiderà attraverso tutte le principali funzionalità relative a Object Oriented Ruby.

Una classe viene utilizzata per specificare la forma di un oggetto e combina la rappresentazione dei dati e i metodi per manipolare tali dati in un unico pacchetto pulito. I dati e i metodi all'interno di una classe sono chiamati membri della classe.

Ruby Class Definition

Quando si definisce una classe, si definisce un progetto per un tipo di dati. Questo in realtà non definisce alcun dato, ma definisce il significato del nome della classe, ovvero, di cosa sarà composto un oggetto della classe e quali operazioni possono essere eseguite su tale oggetto.

Una definizione di classe inizia con la parola chiave class seguito dal class name ed è delimitato da un end. Ad esempio, abbiamo definito la classe Box utilizzando la classe di parole chiave come segue:

class Box
   code
end

Il nome deve iniziare con una lettera maiuscola e per convenzione i nomi che contengono più di una parola vengono eseguiti insieme a ciascuna parola in maiuscolo e senza caratteri di separazione (CamelCase).

Definisci oggetti Ruby

Una classe fornisce i progetti per gli oggetti, quindi fondamentalmente un oggetto viene creato da una classe. Dichiariamo gli oggetti di una classe usandonewparola chiave. Le seguenti istruzioni dichiarano due oggetti della classe Box -

box1 = Box.new
box2 = Box.new

Il metodo di inizializzazione

Il initialize method è un metodo di classe Ruby standard e funziona quasi allo stesso modo di constructorfunziona in altri linguaggi di programmazione orientati agli oggetti. Il metodo initialize è utile quando si desidera inizializzare alcune variabili di classe al momento della creazione dell'oggetto. Questo metodo può richiedere un elenco di parametri e come qualsiasi altro metodo Ruby sarebbe preceduto dadef parola chiave come mostrato di seguito -

class Box
   def initialize(w,h)
      @width, @height = w, h
   end
end

Le variabili di istanza

Il instance variablessono una specie di attributi di classe e diventano proprietà degli oggetti una volta che gli oggetti vengono creati utilizzando la classe. Gli attributi di ogni oggetto vengono assegnati individualmente e non condividono alcun valore con altri oggetti. Sono accessibili utilizzando l'operatore @ all'interno della classe ma per accedervi al di fuori della classe che usiamopublic metodi, che vengono chiamati accessor methods. Se prendiamo la classe sopra definitaBox quindi @width e @height sono variabili di istanza per la classe Box.

class Box
   def initialize(w,h)
      # assign instance variables
      @width, @height = w, h
   end
end

I metodi di accesso e setter

Per rendere le variabili disponibili dall'esterno della classe, devono essere definite all'interno accessor methods, questi metodi di accesso sono noti anche come metodi getter. L'esempio seguente mostra l'utilizzo dei metodi di accesso:

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # accessor methods
   def printWidth
      @width
   end

   def printHeight
      @height
   end
end

# create an object
box = Box.new(10, 20)

# use accessor methods
x = box.printWidth()
y = box.printHeight()

puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

Quando il codice sopra viene eseguito, produce il seguente risultato:

Width of the box is : 10
Height of the box is : 20

Simile ai metodi di accesso, che vengono utilizzati per accedere al valore delle variabili, Ruby fornisce un modo per impostare i valori di tali variabili dall'esterno della classe utilizzando setter methods, che sono definiti come di seguito -

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # accessor methods
   def getWidth
      @width
   end
   def getHeight
      @height
   end

   # setter methods
   def setWidth=(value)
      @width = value
   end
   def setHeight=(value)
      @height = value
   end
end

# create an object
box = Box.new(10, 20)

# use setter methods
box.setWidth = 30
box.setHeight = 50

# use accessor methods
x = box.getWidth()
y = box.getHeight()

puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

Quando il codice sopra viene eseguito, produce il seguente risultato:

Width of the box is : 30
Height of the box is : 50

I metodi di istanza

Il instance methods sono anche definiti nello stesso modo in cui definiamo qualsiasi altro metodo utilizzando defparola chiave e possono essere utilizzati utilizzando un'istanza di classe solo come mostrato di seguito. La loro funzionalità non si limita all'accesso alle variabili di istanza, ma possono anche fare molto di più secondo le tue esigenze.

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # instance method
   def getArea
      @width * @height
   end
end

# create an object
box = Box.new(10, 20)

# call instance methods
a = box.getArea()
puts "Area of the box is : #{a}"

Quando il codice sopra viene eseguito, produce il seguente risultato:

Area of the box is : 200

I metodi e le variabili della classe

Il class variablesè una variabile, che è condivisa tra tutte le istanze di una classe. In altre parole, esiste un'istanza della variabile a cui si accede dalle istanze dell'oggetto. Le variabili di classe sono precedute da due caratteri @ (@@). Una variabile di classe deve essere inizializzata all'interno della definizione di classe come mostrato di seguito.

Un metodo di classe viene definito utilizzando def self.methodname(), che termina con il delimitatore di fine e verrebbe chiamato utilizzando il nome della classe come classname.methodname come mostrato nell'esempio seguente:

#!/usr/bin/ruby -w

class Box
   # Initialize our class variables
   @@count = 0
   def initialize(w,h)
      # assign instance avriables
      @width, @height = w, h

      @@count += 1
   end

   def self.printCount()
      puts "Box count is : #@@count"
   end
end

# create two object
box1 = Box.new(10, 20)
box2 = Box.new(30, 100)

# call class method to print box count
Box.printCount()

Quando il codice sopra viene eseguito, produce il seguente risultato:

Box count is : 2

Il metodo to_s

Qualsiasi classe definita dovrebbe avere un'estensione to_smetodo di istanza per restituire una rappresentazione di stringa dell'oggetto. Di seguito è riportato un semplice esempio per rappresentare un oggetto Box in termini di larghezza e altezza:

#!/usr/bin/ruby -w

class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # define to_s method
   def to_s
      "(w:#@width,h:#@height)"  # string formatting of the object.
   end
end

# create an object
box = Box.new(10, 20)

# to_s method will be called in reference of string automatically.
puts "String representation of box is : #{box}"

Quando il codice sopra viene eseguito, produce il seguente risultato:

String representation of box is : (w:10,h:20)

Controllo di accesso

Ruby offre tre livelli di protezione a livello di metodi di istanza, che possono essere public, private, or protected. Ruby non applica alcun controllo di accesso sulle variabili di istanza e di classe.

  • Public Methods- I metodi pubblici possono essere chiamati da chiunque. I metodi sono pubblici per impostazione predefinita ad eccezione di initialize, che è sempre privato.

  • Private Methods- Non è possibile accedere ai metodi privati ​​o persino visualizzarli dall'esterno della classe. Solo i metodi della classe possono accedere ai membri privati.

  • Protected Methods- Un metodo protetto può essere invocato solo dagli oggetti della classe di definizione e dalle sue sottoclassi. L'accesso è mantenuto all'interno della famiglia.

Di seguito è riportato un semplice esempio per mostrare la sintassi di tutti e tre i modificatori di accesso:

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # instance method by default it is public
   def getArea
      getWidth() * getHeight
   end

   # define private accessor methods
   def getWidth
      @width
   end
   def getHeight
      @height
   end
   # make them private
   private :getWidth, :getHeight

   # instance method to print area
   def printArea
      @area = getWidth() * getHeight
      puts "Big box area is : #@area"
   end
   # make it protected
   protected :printArea
end

# create an object
box = Box.new(10, 20)

# call instance methods
a = box.getArea()
puts "Area of the box is : #{a}"

# try to call protected or methods
box.printArea()

Quando il codice precedente viene eseguito, produce il seguente risultato. Qui, il primo metodo è stato chiamato con successo ma il secondo metodo ha dato un problema.

Area of the box is : 200
test.rb:42: protected method `printArea' called for #
<Box:0xb7f11280 @height = 20, @width = 10> (NoMethodError)

Eredità di classe

Uno dei concetti più importanti nella programmazione orientata agli oggetti è quello dell'ereditarietà. L'ereditarietà ci consente di definire una classe in termini di un'altra classe, il che semplifica la creazione e la manutenzione di un'applicazione.

L'ereditarietà offre anche l'opportunità di riutilizzare la funzionalità del codice e tempi di implementazione rapidi ma sfortunatamente Ruby non supporta più livelli di ereditarietà ma Ruby supporta mixins. Un mixin è come un'implementazione specializzata di ereditarietà multipla in cui viene ereditata solo la parte dell'interfaccia.

Quando si crea una classe, invece di scrivere membri dati e funzioni membro completamente nuovi, il programmatore può designare che la nuova classe erediti i membri di una classe esistente. Questa classe esistente è chiamatabase class or superclasse la nuova classe è indicata come derived class or sub-class.

Ruby supporta anche il concetto di sottoclasse, cioè ereditarietà e il seguente esempio spiega il concetto. La sintassi per estendere una classe è semplice. Basta aggiungere un carattere <e il nome della superclasse alla dichiarazione della classe. Ad esempio, di seguito definire una classe BigBox come una sottoclasse di Box -

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # instance method
   def getArea
      @width * @height
   end
end

# define a subclass
class BigBox < Box

   # add a new instance method
   def printArea
      @area = @width * @height
      puts "Big box area is : #@area"
   end
end

# create an object
box = BigBox.new(10, 20)

# print the area
box.printArea()

Quando il codice sopra viene eseguito, produce il seguente risultato:

Big box area is : 200

Metodi che sovrascrivono

Sebbene tu possa aggiungere nuove funzionalità in una classe derivata, ma a volte vorresti cambiare il comportamento di un metodo già definito in una classe genitore. Puoi farlo semplicemente mantenendo lo stesso nome del metodo e sovrascrivendo la funzionalità del metodo come mostrato di seguito nell'esempio:

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # instance method
   def getArea
      @width * @height
   end
end

# define a subclass
class BigBox < Box

   # change existing getArea method as follows
   def getArea
      @area = @width * @height
      puts "Big box area is : #@area"
   end
end

# create an object
box = BigBox.new(10, 20)

# print the area using overriden method.
box.getArea()

Sovraccarico dell'operatore

Vorremmo che l'operatore + eseguisse l'aggiunta vettoriale di due oggetti Box usando +, l'operatore * per moltiplicare una larghezza e un'altezza di Box per uno scalare e l'operatore unario - per negare la larghezza e l'altezza del Box. Ecco una versione della classe Box con operatori matematici definiti:

class Box
   def initialize(w,h)     # Initialize the width and height
      @width,@height = w, h
   end

   def +(other)       # Define + to do vector addition
      Box.new(@width + other.width, @height + other.height)
   end

   def [email protected]           # Define unary minus to negate width and height
      Box.new([email protected], [email protected])
   end

   def *(scalar)           # To perform scalar multiplication
      Box.new(@width*scalar, @height*scalar)
   end
end

Congelamento di oggetti

A volte, vogliamo impedire che un oggetto venga modificato. Il metodo freeze in Object ci consente di farlo, trasformando efficacemente un oggetto in una costante. Qualsiasi oggetto può essere congelato invocandoObject.freeze. Un oggetto congelato non può essere modificato: non puoi cambiare le sue variabili di istanza.

Puoi controllare se un determinato oggetto è già congelato o non lo utilizza Object.frozen?metodo, che restituisce true nel caso in cui l'oggetto sia congelato, altrimenti viene restituito un valore false. L'esempio seguente chiarisce il concetto:

#!/usr/bin/ruby -w

# define a class
class Box
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # accessor methods
   def getWidth
      @width
   end
   def getHeight
      @height
   end

   # setter methods
   def setWidth=(value)
      @width = value
   end
   def setHeight=(value)
      @height = value
   end
end

# create an object
box = Box.new(10, 20)

# let us freez this object
box.freeze
if( box.frozen? )
   puts "Box object is frozen object"
else
   puts "Box object is normal object"
end

# now try using setter methods
box.setWidth = 30
box.setHeight = 50

# use accessor methods
x = box.getWidth()
y = box.getHeight()

puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

Quando il codice sopra viene eseguito, produce il seguente risultato:

Box object is frozen object
test.rb:20:in `setWidth=': can't modify frozen object (TypeError)
   from test.rb:39

Costanti di classe

È possibile definire una costante all'interno di una classe assegnando un valore numerico o stringa diretto a una variabile, che viene definita senza utilizzare @ o @@. Per convenzione, manteniamo i nomi delle costanti in maiuscolo.

Una volta definita una costante, non è possibile modificarne il valore ma è possibile accedere a una costante direttamente all'interno di una classe in modo molto simile a una variabile, ma se si desidera accedere a una costante al di fuori della classe, è necessario utilizzare classname::constant come mostrato nell'esempio seguente.

#!/usr/bin/ruby -w

# define a class
class Box
   BOX_COMPANY = "TATA Inc"
   BOXWEIGHT = 10
   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # instance method
   def getArea
      @width * @height
   end
end

# create an object
box = Box.new(10, 20)

# call instance methods
a = box.getArea()
puts "Area of the box is : #{a}"
puts Box::BOX_COMPANY
puts "Box weight is: #{Box::BOXWEIGHT}"

Quando il codice sopra viene eseguito, produce il seguente risultato:

Area of the box is : 200
TATA Inc
Box weight is: 10

Le costanti di classe vengono ereditate e possono essere sovrascritte come i metodi di istanza.

Crea oggetto usando Alloca

Potrebbe esserci una situazione in cui si desidera creare un oggetto senza chiamare il suo costruttore initializecioè usando un nuovo metodo, in tal caso puoi chiamare allocate , che creerà un oggetto non inizializzato per te come nell'esempio seguente -

#!/usr/bin/ruby -w

# define a class
class Box
   attr_accessor :width, :height

   # constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # instance method
   def getArea
      @width * @height
   end
end

# create an object using new
box1 = Box.new(10, 20)

# create another object using allocate
box2 = Box.allocate

# call instance method using box1
a = box1.getArea()
puts "Area of the box is : #{a}"

# call instance method using box2
a = box2.getArea()
puts "Area of the box is : #{a}"

Quando il codice sopra viene eseguito, produce il seguente risultato:

Area of the box is : 200
test.rb:14: warning: instance variable @width not initialized
test.rb:14: warning: instance variable @height not initialized
test.rb:14:in `getArea': undefined method `*' 
   for nil:NilClass (NoMethodError) from test.rb:29

Informazioni sulla classe

Se le definizioni di classe sono codice eseguibile, ciò implica che vengano eseguite nel contesto di un oggetto: self deve fare riferimento a qualcosa. Scopriamo cos'è.

#!/usr/bin/ruby -w

class Box
   # print class information
   puts "Type of self = #{self.type}"
   puts "Name of self = #{self.name}"
end

Quando il codice sopra viene eseguito, produce il seguente risultato:

Type of self = Class
Name of self = Box

Ciò significa che una definizione di classe viene eseguita con quella classe come oggetto corrente. Ciò significa che i metodi nella metaclasse e nelle sue superclassi saranno disponibili durante l'esecuzione della definizione del metodo.