129 votes

ruby héritage vs mixin

En Ruby, puisque vous pouvez inclure plusieurs mixin, mais seulement d'étendre une classe, il semble que mixin aurait la préférence sur l'héritage.

Ma question: si vous écrivez du code qui doit être étendu/inclus pour être utile, pourquoi voudriez-vous jamais le faire en classe? Ou mettre une autre manière, pourquoi ne pas vous toujours en faire un module?

Je ne peux que penser à une raison pourquoi vous voulez une classe, et qui est si vous avez besoin d'instancier la classe. Dans le cas d'ActiveRecord::Base, cependant, vous n'avez jamais l'instancier directement. Donc ne devrait-il pas un module à la place?

178voto

Andy Gaskell Points 15264

Je viens de lire sur ce sujet dans Le Bien fondé Rubyist (grand livre, par la voie). L'auteur fait un meilleur travail en expliquant que je ne le ferais donc je vais le citer:


Pas de règle unique ou la formule est toujours la bonne conception. Mais il est utile de garder un quelques considérations à l'esprit lorsque vous êtes à faire des contre-module de décisions:

  • Les Modules n'avez pas de cas. Il s'ensuit que les entités ou les choses sont généralement mieux modélisé dans les classes, et les caractéristiques ou les propriétés d'entités ou de choses sont mieux encapsulé dans des modules. En conséquence, comme indiqué dans la section 4.1.1, classe les noms ont tendance à être des noms, alors que les noms de module sont souvent des adjectifs (Pile rapport Stacklike).

  • Une classe peut avoir qu'une seule super-classe, mais on peut mélanger dans autant de modules qu'il souhaite. Si vous êtes à l'aide de l'héritage, de donner la priorité à la création d'un bon super-classe/sous-classe relation. Ne pas utiliser une classe, le seul super-classe de la relation à doter la classe avec ce qui est peut-être juste un de plusieurs ensembles de caractéristiques.

Un résumé de ces règles dans un exemple, voici ce que vous ne devriez pas faire:

module Vehicle 
... 
class SelfPropelling 
... 
class Truck < SelfPropelling 
  include Vehicle 
... 

Plutôt, vous devriez faire ceci:

module SelfPropelling 
... 
class Vehicle 
  include SelfPropelling 
... 
class Truck < Vehicle 
... 

La deuxième version des modèles les entités et les propriétés beaucoup plus proprement. Camion descend du Véhicule (ce qui est logique), alors que SelfPropelling est une caractéristique de véhicules (au moins, tous ceux que nous aimons dans ce modèle du monde)-une caractéristique qui est transmise aux camions en vertu de Camion à un descendant, ou spécialisés la forme du Véhicule.

40voto

Dan Barowy Points 803

Je pense que mixin est une excellente idée, mais il y a un autre problème que personne n'a mentionné: collisions d'espace de noms. Considérer:

module A
  HELLO = "hi"
  def sayhi
    puts HELLO
  end
end

module B
  HELLO = "you stink"
  def sayhi
    puts HELLO
  end
end

class C
  include A
  include B
end

c = C.new
c.sayhi

Qui gagne? En Ruby, il s'avère être le dernier, module B, parce que vous avez inclus après l' module A. Maintenant, il est facile d'éviter ce problème: assurez-vous que tous module A et module B's les constantes et les méthodes sont peu susceptibles d'espaces de noms. Le problème est que le compilateur ne vous avertit lorsque les collisions peuvent se produire.

Je soutiens que ce comportement n'est pas à l'échelle de grandes équipes de programmeurs-vous ne devriez pas supposer que la personne qui met en œuvre class C connaît chaque nom dans la portée. Ruby va même vous permettre de substituer une constante ou une méthode d'un autre type. Je ne suis pas sûr que pourrait jamais être considéré comme comportement correct.

14voto

Chuck Points 138930

De mon point de vue: les Modules de partage de comportement, tandis que les classes sont pour la modélisation des relations entre les objets. Vous techniquement pourrait faire tout ce qu'une instance de l'Objet et de le mélanger à ce que les modules que vous souhaitez obtenir le jeu souhaité de comportements, mais ce serait une mauvaise, aléatoire et plutôt illisible conception.

10voto

nareshb Points 156

La réponse à votre question est en grande partie du contexte. La distillation de la pubb de l'observation, le choix est principalement motivée par le domaine à l'examen.

Et oui, ActiveRecord devrait avoir été inclus plutôt que prolongée par une sous-classe. Un autre ORM - datamapper - permet justement d'obtenir que!

4voto

Tilo Points 13833

J'aime Andy Gaskell réponse très - voulais juste ajouter que oui, ActiveRecord ne doit pas utiliser l'héritage, mais plutôt d'inclure un module pour ajouter le comportement (surtout la persistance) à un modèle de classe. ActiveRecord est tout simplement en utilisant le paradigme erroné.

Pour la même raison, j'aime beaucoup MongoId sur MongoMapper, car il laisse au développeur la possibilité d'utiliser l'héritage comme un moyen de modélisation de quelque chose de significatif dans le domaine du problème.

C'est triste de constater que pratiquement personne dans la communauté Rails est à l'aide de "Ruby héritage" de la façon dont il est censé être utilisé pour définir des hiérarchies de classes, et pas seulement ajouter de comportement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X