Metaclasse - Metaclass
Na programação orientada a objetos , uma metaclasse é uma classe cujas instâncias são classes. Assim como uma classe comum define o comportamento de certos objetos, uma metaclasse define o comportamento de certas classes e suas instâncias. Nem todas as linguagens de programação orientadas a objetos oferecem suporte a metaclasses. Entre aqueles que o fazem, o grau em que as metaclasses podem substituir qualquer aspecto do comportamento de classe varia. As metaclasses podem ser implementadas fazendo com que as classes sejam cidadãos de primeira classe , caso em que uma metaclasse é simplesmente um objeto que constrói classes. Cada linguagem tem seu próprio protocolo de metaobjeto , um conjunto de regras que governam como objetos, classes e metaclasses interagem.
Exemplo Python
Em Python , a classe interna type
é uma metaclasse. Considere esta classe Python simples:
class Car:
def __init__(self, make: str, model: str, year: int, color: str):
self.make = make
self.model = model
self.year = year
self.color = color
@property
def description(self) -> str:
"""Return a description of this car."""
return f"{self.color} {self.make} {self.model}"
Em tempo de execução, Car
ele próprio é uma instância de type
. O código-fonte da Car
classe, mostrado acima, não inclui detalhes como o tamanho em bytes dos Car
objetos, seu layout binário na memória, como são alocados, que o __init__
método é chamado automaticamente sempre que um Car
é criado e assim por diante. Esses detalhes entram em jogo não apenas quando um novo Car
objeto é criado, mas também sempre que qualquer atributo de a Car
é acessado. Em linguagens sem metaclasses, esses detalhes são definidos pela especificação da linguagem e não podem ser substituídos. Em Python, a metaclasse - type
- controla esses detalhes do Car
comportamento de. Eles podem ser substituídos usando uma metaclasse diferente em vez de type
.
O exemplo acima contém um código redundante para fazer com os quatro atributos make
, model
, year
, e color
. É possível eliminar parte dessa redundância usando uma metaclasse. Em Python, uma metaclasse é mais facilmente definida como uma subclasse de type
.
class AttributeInitType(type):
def __call__(self, *args, **kwargs):
"""Create a new instance."""
# First, create the object in the normal default way.
obj = type.__call__(self, *args)
# Additionally, set attributes on the new object.
for name, value in kwargs.items():
setattr(obj, name, value)
# Return the new object.
return obj
Esta metaclasse apenas substitui a criação de objetos. Todos os outros aspectos do comportamento da classe e do objeto ainda são tratados por type
.
Agora a classe Car
pode ser reescrita para usar essa metaclasse. No Python 3, isso é feito fornecendo um "argumento de palavra-chave" metaclass
para a definição da classe:
class Car(object, metaclass=AttributeInitType):
@property
def description(self) -> str:
"""Return a description of this car."""
return " ".join(str(value) for value in self.__dict__.values())
O objeto resultante Car
pode ser instanciado normalmente, mas pode conter qualquer número de argumentos de palavra-chave:
new_car = Car(make='Toyota', model='Prius', year=2005, color='Green', engine='Hybrid')
Em Smalltalk-80
Em Smalltalk , tudo é um objeto . Além disso, Smalltalk é um sistema baseado em classes , o que significa que cada objeto tem uma classe que define a estrutura desse objeto (ou seja, as variáveis de instância que o objeto possui) e as mensagens que um objeto entende. Juntos, isso implica que uma classe em Smalltalk é um objeto e que, portanto, uma classe precisa ser uma instância de uma classe (chamada metaclasse).
Por exemplo, um objeto carro c
é uma instância da classe Car
. Por sua vez, a classe Car
é novamente um objeto e, como tal, uma instância da metaclasse de Car
chamado Car class
. Observe o espaço em branco no nome da metaclasse. O nome da metaclasse é a expressão Smalltalk que, quando avaliada, resulta no objeto metaclasse. Assim, avaliando os Car class
resultados no objeto da metaclasse Car
cujo nome é Car class
(pode-se confirmar isso avaliando Car class name
que retorna o nome da metaclasse de Car
.)
Os métodos de classe realmente pertencem à metaclasse, assim como os métodos de instância realmente pertencem à classe. Quando uma mensagem é enviada ao objeto 2
, a busca pelo método começa em Integer
. Se não for encontrado, ele continua subindo a cadeia da superclasse, parando em Object, quer seja encontrado ou não.
Quando uma mensagem é enviada para Integer
a pesquisa, o método começa em Integer class
e prossegue na cadeia da superclasse até Object class
. Observe que, até agora, a cadeia de herança da metaclasse segue exatamente aquela da cadeia de herança da classe. Mas a cadeia da metaclasse se estende ainda mais porque Object class
é a subclasse de Class
. Todas as metaclasses são subclasses de Class.
No início de Smalltalks, havia apenas uma metaclasse chamada Class
. Isso implicava que os métodos de todas as classes eram os mesmos, em particular o método para criar novos objetos, ou seja new
,. Para permitir que as classes tenham seus próprios métodos e suas próprias variáveis de instância (chamadas de variáveis de instância de classe e não devem ser confundidas com variáveis de classe ), Smalltalk-80 introduziu para cada classe C
sua própria metaclasse C class
. Isso significa que cada metaclasse é efetivamente uma classe única .
Como não há requisitos para que as metaclasses se comportem de maneira diferente umas das outras, todas as metaclasses são instâncias de apenas uma classe chamada Metaclass
. A metaclasse de Metaclass
é chamada, Metaclass class
que novamente é uma instância de classe Metaclass
.
Em Smalltalk-80, cada classe (exceto Object
) tem uma superclasse . A superclasse abstrata de todas as metaclasses é Class
, que descreve a natureza geral das classes.
A hierarquia da superclasse para metaclasses é paralela àquela para classes, exceto para classe Object
. TODAS as metaclasses são subclasses de Class
, portanto:
Object class superclass == Class.
Como gêmeos siameses , classes e metaclasses nascem juntas. Metaclass
tem uma variável de instância thisClass
, que aponta para sua classe conjunta. Observe que o navegador de classe Smalltalk usual não mostra metaclasses como classes separadas. Em vez disso, o navegador de classe permite editar a classe junto com sua metaclasse ao mesmo tempo.
Os nomes das classes na hierarquia da metaclasse são facilmente confundidos com os conceitos do mesmo nome. Por exemplo:
-
Object
é a classe base que fornece métodos comuns para todos os objetos; "um objeto" é um número inteiro, ou um widget, ou umCar
, etc. -
Class
é a base das metaclasses que fornece métodos comuns para todas as classes (embora não seja uma metaclasse em si); "uma classe" é algo comoInteger
, ouWidget
, ouCar
, etc. -
Metaclass
fornece métodos comuns para todas as metaclasses.
Quatro classes fornecem os recursos para descrever novas classes. Sua hierarquia de herança (do objeto) e as principais facilidades que eles fornecem são:
- Objeto - comportamento padrão comum a todos os objetos, como acesso de classe
- Comportamento - estado mínimo para métodos de compilação e criação / execução de objetos
- ClassDescription ( classe abstrata ) - nomenclatura de classe / variável, comentários
- Classe - instalações semelhantes e mais abrangentes às superclasses
- Metaclasse - inicialização de variáveis de classe, mensagens de criação de instância
- ClassDescription ( classe abstrata ) - nomenclatura de classe / variável, comentários
- Comportamento - estado mínimo para métodos de compilação e criação / execução de objetos
Em Ruby
Ruby purifica o conceito Smalltalk-80 de metaclasses introduzindo eigenclasses , removendo a Metaclass
classe e (des) redefinindo o mapa de classe-of. A mudança pode ser esquematizada da seguinte forma:
|
→ |
|
Observe em particular a correspondência entre as metaclasses implícitas de Smalltalk e as equações de classes de Ruby. O modelo de eigenclass do Ruby torna o conceito de metaclasses implícitas totalmente uniforme: cada objeto x tem seu próprio meta-objeto, chamado de eigenclass de x , que é um meta-nível superior a x . As eigenclasses de "ordem superior" geralmente existem puramente conceitualmente - elas não contêm nenhum método ou armazenam quaisquer (outros) dados na maioria dos programas Ruby.
Os diagramas a seguir mostram uma estrutura central de amostra de Smalltalk-80 e Ruby em comparação. Em ambas as linguagens, a estrutura consiste em uma parte embutida que contém os objetos circulares (ou seja, objetos que aparecem em um ciclo formado por uma combinação de links azuis ou verdes) e uma parte do usuário que tem quatro objetos explícitos: classes A
e B
e objetos terminais u
e v
. Links verdes mostram a relação filho → pai de herança (com a direção ascendente implícita), links azuis mostram a relação membro → contêiner complementar de instanciação (um link azul de x aponta para o menor contêiner real de x que é o ponto inicial para o pesquisa de método quando um método é invocado em x ). Os nós cinza exibem as eigenclasses (resp. Metaclasses implícitas no caso de Smalltalk-80).
Smalltalk-80 | Rubi | |
O diagrama à direita também fornece uma imagem de avaliação preguiçosa de eigenclasses em Ruby. O v
objeto pode ter seu eigenclass avaliado (alocado) como consequência da adição de métodos singleton a v
.
De acordo com o método de introspecção de Ruby denominado class
, a classe de cada classe (e de cada eigenclass) é constantemente a Class
classe (denotada por c
no diagrama).
Class
, e Struct
são as únicas classes que possuem classes como instâncias. A subclasse de Class
não é permitida. Seguindo a definição padrão de metaclasses, podemos concluir que Class
e Struct
são as únicas metaclasses em Ruby. Isso parece contradizer a correspondência entre Ruby e Smalltalk, uma vez que em Smalltalk-80, cada classe tem sua própria metaclasse. A discrepância é baseada na discordância entre o class
método de introspecção em Ruby e Smalltalk. Enquanto o mapa x ↦ x. class
coincide em objetos terminais, difere na restrição de classes. Como já mencionado acima, para uma classe x
, a expressão Ruby é x.class
avaliada constantemente como Class
. Em Smalltalk-80, se x
for uma classe, a expressão x class
corresponde ao Ruby x.singleton_class
- que avalia o eigenclass de x
.
Em Objective-C
As metaclasses em Objective-C são quase iguais às de Smalltalk-80 - o que não é surpreendente, já que Objective-C toma muito emprestado de Smalltalk. Como Smalltalk, em Objective-C, as variáveis de instância e métodos são definidos por uma classe de objeto. Uma classe é um objeto, portanto, é uma instância de uma metaclasse.
Como Smalltalk, em Objective-C, os métodos de classe são simplesmente métodos chamados no objeto de classe, portanto, os métodos de classe de uma classe devem ser definidos como métodos de instância em sua metaclasse. Como diferentes classes podem ter diferentes conjuntos de métodos de classe, cada classe deve ter sua própria metaclasse separada. Classes e metaclasses são sempre criadas como um par: o tempo de execução tem funções objc_allocateClassPair()
e objc_registerClassPair()
para criar e registrar pares classe-metaclasse, respectivamente.
Não há nomes para as metaclasses; entretanto, um ponteiro para qualquer objeto de classe pode ser referido com o tipo genérico Class
(semelhante ao tipo que id
está sendo usado para um ponteiro para qualquer objeto).
Como os métodos de classe são herdados por herança, como Smalltalk, as metaclasses devem seguir um esquema de herança paralelo ao das classes (por exemplo, se a classe-mãe da classe A é a classe B, então a classe-mãe da metaclasse de A é a metaclasse de B), exceto aquela da classe raiz.
Ao contrário de Smalltalk, a metaclasse da classe raiz herda da própria classe raiz (geralmente NSObject
usando a estrutura Cocoa ). Isso garante que todos os objetos de classe sejam, em última instância, instâncias da classe raiz, para que você possa usar os métodos de instância da classe raiz, geralmente métodos de utilitário úteis para objetos, nos próprios objetos de classe.
Uma vez que os objetos de metaclasse não se comportam de maneira diferente (você não pode adicionar métodos de classe para uma metaclasse, portanto, todos os objetos de metaclasse têm os mesmos métodos), eles são todos instâncias da mesma classe - a metaclasse da classe raiz (ao contrário de Smalltalk). Portanto, a metaclasse da classe raiz é uma instância de si mesma. A razão para isso é que todas as metaclasses herdam da classe raiz; portanto, eles devem herdar os métodos de classe da classe raiz.
Suporte em idiomas e ferramentas
A seguir estão algumas das linguagens de programação mais proeminentes que oferecem suporte a metaclasses.
- Lisp comum , via CLOS
- Delphi e outras versões do Object Pascal influenciadas por ele
- Groovy
- Objective-C
- Pitão
- Perl , por meio do pragma metaclasse, bem como Moose
- Rubi
- Conversa fiada
- C ++ (planejado para C ++ 23 )
Algumas linguagens menos difundidas que suportam metaclasses incluem OpenJava , OpenC ++ , OpenAda , CorbaScript , ObjVLisp , Object-Z , MODEL-K , XOTcl e MELDC . Vários desses idiomas datam do início dos anos 1990 e são de interesse acadêmico.
Logtalk , uma extensão orientada a objetos do Prolog , também suporta metaclasses.
O Resource Description Framework (RDF) e a Unified Modeling Language (UML) oferecem suporte a metaclasses.