Nos últimos anos, os idiomas que eu gosto de usar estão se tornando cada vez mais "funcionais". Agora uso linguagens que são uma espécie de "híbrido": C #, F #, Scala. Eu gosto de projetar meu aplicativo usando classes que correspondem aos objetos do domínio e usar recursos funcionais, onde isso torna a codificação mais fácil, mais coincidente e mais segura (especialmente quando se opera em coleções ou quando passa funções).
No entanto, os dois mundos "colidem" quando se trata de padrões de design. O exemplo específico que eu enfrentei recentemente é o padrão Observer. Quero que um produtor notifique algum outro código (os "consumidores / observadores", por exemplo, um armazenamento de banco de dados, um criador de logs e assim por diante) quando um item for criado ou alterado.
Inicialmente, eu fiz "funcionalmente" assim:
producer.foo(item => { updateItemInDb(item); insertLog(item) })
// calls the function passed as argument as an item is processed
Mas agora estou pensando se devo usar uma abordagem mais "OO":
interface IItemObserver {
onNotify(Item)
}
class DBObserver : IItemObserver ...
class LogObserver: IItemObserver ...
producer.addObserver(new DBObserver)
producer.addObserver(new LogObserver)
producer.foo() //calls observer in a loop
Quais são os prós e contras das duas abordagens? Uma vez ouvi um guru de FP dizer que os padrões de design existem apenas por causa das limitações da linguagem, e é por isso que existem tão poucas em linguagens funcionais. Talvez isso possa ser um exemplo disso?
EDIT: No meu cenário particular, não preciso disso, mas .. como você implementaria a remoção e adição de "observadores" da maneira funcional? (Ou seja, como você implementaria todas as funcionalidades do padrão?) Apenas passando uma nova função, por exemplo?
fonte
Respostas:
Esse é um bom exemplo de duas abordagens diferentes que carregam a noção de executar uma tarefa fora do limite de preocupação com o objeto que está chamando.
Embora neste exemplo fique claro que você deve adotar a abordagem funcional, em geral isso dependerá realmente de quão complexo é o comportamento do objeto chamado. Se realmente for uma questão de comportamento complexo, onde você se aplicará frequentemente a uma lógica semelhante e os geradores de funções não puderem ser usados para expressá-la claramente, provavelmente desejará ir para a composição ou herança de classe, onde tenha um pouco mais de liberdade para reutilizar e estender o comportamento existente em uma base ad-hoc.
Um padrão que observei, porém, é que geralmente os desenvolvedores adotam a abordagem funcional inicialmente e somente quando a demanda por comportamento mais granular surge, eles decidem optar por uma abordagem baseada em classe. Eu sei, por exemplo, que o Django passou de visualizações baseadas em funções, carregadores de modelos e executores de testes para baseadas em classes, uma vez que os benefícios e requisitos se tornaram óbvios, mas não antes disso.
fonte
A versão funcional é muito mais curta, mais fácil de manter, mais fácil de ler e geralmente muito superior em quase todos os aspectos imagináveis.
Muitos, embora longe de todos os padrões, devem compensar a falta de recursos no POO, como os Observadores. Essas são muito melhor modeladas funcionalmente.
fonte
O seu "guru do FP" está parcialmente certo; muitos padrões OO são hacks para fazer coisas funcionais. (Sua alegação de que essa é a razão pela qual existem poucas linguagens de FP parece duvidosa.) Os padrões de Observador e Estratégia estão tentando imitar funções de primeira classe. O Padrão do Visitante é um truque para simular a correspondência de padrões. Você
IItemObserver
é apenas uma função disfarçada. Fingir que é diferente de qualquer outra função que pegue um item não comprará nada para você.Objetos são apenas um tipo de abstração de dados. Este artigo ajudará a esclarecer esse assunto. Os objetos podem ser úteis, mas é importante reconhecer que eles não são apropriados para tudo. Não há dicotomia; basta escolher a ferramenta certa para o trabalho certo e a programação funcional não exige que você desista dos objetos. Além disso, a programação funcional é mais do que apenas usar funções. Também se trata de minimizar efeitos colaterais e mutações.
fonte
Não consigo responder à pergunta porque não sou bom em linguagens funcionais; mas acho que você deve se preocupar menos com a abordagem, desde que o que você tem funcione. Pelo que entendi, desde que você não adicione mais ouvintes no futuro ou não altere os ouvintes durante a execução, é possível pular muito bem o padrão Observador aqui.
Não concordo que os padrões de design compensem "limitações" nos idiomas OO. Eles estão lá para fazer bom uso dos recursos do OOP. Polimorfismo e herança são características, não limitações. Os padrões de design usam esses recursos para promover um design flexível. Você pode criar um programa absolutamente não-OO em um OO. Você pode, com muito cuidado, escrever um programa inteiro contendo objetos sem estado, imitando o FP.
fonte