Eu apenas li sobre esse assunto em O Rubyist bem fundamentada (grande livro, por sinal). O autor faz um trabalho melhor de explicar do que eu faria, então vou citá-lo:
Nenhuma regra ou fórmula única sempre resulta no design certo. Mas é útil ter em mente algumas considerações ao tomar decisões de classe versus módulo:
Módulos não têm instâncias. Segue-se que entidades ou coisas geralmente são melhor modeladas em classes, e características ou propriedades de entidades ou coisas são melhor encapsuladas em módulos. Da mesma forma, como observado na seção 4.1.1, os nomes de classes tendem a ser substantivos, enquanto os nomes de módulos são frequentemente adjetivos (Stack versus Stacklike).
Uma classe pode ter apenas uma superclasse, mas pode misturar quantos módulos desejar. Se você estiver usando herança, dê prioridade à criação de um relacionamento sensato de superclasse / subclasse. Não use o único e único relacionamento de superclasse de uma classe para dotar a classe do que pode vir a ser apenas um dos vários conjuntos de características.
Resumindo essas regras em um exemplo, eis o que você não deve fazer:
module Vehicle
...
class SelfPropelling
...
class Truck < SelfPropelling
include Vehicle
...
Em vez disso, você deve fazer o seguinte:
module SelfPropelling
...
class Vehicle
include SelfPropelling
...
class Truck < Vehicle
...
A segunda versão modela as entidades e propriedades com muito mais cuidado. O caminhão desce do veículo (o que faz sentido), enquanto a autopropulsão é uma característica dos veículos (pelo menos, todos aqueles com quem nos preocupamos neste modelo do mundo) - uma característica que é repassada aos caminhões em virtude de o caminhão ser descendente, ou forma especializada, de veículo.
Truck
IS AVehicle
- não háTruck
que não seria aVehicle
. No entanto, eu chamaria o módulo talvezSelfPropelable
(:?) HmmSelfPropeled
parece certo, mas é quase o mesmo: D. De qualquer forma, eu não o incluiria,Vehicle
mas simTruck
- como existem veículos que não sãoSelfPropeled
. Também é uma boa indicação a perguntar - existem outras coisas, NÃO veículos que SÃOSelfPropeled
? - Bem, talvez, mas seria mais difícil de encontrar. Assim,Vehicle
poderia herdar da classe SelfPropelling (como classe, não caberia comoSelfPropeled
- pois isso é mais importante) #Eu acho que mixins são uma ótima idéia, mas há outro problema aqui que ninguém mencionou: colisões de namespace. Considerar:
Qual deles vence? No Ruby, verifica-se o último
module B
, porque você o incluiu depoismodule A
. Agora, é fácil de evitar este problema: certifique-se de todosmodule A
emodule B
's constantes e métodos estão em namespaces improváveis. O problema é que o compilador não avisa quando ocorrem colisões.Argumento que esse comportamento não se ajusta a grandes equipes de programadores - você não deve assumir que a pessoa que implementa
class C
conhece todos os nomes no escopo. O Ruby até permite que você substitua uma constante ou método de um tipo diferente . Eu não tenho certeza de que poderia nunca ser considerado um comportamento correto.fonte
C#sayhi
geraB::HELLO
não porque o Ruby mistura as constantes, mas porque o ruby resolve as constantes do mais próximo para o mais distante -, então asHELLO
referências mencionadas emB
sempre resolveriamB::HELLO
. Isso vale mesmo se a classe C definiu a suaC::HELLO
também.Minha opinião: os módulos são para compartilhar comportamentos, enquanto as classes são para modelar relacionamentos entre objetos. Tecnicamente, você pode transformar tudo em uma instância do Object e misturar os módulos em que deseja obter o conjunto desejado de comportamentos, mas isso seria um design pobre, aleatório e bastante ilegível.
fonte
A resposta para sua pergunta é amplamente contextual. Destilando a observação do pubb, a escolha é impulsionada principalmente pelo domínio em consideração.
E sim, o ActiveRecord deveria ter sido incluído em vez de estendido por uma subclasse. Outro ORM - datamapper - consegue exatamente isso!
fonte
Eu gosto muito da resposta de Andy Gaskell - só queria acrescentar que sim, o ActiveRecord não deve usar herança, mas incluir um módulo para adicionar o comportamento (principalmente persistência) a um modelo / classe. O ActiveRecord está simplesmente usando o paradigma errado.
Pelo mesmo motivo, eu gosto muito do MongoId sobre o MongoMapper, porque deixa ao desenvolvedor a chance de usar a herança como uma maneira de modelar algo significativo no domínio do problema.
É triste que praticamente ninguém na comunidade Rails esteja usando "herança Ruby" da maneira que deveria ser usada - para definir hierarquias de classes, não apenas para adicionar comportamento.
fonte
A melhor maneira de entender mixins é como classes virtuais. Mixins são "classes virtuais" que foram injetadas na cadeia ancestral de uma classe ou módulo.
Quando usamos "include" e passamos a ele um módulo, ele adiciona o módulo à cadeia ancestral logo antes da classe da qual estamos herdando:
Todo objeto no Ruby também tem uma classe singleton. Os métodos adicionados a essa classe singleton podem ser chamados diretamente no objeto e, portanto, agem como métodos de "classe". Quando usamos "estender" em um objeto e passamos ao objeto um módulo, estamos adicionando os métodos do módulo à classe singleton do objeto:
Podemos acessar a classe singleton com o método singleton_class:
O Ruby fornece alguns ganchos para os módulos quando eles estão sendo misturados em classes / módulos.
included
é um método de gancho fornecido pelo Ruby que é chamado sempre que você inclui um módulo em algum módulo ou classe. Assim como incluído, há umextended
gancho associado para extensão. Será chamado quando um módulo for estendido por outro módulo ou classe.Isso cria um padrão interessante que os desenvolvedores poderiam usar:
Como você pode ver, este módulo único está adicionando métodos de instância, métodos "class" e agindo diretamente na classe de destino (chamando a_class_method () neste caso).
ActiveSupport :: Concern encapsula esse padrão. Aqui está o mesmo módulo reescrito para usar o ActiveSupport :: Concern:
fonte
No momento, estou pensando no
template
padrão de design. Simplesmente não pareceria certo com um módulo.fonte