Programação Declarativa

TODO:

  • [ ] Imperativo vs Declarativo
  • [ ] Nomear variaveis e funções de forma declarativa
  • [ ] Nomes de funções declarativos
  • [ ] Evitar imperatividade no código(switch code, too much ifs).

Demonstação

Talvez seja uma rápida demonstração a melhor maneira de iniciarmos com o paradigma funcional. Realizaremos uma só tarefa de duas maneiras, na primeira utilizaremos os métodos já existentes no 'Core' e noutra o paradigma funcional. Em seguida, compararemos os dois métodos.

A aplicação – an e-commerce website

Digamos que estamos a construir uma aplicação do mundo real, um comércio eletrônico para uma empresa de feijão e café que tem como objetivo aceitar pedidos por conrrespondência. Eles vendem café de qualidade e quantidade distintas, acarretando mudanças de preço.

Metodos imperativos

Primeiramente, vamos conferir o estilo imperativo. Para melhor demonstração, temos de criar objetos que mantenham os dados. Neles podemos buscar valores como se estivéssemos operando no banco de dados; e posteriormente, talvez possamos operá-los por lá. Mas, no momento, assumiremos que são definidos de forma estática.

// create some objects to store the data
var columbian = {
  name: 'columbian',
  basePrice: 5
};
var frenchRoast = {
  name: 'french roast',
  basePrice: 8
};
var decaf = {
  name: 'decaf',
  basePrice: 6
};

// we'll use a helper function to calculate the cost
// according to the size and print it to an HTML list
function printPrice( coffee, size ) {
  if( size == 'small' ) {
    var price = coffee.basePrice + 2;
  }
  else if( size == 'medium' ) {
    var price = coffee.basePrice + 4;
  }
  else {
    var price = coffee.basePrice + 6
  }

  // create the new html list item
  var node = document.createElement( 'li' );
  var label = coffee.name + ' ' + size;
  var textnode = document.createTextNode( label + ' price: $'+price );
  node.appendChild( textnode );
  document.getElementById( 'products1' ).appendChild( node );
}

// now all we need to do is call the printPrice function
// for every sigle combination of coffee type and size
printPrice( columbian, 'small' );
printPrice( columbian, 'medium' );
printPrice( decaf, 'medium' );

Notemos a simplicidade do código. E se houvessem mais sabores além dos três? quem sabe 20, ou talvez 40? Imaginemos então que se além do tamanho, existisse as opções orgânico e não orgânico. Haja código!

Usando este método, estamos dizendo à máquina o que imprimir para cada sabor e tamanho. Isso é o que há de pior em código imperativo - repetição.

Functional programming Enquanto no paradigma imperativo dizemos para a máquina como ela deve agir para resolver o problema - passo a passo -, no funcional o descrevemos matematicamente para que a máquina aja sob ele.

Tomando o princípio funcional, teremos o mesmo aplicativo com o seguinte código:

// separate the data and logic from the interface
var printPrice = function( price, label ) {
  var node = document.createElement( 'li' );
  var textnode = document.createTextNode( label + ' price: $' + price );
  node.appendChild( textnode );
  document.getElementById( 'products2' ).appendChild( node );
};

// create function objects for each type of coffee
var columbian = function() {
  this.name = 'columbian';
  this.basePrice = 5;
};
var frenchRoast = function() {
  this.name = 'french roast';
  this.basePrice = 8;
};
var decaf = function() {
  this.name = 'decaf';
  this.basePrice = 6;
};

//create object literals for te different sizes
var small = {
  getPrice : function() { return this.basePrice + 2 },
  getLabel : function() { return this.name + ' small' }
};
var medium = {
  getPrice : function() { return this.basePrice + 4 },
  getLabel : function() { return this.name + ' medium' }
};
var large = {
  getPrice : function() { return this.basePrice + 6 },
  getLabel : function() { return this.name + ' large' }
};

// put all the coffee types and sizes into arrays
var coffeeFlavors = [ columbian, frenchRoast, decaf ];
var coffeeSizes = [ small, medium, large ];

// build new objects that are combinations of the above
// and put them into a new array
var coffees = coffeeFlavors.reduce( function( previous, current ) {
  var newCoffee = coffeeSizes.map( function( mixin ) {
    // `plusmix` function for functional mixins, see Ch.7
    var newCoffeeObj = plusMixin( current, mixin );
    return new newCoffeeObj();
  });
  return previous.concat( newCoffee );
}, [] )

// we've now defined how to get the price and label for each
// coffee flavor and size combination, now we can just print them
coffees.forEach( function( coffee ) {
  printPrice( coffee.getPrice(), coffee.getLabel() );
})

Nos é evidente que este é enormemente maior em termos de modularidade. Torna-se agora tarefa simples adicionarmos novos tamanhos e sabores, segue-se abaixo um exemplo:

var arabica = function() {
  this.name = 'arabica',
  this.basePrice = 11;
};

var extraLarge = {
  getPrice : function() { return this.basePrice + 10 },
  getLabel : function() { return this.name + ' extra large' }
};

coffeeFlavors.push( arabica );
coffeeSizes.push( extraLarge );

Os arrays de objetos coffee são "misturados" com os de objetos size - com o auxílio da função plusMixin (consulte o Cap. 7, Programação funcional e orientada a objetos em javascript). As classes de tipo coffee é quem armazena em variáveis o nome do sabor e o preço padrão, as de tipo size contêm métodos para operarmos com os nomes e preços. A "mistura" acontece dentro de uma operação map(), que aplica uma função pura em cada elemento do array e retorna uma nova função dentro da operação reduce() - outra higher order function similar a função map(), exceto pelo fato dele "reduzir" todos os elementos de um Array num só elemento. E por conseguinte, o novo array contendo todas as combinações de tipos e tamanhos é iterado com o método forEach(); o forEach() é outra higher order function que aplica um callback em cada objeto do array. Neste exemplo, fornecemos uma função anônima que instancia os objetos e chama a função printPrice() passando os métodos getPrice() e getLabel() como argumentos.

Poderíamos ainda, deixar nosso exemplo mais funcional removendo a variável coffees e fazendo um encadeamento de funções - este é um poder secreto da programação funcional.

coffeeFlavors.reduce( function( previous, current ) {
  var newCoffee = coffeeSizes.map( function( mixin ) {
    // `plusMixin` function for functional mixins, see Ch. 7
    var newCoffeeObj = plusMixin( current, mixin );
    return new newCoffeeObj();
  });
  return previous.concat( newCoffee );
}, []).forEach(function( coffee ) {
  printPrice( coffee.getPrice(), coffee.getLabel() );
})

Além disso, o fluxo de controle não é "uma sentada de cima pra baixo" como aconteceu em nosso código imperativo. Na programação funcional, as higher-order functions tomam o lugar dos laços for e while, e, em consequência quase ou nenhuma importância é dada quanto a ordem de execução. Iniciantes tedem a tremer quando "batem o olho" em código quem tem como princípio o paradigma funcional, no começo pode até dar medo, confesso, mas basta um pouco de prática para pegarmos o jeito - não é doloroso -, e logo verá o quão bom é código funcional.

results matching ""

    No results matching ""