Thursday, March 08, 2007 12:47 PM

Escrevendo a minha da próxima revista Mundo.Net, sobre ferramentas de Mock, tive contato com as interfaces fluentes. O Rhino Mocks permite que você estabeleça as expectativas do mock em uma interface fluente. Veja como fica o código:

    Expect.Call(MailerMock.SendMessage("mail@host.com")).IgnoreArguments().Return(5);

    //a mesma chamada em um layout mais fácil de entender
    Expect.Call(MailerMock.SendMessage("mail@host.com"))
            .IgnoreArguments()
            .Return(5);

Ao invés de criar vários objetos e incluir cada objeto em uma propriedade do objeto principal, você simplesmente vai chamando os métodos um após o outro. Fica muito mais fácil de escrever o código, com menos linhas, e muito mais fácil de ler. É quase como uma frase separada por vírgulas. Com o código acima, por exemplo, você espera uma chamada no método SendMessage do objeto MailerMock, não quer validar os argumentos recebidos e quer forçar o retorno da chamada seja igual a 5.

Achei bem interessante e resolvi implementar o exemplo que o Martin Fowler mostra no seu post sobre o assunto. Ele mostra o exemplo de uma criação de uma ordem de compra em uma linha de código. A coisa é mais simples do que parece a primeira vista.

Comecei implementando as classes Customer, Order e OrderLine. Cada um tem suas propriedades, como nome, endereço de entrega, etc. O customer tem um método NewOrder que retorna um objeto Order. Até ai o código é “normal”, então tenho que criar o método With, que recebe os dados para criar uma OrderLine. O pulo do gato é retornar o próprio o objeto Order no método. O método ShipTo segue o mesmo princípio, recebe o endereço, grava na variável e retorna o this.

    public class Customer
    {
        private string _name;

        public string Name
        {
            get { return _name; }
        }

        public Customer(string name)
        {
            _name = name;
        }

        public Order NewOrder()
        {
            return new Order(this);
        }
    }
    public class Order
    {
        private Customer _customer;
        private List<OrderLine> _orderLines;
        private string _shipToAddress;

        public Customer Customer
        {
            get { return _customer; }
        }
        public List<OrderLine> OrderLines
        {
            get { return _orderLines; }
        }
        public string ShipToAddress
        {
            get { return _shipToAddress; }
        }

        public Order(Customer customer)
        {
            _orderLines = new List<OrderLine>();
            _customer = customer;
        }
        public Order With(int itemId, float itemPrice)
        {
            _orderLines.Add(new OrderLine(itemId, itemPrice));

            return this;
        }
        public Order ShipTo(string address)
        {
            _shipToAddress = address;

            return this;
        }
    }

    public class OrderLine
    {
        private int _itemId;
        private float _itemPrice;

        public int ItemId
        {
            get { return _itemId; }
        }
        public float ItemPrice
        {
            get { return _itemPrice; }
        }

        public OrderLine(int itemId, float itemPrice)
        {
            _itemId = itemId;
            _itemPrice = itemPrice;
        }
    }

A criação da ordem tanto pode ser no método tradicional como utilizando a interface fluente, como mostra o código abaixo.

    //Modo tradicional
    Customer cust = new Customer("Other Customer");
    Order order = cust.NewOrder();

    order.OrderLines.Add(new OrderLine(1, 5.0F));
    order.OrderLines.Add(new OrderLine(2, 9.4F));
    order.ShipTo("My address");

    //Modo fluente
    Customer cust = new Customer("My Customer");

   Order order = cust.NewOrder()
                  .With(1, 10.0F)
                 .With(2, 12.3F)
                 .ShipTo("My address");

Bom, não foi tão difícil em um exemplo simples, a coisa pode ficar bem mais complicada quando você quer forçar uma ordem para as chamadas ou limitar a quantidade de vezes que um método pode ser chamado.

Algumas aplicações interessantes de interfaces fluentes:

Posts sobre aplicação prática de interfaces fluentes:

 

Comments

At 3/15/2007 12:11 PM, Eduardo Miranda said:

#  Interfaces fluentes II - A continuação

Post Comment
Title *
Name *
Email (never displayed)
Website
Comment * (Allowed tags: blockquote, a, strong, em, p, u, strike, super, sub, code)  
Please add 4 and 6 and type the answer here: