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 demaisoutras 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.

2 Comentários

  1. Angelo 26/02/2010 at 14:17 #

    bem legal.. o dynamic realmente é bem interessante..

  2. Camacho 03/08/2010 at 11:24 #

    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!

Deixe uma resposta