RESTfulie com C# – O poder do dynamic
Há um mês lançamos uma versão inicial do projeto Restfulie para C#, projeto que tem ganhado bastante visibilidade. Para deixa-lo tão fácil quanto a versão original em Ruby, utilizamos as novas características dinâmicas da versão 4.0 do C#, alterando a estrutura de objetos em tempo de execução. Algumas pessoas acham esse recurso perigoso demais, outras acham a escolha certa.
Para enxergar o uso da nova palavra chave dynamic
, consideremos um recurso REST que representa um pedido com sua representação em XML como a que segue:
<pedido> <data>26/12/2009 11:40</data> <total>300.00</total> <atom:link rel=”refresh” href=”http://www.caelum.com.br/pedidos/1″ xmlns:atom=”http://www.w3.org/2005/Atom”/> <atom:link rel=”update” href=”http://www.caelum.com.br/pedidos/1″ xmlns:atom=”…”/> <atom:link rel=”pagar” href=”http://www.caelum.com.br/pedidos’/1/pagar” xmlns:atom=”…”/> <atom:link rel=”excluir” href=”http://www.caelum.com.br/pedidos/1″ xmlns:atom=”…”/> <atom:link rel=”obterCliente” href=”http://www.caelum.com.br/pedidos/1/clientexmlns:atom=” xmlns:atom=”…”/> </pedido>
É interessante notar que neste XML temos os dados (data, total) e temos ações que podem ser executadas sobre este recurso. Por exemplo, depois que obtivemos a representação deste recurso, é possível executar algumas ações, descritas através de links no próprio xml: pagar
, excluir
, obterCliente
. Para obter a representação de um recurso no restfulie C# fazemos da seguinte maneira:
dynamic pedido = Restfulie.At(“www.caelum.com.br\pedidos\1”).Get();
Repare que o primeiro passo que fazemos ao declarar a variável pedido é ignorar o seu tipo. Ou melhor, dizer explicitamente que ela é dinâmica, através do tipo dynamic. Isso nos trás uma série de vantagens como, por exemplo, simplesmente acessar suas properties:
Console.WriteLine(string.Format(“A data do pedido é: {0}”, pedido.Data)); Console.WriteLine(string.Format(“O valor total do pedido é: {0}”, pedido.Total));
Além de podermos acessar suas properties, podemos também seguir os links que estão disponíveis na representação, utilizando invocações de métodos:
pedido.Pagar(); pedido.Excluir(); dynamic cliente = pedido.obterCliente(); Console.WriteLine(string.Format(“Nome do cliente: {0}”, cliente.Nome));
Isso é bastante poderoso: perceba que do lado cliente não precisamos definir nada, apenas dizer que uma referência é dinâmica. Mas como esta mágica acontece com o C#?
Além de existir a palavra reservada dynamic
existe uma classe DynamicObject no C#. Diferente de Ruby, em C#, nem todos os objetos podem ser modificados em Runtime. Para que seja possível adicionar comportamento dinâmico em a um objeto, precisamos estender a classe DynamicObject
.
Ao estender esta classe, ganhamos a oportunidade de alterar o comportamento do objeto. Quando fizermos então uma invocação a pedido.Total
, o que de fato tem que ocorrer aqui? De acordo com o xml de representação do recurso, devemos obter o valor que está na tag <Total>.
Para implementar isso utilizamos um recurso bem parecido com o method_missing em Ruby e, de certa forma, como as dynamic proxies do Java (apesar destas precisarem de interfaces explícitas). Vejamos a classe DynamicXmlResource
:
class DynamicXmlResource : DynamicObject { private XElement xmlRepresentation; public override bool TryGetMember(GetMemberBinder binder, out object result) { //pseudo código que encontra a tag xml de acordo //com o nome da property informada object = XmlRepresentation.FindXMLTagWithName( binder.Name) .ReadTheValue() return result != null; } }
Esta classe estende DynamicObject
e tem um atributo que é a representação do recurso em XML (xmlRepresentation). O mais interessante é o método TryGetMember
que intercepta qualquer invocação a um Get
de uma property e nos permite fazer o que for necessário. No nosso caso, o que é feito é buscar o valor da da property no xml, que nesse caso tem o nome igual ao da tag do xml. Com isso, toda vez que fizermos pedido.Total
, o que está acontecendo é a invocação de TryGetMember
da classe DynamicXmlResource
.
Da mesma forma que existe o método TryGetMember
, existem outros que nos permitem alterar o comportamento do objeto. No nosso projeto, também fizemos o uso do TryInvokeMember
:
class DynamicXmlResource : DynamicObject { private XElement xmlRepresentation; public override bool TryGetMember(GetMemberBinder binder, out object result) { result = XmlRepresentation.FindXMLTagWithName(binder.Name).ReadTheValue() return result != null; } public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { // pseudo código para pegar o link no xml a partir do nome do método object link = XmlRepresentation.FindRelAttributeWithName(Binder.Name).ReadTheLink(); if (link == null) throw new ArgumentException (string.Format("There is not method defined with name:", binder.Name)); // faz a chamada remota para o servidor através do link. HttpRemoteResponse response = (HttpRemoteResponse) this.InvokeRemoteResource(value.ToString(), binder.Name); return result != null; } }
Este método também é parecido com o method_missing
, mas neste caso ele intercepta a chamada de um método. No caso do restfulie, quando chamamos um método qualquer, o que ele faz é, procurar na representação XML e descobrir o link relacionado com o nome do método. Depois disso é só fazer uma requisição http. Novamente, quando fizermos a invocação pedido.Pagar(), ela será interceptada pelo TryInvokeMember
e executará uma invocação remota para o Restfulie Server.
Utilizando estas características do C# conseguimos alcançar algo muito próximo do que se faz com o Restfulie Ruby, como comparamos a seguir. Em Ruby:
pedido = Restfulie.at(“http://restfulie-test.heroku.com/orders/14”).get puts pedido.customer-name pedido.cancel
E, em C#, temos o código tão sucinto quanto:
dynamic pedido = Restfulie.At(“http://restfulie-test.heroku.com/orders/14.xml”).Get(); Console.WriteLine(pedido.customer_name); pedido.Cancel();
Uma das consequências da utilização de tipos dinâmicos é que perdemos as vantagens da fase da tipagem estática: não há code complete, e pode ser que a gente erre o nome de um membro, como customer_name
, e só viremos a saber disso em tempo de execução. Nada que testes unitários bem colocados não resolvam.
Como podemos ver, a utilização de tipos dinâmicos facilita bastante a utilização da api do Restfulie, assim como diversos outros frameworks vem tirando vantagem com o auxílio desse recurso.
Reforçando a tendência, o Java também anda nesse caminho, com a JSR 292, que melhora o suporte de linguagens dinâmicas na JVM. Em abril de 2003 Robert C. Martin escreveu o artigo “As linguagens dinâmicas vão substituir as estáticas?” e, no final deste artigo, ele faz seguinte pergunta: Estaremos todos nós programando numa linguagem dinamicamente tipada em 2010?. Isso não aconteceu, mas com certeza cada vez mais cenários de utilização estão surgindo, e a tendência é cada vez mais forte.
Programação, Mobile, Front-end, Design & UX, Infraestrutura e Business
bem legal.. o dynamic realmente é bem interessante..
O dynamic é para poucos… rsrs
Algumas features de uma linguagem de programação como interceptação de métodos geralmente fazem parte do arsenal de conhecimento de um desenvolvedor experiente. Ok, a Caelum possui esse perfil de desenvolvedor em seu quadro de colaboradores. Mas tratando-se do C#, pensei em como poderia colaborar para que programadores iniciantes tivessem seu primeiro contato com o básico de design pattern para que consiga dar seus passos nessa longa estrada.
Pensando em colocar na prática um exemplo de MVC, tive a alegria de publicar o livro “Desenvolvimento em Camadas com C# .NET”. Espero que seja útil aos programadores iniciantes / intermediários na linguagem C# .NET.
Parabéns à Caelum pelo blog, pelos cursos de alta qualidade e pela iniciativa de colaborar em projetos open source. A comunidade agradece!