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, Carele próprio é uma instância de type. O código-fonte da Carclasse, mostrado acima, não inclui detalhes como o tamanho em bytes dos Carobjetos, 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 Carobjeto é 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 Carcomportamento 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 Carpode ser reescrita para usar essa metaclasse. No Python 3, isso é feito fornecendo um "argumento de palavra-chave" metaclasspara 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 Carpode 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

A hierarquia de metaclasse Smalltalk-80 como um diagrama UML
Diagrama das relações de herança e instância entre classes e metaclasses em Smalltalk

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 Carchamado 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 classresultados no objeto da metaclasse Carcujo nome é Car class(pode-se confirmar isso avaliando Car class nameque 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 Integera pesquisa, o método começa em Integer classe 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 Csua 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 classque 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. Metaclasstem 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 um Car, 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 como Integer, ou Widget, ou Car, 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

Em Ruby

Ruby purifica o conceito Smalltalk-80 de metaclasses introduzindo eigenclasses , removendo a Metaclassclasse e (des) redefinindo o mapa de classe-of. A mudança pode ser esquematizada da seguinte forma:

Smalltalk-80
Aulas

Metaclasses implícitas
  

Objetos terminais
Rubi
Aulas
Eigenclasses de
classes
Eigenclasses
de
eigenclasses

Objetos terminais
Eigenclasses de
objetos terminais

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 Ae B e objetos terminais ue 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
Metaclasses implícitas em Smalltalk-80 - Uma estrutura de amostra Eigenclasses in Ruby - Uma estrutura de amostra

O diagrama à direita também fornece uma imagem de avaliação preguiçosa de eigenclasses em Ruby. O vobjeto 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 Classclasse (denotada por cno diagrama). Class, e Structsão as únicas classes que possuem classes como instâncias. A subclasse de Classnão é permitida. Seguindo a definição padrão de metaclasses, podemos concluir que Classe Structsã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 classmétodo de introspecção em Ruby e Smalltalk. Enquanto o mapa x ↦ x. classcoincide em objetos terminais, difere na restrição de classes. Como já mencionado acima, para uma classe x, a expressão Ruby é x.classavaliada constantemente como Class. Em Smalltalk-80, se xfor uma classe, a expressão x classcorresponde ao Ruby x.singleton_class - que avalia o eigenclass de x.

Em Objective-C

Diagrama das relações de herança e instância entre classes e metaclasses em Objective-C. Observe que Objective-C tem várias classes raiz; cada classe raiz teria uma hierarquia separada. Este diagrama mostra apenas a hierarquia de um exemplo de classe raiz NSObject. Cada outra classe raiz teria uma hierarquia semelhante.

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 idestá 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 NSObjectusando 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.

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.

Veja também

Referências