Ruby - Orienté objet

Ruby est un pur langage orienté objet et tout apparaît à Ruby comme un objet. Chaque valeur de Ruby est un objet, même les choses les plus primitives: des chaînes, des nombres et même vrai et faux. Même une classe elle-même est un objet qui est une instance de la classe Class . Ce chapitre vous présentera toutes les fonctionnalités principales liées à Ruby orienté objet.

Une classe est utilisée pour spécifier la forme d'un objet et combine la représentation des données et les méthodes de manipulation de ces données dans un package soigné. Les données et les méthodes d'une classe sont appelées membres de la classe.

Définition de la classe Ruby

Lorsque vous définissez une classe, vous définissez un plan pour un type de données. Cela ne définit en fait aucune donnée, mais définit ce que signifie le nom de la classe, c'est-à-dire en quoi consistera un objet de la classe et quelles opérations peuvent être effectuées sur un tel objet.

Une définition de classe commence par le mot-clé class suivi du class name et est délimité par un end. Par exemple, nous avons défini la classe Box en utilisant la classe de mot-clé comme suit -

class Box
   code
end

Le nom doit commencer par une majuscule et par convention, les noms contenant plus d'un mot sont exécutés avec chaque mot en majuscule et sans caractère de séparation (CamelCase).

Définir des objets Ruby

Une classe fournit les plans pour les objets, donc fondamentalement un objet est créé à partir d'une classe. Nous déclarons les objets d'une classe en utilisantnewmot-clé. Les instructions suivantes déclarent deux objets de la classe Box -

box1 = Box.new
box2 = Box.new

La méthode initialize

le initialize method est une méthode de classe Ruby standard et fonctionne presque de la même manière que constructorfonctionne dans d'autres langages de programmation orientés objet. La méthode initialize est utile lorsque vous souhaitez initialiser certaines variables de classe au moment de la création de l'objet. Cette méthode peut prendre une liste de paramètres et comme toute autre méthode ruby, elle serait précédée dedef mot-clé comme indiqué ci-dessous -

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

Les variables d'instance

le instance variablessont des sortes d'attributs de classe et deviennent des propriétés d'objets une fois que les objets sont créés à l'aide de la classe. Les attributs de chaque objet sont attribués individuellement et ne partagent aucune valeur avec les autres objets. Ils sont accessibles à l'aide de l'opérateur @ dans la classe mais pour y accéder en dehors de la classe que nous utilisonspublic méthodes, qui sont appelées accessor methods. Si nous prenons la classe définie ci-dessusBox puis @width et @height sont des variables d'instance pour la classe Box.

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

Les méthodes accesseur et setter

Pour rendre les variables disponibles depuis l'extérieur de la classe, elles doivent être définies dans accessor methods, ces méthodes d'accès sont également appelées méthodes getter. L'exemple suivant montre l'utilisation des méthodes d'accesseur -

#!/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}"

Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -

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

Semblable aux méthodes d'accès, qui sont utilisées pour accéder à la valeur des variables, Ruby fournit un moyen de définir les valeurs de ces variables depuis l'extérieur de la classe en utilisant setter methods, qui sont définis comme ci-dessous -

#!/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}"

Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -

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

Les méthodes d'instance

le instance methods sont également définis de la même manière que nous définissons toute autre méthode utilisant defet ils peuvent être utilisés à l'aide d'une instance de classe uniquement comme indiqué ci-dessous. Leur fonctionnalité ne se limite pas à accéder aux variables d'instance, mais ils peuvent également faire beaucoup plus selon vos besoins.

#!/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}"

Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -

Area of the box is : 200

La classe Méthodes et variables

le class variablesest une variable partagée entre toutes les instances d'une classe. En d'autres termes, il existe une instance de la variable et elle est accessible par des instances d'objet. Les variables de classe sont précédées de deux caractères @ (@@). Une variable de classe doit être initialisée dans la définition de classe comme indiqué ci-dessous.

Une méthode de classe est définie en utilisant def self.methodname(), qui se termine par un délimiteur de fin et serait appelé en utilisant le nom de classe comme classname.methodname comme indiqué dans l'exemple suivant -

#!/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()

Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -

Box count is : 2

La méthode to_s

Toute classe que vous définissez doit avoir un to_sinstance pour renvoyer une représentation sous forme de chaîne de l'objet. Voici un exemple simple pour représenter un objet Box en termes de largeur et de hauteur -

#!/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}"

Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -

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

Contrôle d'accès

Ruby vous offre trois niveaux de protection au niveau des méthodes d'instance, qui peuvent être public, private, or protected. Ruby n'applique aucun contrôle d'accès aux variables d'instance et de classe.

  • Public Methods- Les méthodes publiques peuvent être appelées par n'importe qui. Les méthodes sont publiques par défaut, sauf pour initialize, qui est toujours privé.

  • Private Methods- Les méthodes privées ne sont pas accessibles, ni même vues de l'extérieur de la classe. Seules les méthodes de classe peuvent accéder aux membres privés.

  • Protected Methods- Une méthode protégée ne peut être appelée que par des objets de la classe de définition et de ses sous-classes. L'accès est conservé au sein de la famille.

Voici un exemple simple pour montrer la syntaxe des trois modificateurs d'accès -

#!/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()

Lorsque le code ci-dessus est exécuté, il produit le résultat suivant. Ici, la première méthode est appelée avec succès mais la deuxième méthode pose un problème.

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

Héritage de classe

L'un des concepts les plus importants de la programmation orientée objet est celui de l'héritage. L'héritage nous permet de définir une classe en termes d'une autre classe, ce qui facilite la création et la maintenance d'une application.

L'héritage offre également la possibilité de réutiliser la fonctionnalité de code et un temps d'implémentation rapide, mais malheureusement, Ruby ne prend pas en charge plusieurs niveaux d'héritage, mais Ruby prend en charge mixins. Un mixin est comme une implémentation spécialisée de l'héritage multiple dans laquelle seule la partie d'interface est héritée.

Lors de la création d'une classe, au lieu d'écrire des membres de données et des fonctions membres complètement nouveaux, le programmeur peut désigner que la nouvelle classe doit hériter des membres d'une classe existante. Cette classe existante s'appelle lebase class or superclass, et la nouvelle classe est appelée derived class or sub-class.

Ruby prend également en charge le concept de sous-classification, c'est-à-dire l'héritage et l'exemple suivant explique le concept. La syntaxe pour étendre une classe est simple. Ajoutez simplement un caractère <et le nom de la superclasse à votre instruction de classe. Par exemple, définissez ci-dessous une classe BigBox en tant que sous-classe de 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()

Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -

Big box area is : 200

Remplacement des méthodes

Bien que vous puissiez ajouter de nouvelles fonctionnalités dans une classe dérivée, vous souhaitez parfois modifier le comportement d'une méthode déjà définie dans une classe parente. Vous pouvez le faire simplement en gardant le même nom de méthode et en remplaçant la fonctionnalité de la méthode comme indiqué ci-dessous dans l'exemple -

#!/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()

Surcharge de l'opérateur

Nous aimerions que l'opérateur + effectue une addition vectorielle de deux objets Box en utilisant +, l'opérateur * pour multiplier une largeur et une hauteur de Box par un scalaire, et l'opérateur unaire - pour annuler la largeur et la hauteur de la Box. Voici une version de la classe Box avec des opérateurs mathématiques définis -

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

Gel d'objets

Parfois, nous voulons empêcher un objet d'être modifié. La méthode de gel dans Object nous permet de faire cela, transformant efficacement un objet en constante. Tout objet peut être gelé en invoquantObject.freeze. Un objet figé ne peut pas être modifié: vous ne pouvez pas modifier ses variables d'instance.

Vous pouvez vérifier si un objet donné est déjà figé ou n'utilise pas Object.frozen?méthode, qui renvoie true au cas où l'objet est gelé, sinon une valeur false est renvoyée. L'exemple suivant clarifie le concept -

#!/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}"

Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -

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

Constantes de classe

Vous pouvez définir une constante à l'intérieur d'une classe en affectant une valeur numérique directe ou une valeur de chaîne à une variable, qui est définie sans utiliser ni @ ni @@. Par convention, nous gardons les noms constants en majuscules.

Une fois qu'une constante est définie, vous ne pouvez pas changer sa valeur mais vous pouvez accéder à une constante directement à l'intérieur d'une classe un peu comme une variable, mais si vous voulez accéder à une constante en dehors de la classe, vous devez utiliser classname::constant comme indiqué dans l'exemple ci-dessous.

#!/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}"

Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -

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

Les constantes de classe sont héritées et peuvent être remplacées comme les méthodes d'instance.

Créer un objet avec Allocate

Il peut y avoir une situation où vous souhaitez créer un objet sans appeler son constructeur initializec'est-à-dire en utilisant une nouvelle méthode, dans ce cas, vous pouvez appeler allocate , ce qui créera un objet non initialisé pour vous comme dans l'exemple suivant -

#!/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}"

Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -

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

Information sur la classe

Si les définitions de classe sont du code exécutable, cela implique qu'elles s'exécutent dans le contexte d'un objet: self doit référencer quelque chose. Découvrons ce que c'est.

#!/usr/bin/ruby -w

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

Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -

Type of self = Class
Name of self = Box

Cela signifie qu'une définition de classe est exécutée avec cette classe comme objet courant. Cela signifie que les méthodes de la métaclasse et de ses superclasses seront disponibles pendant l'exécution de la définition de méthode.