As funções de primeira classe são um substituto para o padrão Strategy?

15

O padrão de design da estratégia é geralmente considerado como um substituto para funções de primeira classe em idiomas que não as possuem.

Por exemplo, digamos que você queira passar a funcionalidade para um objeto. Em Java você teria que passar no objeto outro objeto que encapsula o comportamento desejado. Em uma linguagem como Ruby, você passaria a própria funcionalidade na forma de uma função anônima.

No entanto, eu estava pensando sobre isso e decidi que talvez a Estratégia ofereça mais do que uma simples função anônima.

Isso ocorre porque um objeto pode manter o estado que existe independentemente do período em que o método é executado. No entanto, uma função anônima por si só pode manter o estado que deixa de existir no momento em que a função termina a execução.

Em uma linguagem orientada a objetos que suporta funções de primeira classe, o padrão de estratégia tem alguma vantagem sobre o uso de funções?

    
por Aviv Cohn 16.08.2014 / 13:34
fonte

4 respostas

13

Quando o idioma suporta referências à função ( Java faz desde a versão 8 ), estas são frequentemente uma boa alternativa para estratégias, porque geralmente expressam a mesma coisa com menos sintaxe. No entanto, existem alguns casos em que um objeto real pode ser útil.

Uma interface de estratégia pode ter vários métodos. Vamos pegar uma interface RouteFindingStragegy como exemplo, que encapsula diferentes algoritmos de localização de rotas. Poderia declarar métodos como

  • Route findShortestRoute(Node start, Node destination)
  • boolean doesRouteExist(Node start, Node destination)
  • Route[] findAllPossibleRoutes(Node start, Node destination)
  • Route findShortestRouteToClosestDestination(Node start, Node[] destinations)
  • Route findTravelingSalesmanRoute(Node[] stations)

que então seria implementado pela estratégia. Alguns algoritmos de localização de rotas podem permitir otimizações internas para alguns desses casos de uso e outros podem não, portanto, o implementador pode decidir como implementar cada um desses métodos.

Outro caso é quando a estratégia tem um estado interno. Claro, em algumas línguas os closures podem ter um estado interno, mas quando esse estado interno se torna muito complexo, muitas vezes torna-se mais elegante promover o fechamento para uma classe completa.

    
por 16.08.2014 / 13:55
fonte
5

Não é verdade que uma função anônima possa manter apenas o estado que deixa de existir quando a função termina a execução.

Tome o seguinte exemplo no Common Lisp:

(defun number-strings (ss)
  (let ((counter 0))
    (mapcar #'(lambda (s) (format nil "~a: ~a" (incf counter) s)) ss)))

Esta função recebe uma lista de strings e preenche um contador para cada elemento da lista. Então, por exemplo, invocar

(number-strings '("a" "b" "c"))

("1: a" "2: b" "3: c")

A função number-strings usa internamente uma função anônima com uma variável counter que mantém o estado (o valor atual do contador) que é reutilizado toda vez que a função é invocada.

Em geral, você pode pensar em um fechamento como um objeto com apenas um método. Como alternativa, um objeto é uma coleção de closures que compartilham as mesmas variáveis de fechamento. Então não tenho certeza se há casos em que você precisa usar um objeto em vez de um fechamento: eu diria que ambos são formas de olhar para o mesmo padrão a partir de diferentes perspectivas.

Em particular, o padrão de estratégia requer um objeto com apenas um método, portanto, um fechamento deve fazer o trabalho. Mas, como Philipp observou em sua resposta, dependendo das circunstâncias (estado complexo) e das linguagens de programação, você pode obter uma solução mais elegante usando objetos.

    
por 16.08.2014 / 13:49
fonte
1

Só porque dois projetos podem resolver o mesmo problema não significa que eles sejam substituições diretas um pelo outro.

Se você precisar controlar o estado em um programa funcional, não altere uma variável fechada, mesmo que o idioma permita. Você organiza para chamar uma função que aceita um estado como um argumento e retorna o novo estado como seu valor de retorno.

Sua arquitetura será muito diferente, mas você atingirá o mesmo objetivo. Não tente forçar os padrões de um paradigma diretamente no outro.

    
por 17.08.2014 / 18:26
fonte
1

Estratégia é um conceito , uma receita útil para resolver um problema recorrente e particular. Não é uma construção de linguagem, nem é sobre qualquer forma de implementação . Um fechamento pode ser usado para implementar a estratégia um dia e o observador no dia seguinte.

A estratégia term é mais útil em conversas com outros programadores para expressar sua intenção de forma concisa. Não há nada de mágico nisso.

    
por 17.08.2014 / 21:47
fonte