Na Definição de Orientação a Objetos de Alan Kays, existe esta definição que parcialmente não entendo:
OOP para mim significa apenas mensagens, retenção e proteção local e ocultação de processos estatais e LateBinding extremo de todas as coisas.
Mas o que significa "LateBinding"? Como posso aplicar isso em um idioma como C #? E por que isso é tão importante?
object-oriented
object-oriented-design
Luca Zulian
fonte
fonte
Respostas:
"Vinculação" refere-se ao ato de resolver um nome de método para um pedaço de código invocável. Geralmente, a chamada de função pode ser resolvida no tempo de compilação ou no tempo do link. Um exemplo de linguagem que usa ligação estática é C:
Aqui, a chamada
foo(40)
pode ser resolvida pelo compilador. Este início permite certas otimizações, como inlining. As vantagens mais importantes são:Por outro lado, alguns idiomas adiam a resolução da função até o último momento possível. Um exemplo é o Python, onde podemos redefinir símbolos rapidamente:
Este é um exemplo de ligação tardia. Embora faça uma verificação rigorosa de tipo de maneira não razoável (a verificação de tipo só pode ser feita em tempo de execução), é muito mais flexível e nos permite expressar conceitos que não podem ser expressos dentro dos limites da digitação estática ou da ligação antecipada. Por exemplo, podemos adicionar novas funções em tempo de execução.
O despacho de método, como geralmente implementado nas linguagens OOP "estáticas", está entre esses dois extremos: Uma classe declara o tipo de todas as operações suportadas desde o início, portanto, elas são conhecidas estaticamente e podem ser checadas. Podemos então criar uma tabela de pesquisa simples (VTable) que aponte para a implementação real. Cada objeto contém um ponteiro para uma tabela. O sistema de tipos garante que qualquer objeto obtido tenha uma vtable adequada, mas não temos idéia em tempo de compilação qual é o valor dessa tabela de pesquisa. Portanto, os objetos podem ser usados para transmitir funções como dados (metade da razão pela qual a OOP e a programação de funções são equivalentes). Vtables pode ser facilmente implementado em qualquer idioma que suporte ponteiros de função, como C.
Esse tipo de pesquisa de método também é conhecido como "despacho dinâmico" e em algum momento entre a ligação inicial e a ligação tardia. Considero que o envio de método dinâmico é a propriedade definidora central da programação OOP, com qualquer outra coisa (por exemplo, encapsulamento, subtipagem ...) como secundária. Ele nos permite introduzir polimorfismo em nosso código e até adicionar novo comportamento a um pedaço de código sem precisar recompilar! No exemplo C, qualquer pessoa pode adicionar uma nova vtable e passar um objeto com essa vtable para
sayHelloToMeredith()
.Embora seja uma ligação tardia, essa não é a "ligação tardia extrema" preferida por Kay. Em vez do modelo conceitual “despacho de método por meio de ponteiros de função”, ele usa “despacho de método por passagem de mensagem”. Essa é uma distinção importante porque a passagem de mensagens é muito mais geral. Nesse modelo, cada objeto tem uma caixa de entrada onde outros objetos podem colocar mensagens. O objeto receptor pode então tentar interpretar essa mensagem. O sistema OOP mais conhecido é a WWW. Aqui, as mensagens são solicitações HTTP e servidores são objetos.
Por exemplo, posso perguntar ao servidor programmers.stackexchange.se
GET /questions/301919/
. Compare isso com a notaçãoprogrammers.get("/questions/301919/")
. O servidor pode recusar esta solicitação ou me devolver um erro ou pode me responder sua pergunta.O poder da passagem de mensagens é que ele se adapta muito bem: nenhum dado é compartilhado (apenas transferido), tudo pode acontecer de forma assíncrona e os objetos podem interpretar as mensagens da maneira que desejarem. Isso torna uma mensagem que passa pelo sistema OOP facilmente extensível. Posso enviar mensagens que nem todos podem entender e recuperar meu resultado esperado ou um erro. O objeto não precisa declarar antecipadamente a quais mensagens ele responderá.
Isso coloca a responsabilidade de manter a correção no receptor de uma mensagem, um pensamento também conhecido como encapsulamento. Por exemplo, não consigo ler um arquivo de um servidor HTTP sem solicitá-lo por meio de uma mensagem HTTP. Isso permite que o servidor HTTP recuse minha solicitação, por exemplo, se eu não tiver permissões. Em OOP de menor escala, isso significa que não tenho acesso de leitura e gravação ao estado interno de um objeto, mas devo passar por métodos públicos. Um servidor HTTP também não precisa me servir um arquivo. Pode ser conteúdo gerado dinamicamente a partir de um banco de dados. Na OOP real, o mecanismo de como um objeto responde às mensagens pode ser desativado, sem que o usuário perceba. Isso é mais forte que a "reflexão", mas geralmente um protocolo completo de meta-objeto. Meu exemplo de C acima não pode alterar o mecanismo de despacho em tempo de execução.
A capacidade de alterar o mecanismo de despacho implica em ligação tardia, pois todas as mensagens são roteadas por código definido pelo usuário. E isso é extremamente poderoso: dado um protocolo de meta-objeto, posso adicionar recursos como classes, protótipos, herança, classes abstratas, interfaces, características, herança múltipla, despacho múltiplo, programação orientada a aspectos, reflexão, invocação de método remoto, objetos proxy etc. para um idioma que não inicia com esses recursos. Esse poder de evoluir está completamente ausente de linguagens mais estáticas, como C #, Java ou C ++.
fonte
A ligação tardia se refere a como os objetos se comunicam. O ideal que Alan está tentando alcançar é que os objetos sejam tão fracamente acoplados quanto possível. Em outras palavras, um objeto precisa saber o mínimo possível para se comunicar com outro objeto.
Por quê? Porque isso incentiva a capacidade de alterar partes do sistema de forma independente e permite que ele cresça e mude organicamente.
Por exemplo, em C #, você pode escrever um método para
obj1
algo comoobj2.doSomething()
. Você pode ver isso como seobj1
comunicarobj2
. Para que isso aconteça em C #, éobj1
necessário conhecer bastanteobj2
. Será necessário conhecer sua classe. Teria verificado se a classe possui um método chamadodoSomething
e se existe uma versão desse método que aceita zero parâmetros.Agora imagine um sistema para o qual você está enviando uma mensagem através de uma rede ou similar. você pode escrever algo como
Runtime.sendMsg(ipAddress, "doSomething")
. Nesse caso, você não precisa saber muito sobre a máquina com a qual está se comunicando; presumivelmente, pode ser contatado via IP e fará alguma coisa quando receber a string "doSomething". Mas, caso contrário, você sabe muito pouco.Agora imagine que é assim que os objetos se comunicam. Você conhece um endereço e pode enviar mensagens arbitrárias para esse endereço com algum tipo de função "caixa postal". Nesse caso,
obj1
não precisa saber muitoobj2
, apenas o endereço. Nem precisa saber que entendedoSomething
.Esse é basicamente o ponto crucial da ligação tardia. Agora, em idiomas que o utilizam, como Smalltalk e ObjectiveC, geralmente há um pouco de açúcar sintático para ocultar a função de caixa postal. Mas, caso contrário, a ideia é a mesma.
Em C #, você pode replicá-lo, meio que, tendo uma
Runtime
classe que aceita um objeto ref e uma string e usa reflexão para encontrar o método e invocá-lo (ele começará a ficar complicado com argumentos e valores de retorno, mas seria possível feio).Editar: para acalmar alguma confusão com relação ao significado da ligação tardia. Nesta resposta, estou me referindo à ligação tardia, pois entendo que Alan Kay quis dizer isso e o implementou no Smalltalk. Não é o uso moderno e mais comum do termo que geralmente se refere ao envio dinâmico. O último cobre o atraso na resolução do método exato até o tempo de execução, mas ainda requer algumas informações de tipo para o receptor em tempo de compilação.
fonte