J'ai besoin d'aide pour comprendre l'héritage.
class MyArray < Array end a = MyArray[1, 2, 3] #=> [1, 2, 3] b = MyArray[4, 5] #=> [4, 5] c = a + b #=> [1, 2, 3, 4, 5] a.class #=> MyArray b.class #=> MyArray c.class #=> Array
Je ne comprends pas pourquoi le résultat de l'ajout n'est pas une instance de la classe MyArray
.
3 Réponses :
Je ne comprends pas pourquoi mon tableau "a" n'est pas la classe "MyArray" après l'ajout.
Pourquoi devrait-il (être un MyArray
)? L'opération de concaténation sur les tableaux est définie pour renvoyer un nouveau Array
, c'est donc ce qui se passe ici. https://ruby-doc.org/core-2.5 .3 / Array.html # method-i-2B
Si vous le souhaitez, vous pouvez remplacer cette opération dans votre classe pour renvoyer une instance de MyArray. N'oubliez pas toutes les autres méthodes similaires.
C'est aussi pourquoi c'est une mauvaise idée de sous-classer les collections standard. Mieux vaut utiliser la composition plutôt que l'héritage ici.
OK, je comprends. Mais je souhaite définir d'autres méthodes sur mes objets MyArray. Comment puis-je faire ça?
@ µgeek: que voulez-vous dire? Tout comme vous définissez n'importe quelle autre méthode.
Oui, je veux définir une méthode, par exemple pour comparer 2 tableaux, ou couper un tableau à deux autres tableaux ...
Mais je veux aussi pouvoir utiliser les méthodes des tableaux
Dois-je seulement insérer des méthodes dans la classe Array?
@ µgeek Je ne sais pas ce que vous vouliez dire par là, mais il semble que vous bénéficierez de continuer avec le tutoriel / livre / tout ce que vous utilisez et d'essayer des exercices plus simples pour l'instant (ceux qui n'impliquent pas de sous-classes de classes standard). Si ma mémoire est bonne, le livre de pioche a utilisé une excellente application de bibliothèque de chansons comme exemples.
Merci pour votre conseil. Je vais essayer des exercices plus simples ... Est-ce vraiment une mauvaise façon d'ajouter des méthodes à une classe standard?
@ µgeek: oui, vous devriez vous abstenir de faire cela, jusqu'à ce que vous puissiez expliquer pourquoi vous en avez besoin et pourquoi d'autres approches ne sont pas assez bonnes.
"L'opération de concaténation est définie pour renvoyer un nouveau tableau" - la documentation dit simplement "tableau" (minuscules), la classe n'est pas spécifiée. Et d'autres méthodes comme a [1..2]
ou a * 2
qui retournent également un "nouveau tableau" conservent en fait la classe du receveur, c'est-à-dire qu'elles renvoient Instances MyArray
. "Pourquoi devrait-il s'agir d'un MyArray?" - car ce serait moins surprenant. Je pense que la conversion en Array
est due à l'optimisation plutôt qu'à la considération de la POO.
"un [1..2] ou un * 2 ... retourne des instances MyArray" - oh wow, je ne m'y attendais absolument pas.
Juste pour ajouter un peu à la réponse de Sergio en termes de son commentaire sur l'utilisation de la composition par rapport à l'héritage et de l'échange dans les commentaires.
Au lieu de dire MyArray est un tableau, vous pouvez dire MyArrayLike a et array. Ensuite, vous pouvez "transférer" les méthodes qui ont du sens vers le tableau sous-jacent, tout en ajoutant vos propres fonctionnalités qui ont du sens pour votre classe sans tableau de sous-classification.
Ruby en a même plusieurs façons de rendre cela très facile, y compris le Forwardable a > module.
class MyArrayLike attr_reader :arr def initialize( initial_arr ) @arr = initial_arr end def +(other) result = self.class.new(arr + other.arr) # maybe you want to do more than just concat the underlying array, if so you can do it here result end def first # for example maybe you want first to just return the first item in the underlying array. arr.first end end a = MyArrayLike.new([1,2,3]) b = MyArrayLike.new([4,5]) puts "a.class = #{a.class}" # => a.class = MyArrayLike puts a # => #<MyArrayLike:0x00000000dc4b00> a += b puts "a.class = #{a.class}" # => a.class = MyArrayLike puts a # => #<MyArrayLike:0x00000000dc4470> puts a.first # => 1 puts a.arr # => 1 # 2 # 3 # 4 # 5
Ajouter MyArray
à MyArray
pour obtenir Array
peut être contre-intuitif, mais une méthode peut être définie pour renvoyer n'importe quelle classe. Et dans le cas de Array # +
, que vous appelez, il se trouve qu'il est simplement défini pour renvoyer un Array
. C'est tout.
Si vous voulez qu'il renvoie un MyArray
, une façon de le faire est de définir MyArray # +
comme suit:
class MyArray < Array def +other MyArray.new(super) end end (MyArray.new([1, 2, 3]) + MyArray.new([4, 5])).class # => MyArray
En passant, notez que votre définition de MyArray # initialize
n'a pas de sens, et donc redondante.
Implémentation alternative: dup.concat (autre)
A lire: Méfiez-vous des sous-classes de classes de base Ruby