Design em linguagens “mistas”: design orientado a objetos ou programação funcional?

11

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?

Lorenzo Dematté
fonte
E os atores?
kiritsuku
Esqueça as aulas e todas essas coisas inúteis do OOPish. É melhor pensar em termos de módulos (consulte as linguagens SML e OCaml para obter uma inspiração).
SK-logic
@Antoras Se você pudesse uma comparação com uma abordagem baseada em atores, seria mais que bem-vindos :)
Lorenzo DEMATTÊ
2
@ dema80, OCaml é perfeitamente multi-paradigma. Os módulos não estão relacionados à programação funcional. Existe um sistema de módulo avançado em um Ada puramente imperativo, por exemplo. E toda a fama que o OP ganhou na verdade deve ir para os módulos - tudo de bom no OP é apenas várias formas de simular a funcionalidade dos módulos. Você pode esquecer totalmente todas essas classes e usar sua sintaxe para expressar módulos, pensando em termos de módulos, não em OOP. Aliás, foi exatamente o que a Microsoft fez com o mscorlib - não há muita POO lá, apenas módulos e namespaces.
SK-logic
1
Eu acho que a melhor pergunta é "Existe alguma organização ou clareza que você perde fazendo da maneira do FP?"
djechlin

Respostas:

0

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.

Filip Dupanović
fonte
Eu acho que essa resposta é um pouco equivocada. A programação funcional não é uma programação (apenas com) funções. A programação funcional também usa abstrações, então não há dicotomia aqui.
Doval
"composição ou herança, onde você terá um pouco mais de liberdade para reutilizar e estender o comportamento existente", você está falando assim NÃO é um dos principais benefícios do FP puro. modularidade, composição e reutilização acontecem naturalmente quando você codifica funcionalmente, isso não é uma "coisa de OOP" #
sara
@ FilipDupanović Eu estava me referindo à reutilização e extensão do código existente. funções puras são provavelmente as coisas mais composíveis em toda a programação. você escreveu como se não pudesse gerenciar a complexidade em um ambiente funcional puro. gerenciar a complexidade compondo partes simples em partes maiores, mas ainda assim simples e opacas, é o cerne do FP, e muitos argumentam que é ainda melhor do que o OOP. Não concordo com a dicotomia entre "one-liners funcionais que não escalam VS OOP sólido que não escala nenhum problema" que você propõe.
Sara
você disse que o FP era inferior quando se trata de compor e reutilizar códigos e que, quando os aplicativos se tornarem mais complexos, o OOP será mais ou menos sempre "melhor". Eu afirmo que isso é simplesmente falso e enganoso, e por isso pensei que isso justificaria um voto negativo. Isso é realmente "fanatismo purista"? Não discutirei isso mais aqui, pois os comentários não são para discussões prolongadas como essas.
Sara
5

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.

DeadMG
fonte
Eu concordo e tenho o mesmo sentimento.
Lorenzo Dematté 29/03/12
Eu concordo e tenho o mesmo sentimento. Mas eu estava meio que "trapaceando" com meu código funcional: no meu caso, é tudo o que eu preciso, mas e se eu precisar adicionar e remover "observadores"? Eu editei minha pergunta
Lorenzo Dematté
5

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.

Doval
fonte
+1 para (IMO) uma boa resposta. Além disso, obrigado pelo link para o artigo: eu já havia lido há algum tempo e depois o perdido. Agora eu encontrei novamente.
Giorgio
-1

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.

DPD
fonte
1
"Você pode, com muito cuidado, escrever um programa inteiro contendo objetos que não têm estado, imitando o FP.", É claro, e através da disciplina você pode fazer o mesmo em linguagens imperativas. :) Em geral, eu não acho que a padrões de design como algo para compensar limitações, mas considere o caso do padrão Visitor ..
Lorenzo DEMATTÊ
Além disso, não estou preocupado com o código: no entanto, gostaria de confrontar com colegas programadores sobre a abordagem (pelo que entendi, é para isso que programmers.se é, caso contrário, eu teria postado meu Q no SO)
Lorenzo Dematté
Toda vez que você identifica um padrão repetitivo, não passa de uma indicação de uma severa limitação de sua linguagem e estrutura de modelagem. Não haverá padrões no mundo ideal.
SK-logic
@ SK-logic: Infelizmente o mundo não é ideal e o mundo real tem padrões. Qual é porque linguagens OO tem herança, para implementar o código repeateable sem ter que reescrevê-los em todo objeto do mundo again.Real ter um estado, é por isso que há um padrão Estado
DPD
1
@ SK-logic: Estou ciente de que algumas linguagens são programáveis ​​e algumas permitem a imposição de efeitos do sistema de tipos também. Meu argumento é que, como "OOP", "padrão" é um termo tristemente incompreendido. Um padrão é algo que continua reaparecendo de formas semelhantes, mas diferentes , que não admite uma solução uniforme . Às vezes, você pode fazer com que essas quase-petições desapareçam - os vários métodos tornam o Visitor / despacho duplo redundante. Isso não implica "todos os padrões são sinais de deficiência", porque isso significa que o mundo real é deficiente. Padrões originados na arquitetura , não no software.
Frank Shearar