A programação funcional substitui os padrões de design do GoF?

1047

Desde que comecei a aprender F # e OCaml no ano passado, li um grande número de artigos que insistem que os padrões de design (especialmente em Java) são soluções alternativas para os recursos ausentes em linguagens imperativas. Um artigo que encontrei faz uma afirmação bastante forte :

A maioria das pessoas que conheci leu o livro Design Patterns do Gang of Four (GoF). Qualquer programador que se preze diz a você que o livro é independente de idioma e que os padrões se aplicam à engenharia de software em geral, independentemente do idioma usado. Esta é uma reivindicação nobre. Infelizmente, está muito longe da verdade.

Linguagens funcionais são extremamente expressivas. Em uma linguagem funcional, não é necessário padrões de design, porque a linguagem provavelmente é de alto nível, você acaba programando conceitos que eliminam todos os padrões de design.

Os principais recursos da programação funcional (FP) incluem funções como valores de primeira classe, currying, valores imutáveis ​​etc. Não me parece óbvio que os padrões de design do OO estejam se aproximando de qualquer um desses recursos.

Além disso, em linguagens funcionais que suportam OOP (como F # e OCaml), parece-me óbvio que os programadores que usam essas linguagens usariam os mesmos padrões de design encontrados disponíveis para qualquer outra linguagem OOP. De fato, agora eu uso F # e OCaml todos os dias, e não há diferenças marcantes entre os padrões que uso nessas linguagens e os padrões que uso quando escrevo em Java.

Existe alguma verdade na alegação de que a programação funcional elimina a necessidade de padrões de design de OOP? Em caso afirmativo, você poderia postar ou vincular um exemplo de um padrão de design típico de POO e seu equivalente funcional?

Julieta
fonte
18
Você pode olhar para o artigo de Steve Yegge ( steve-yegge.blogspot.com/2006/03/… )
Ralph
27
"o livro é independente da linguagem e os padrões se aplicam à engenharia de software em geral" - deve-se notar que o livro discorda dessa afirmação, no sentido de que algumas linguagens não precisam expressar certas coisas, como padrões de design: "Nossos padrões assuma os recursos de linguagem no nível Smalltalk / C ++, e essa escolha determina o que pode e não pode ser implementado facilmente [...] O CLOS possui vários métodos, por exemplo, que diminuem a necessidade de um padrão como o Visitor (Página 331). " (page 4)
Guildenstern
6
Lembre-se também de que muitos padrões de design nem são necessários em linguagens imperativas de nível suficientemente alto.
R. Barzell 25/02
3
@ R.Barzell O que são essas "linguagens imperativas de nível suficientemente alto"? Obrigado.
cibercitizen1
5
@ cibercitizen1 linguagens tipificadas por pato com suporte para funções de ordem superior e funções anônimas. Esses recursos fornecem grande parte da energia que muitos padrões de design deveriam fornecer.
R. Barzell

Respostas:

1077

O post que você citou exagera um pouco sua afirmação. O FP não elimina a necessidade de padrões de design. O termo "padrões de design" simplesmente não é amplamente usado para descrever a mesma coisa nas linguagens FP. Mas eles existem. As linguagens funcionais possuem muitas regras de boas práticas no formato "quando você encontrar o problema X, use um código parecido com Y", que é basicamente o que é um padrão de design.

No entanto, é correto que a maioria dos padrões de design específicos de OOP sejam praticamente irrelevantes nas linguagens funcionais.

Não acho que deva ser particularmente controverso dizer que os padrões de design em geral só existem para corrigir as deficiências da linguagem. E se outro idioma puder resolver o mesmo problema trivialmente, ele não precisará de um padrão de design para ele. Os usuários desse idioma podem nem estar cientes de que o problema existe , porque, bem, não é um problema nesse idioma.

Aqui está o que a Gangue dos Quatro tem a dizer sobre esse problema:

A escolha da linguagem de programação é importante porque influencia o ponto de vista de alguém. Nossos padrões assumem os recursos de linguagem no nível Smalltalk / C ++, e essa escolha determina o que pode e não pode ser implementado facilmente. Se assumirmos linguagens procedurais, poderíamos ter incluído padrões de design chamados "Herança", "Encapsulamento" e "Polimorfismo". Da mesma forma, alguns de nossos padrões são suportados diretamente pelas linguagens orientadas a objetos menos comuns. O CLOS possui vários métodos, por exemplo, que diminuem a necessidade de um padrão como o Visitor. De fato, existem diferenças suficientes entre o Smalltalk e o C ++ para significar que alguns padrões podem ser expressos mais facilmente em um idioma que no outro. (Veja Iterator, por exemplo.)

(O texto acima é uma citação do livro Introdução aos padrões de design, página 4, parágrafo 3)

Os principais recursos da programação funcional incluem funções como valores de primeira classe, curry, valores imutáveis ​​etc. Não me parece óbvio que os padrões de design do OO estejam se aproximando de qualquer um desses recursos.

Qual é o padrão de comando, se não uma aproximação das funções de primeira classe? :) Em uma linguagem FP, você simplesmente passaria uma função como argumento para outra função. Em uma linguagem OOP, é necessário agrupar a função em uma classe, a qual você pode instanciar e depois passar esse objeto para a outra função. O efeito é o mesmo, mas no OOP é chamado de padrão de design, e é necessário muito mais código. E qual é o padrão abstrato de fábrica, se não curry? Passe parâmetros para uma função um pouco de cada vez, para configurar que tipo de valor ele gera quando você finalmente o chama.

Portanto, sim, vários padrões de design do GoF são redundantes nas linguagens FP, porque existem alternativas mais poderosas e fáceis de usar.

Mas é claro que ainda existem padrões de design que não são resolvidos pelas linguagens FP. Qual é o equivalente FP de um singleton? (Ignorando por um momento que singletons geralmente são um padrão terrível de se usar.)

E funciona nos dois sentidos também. Como eu disse, o FP também tem seus padrões de design; as pessoas simplesmente não pensam nelas como tal.

Mas você pode ter passado por mônadas. Quais são eles, senão um padrão de design para "lidar com o estado global"? Esse é um problema tão simples nas linguagens OOP que não existe um padrão de design equivalente lá.

Não precisamos de um padrão de design para "incrementar uma variável estática" ou "ler a partir desse soquete", porque é exatamente isso que você faz .

Dizer que uma mônada é um padrão de design é tão absurdo quanto dizer que Inteiros com suas operações usuais e elemento zero é um padrão de design. Não, uma mônada é um padrão matemático , não um padrão de design.

Em linguagens funcionais (puras), efeitos colaterais e estado mutável são impossíveis, a menos que você trabalhe com o "padrão de design" da mônada ou com qualquer outro método para permitir a mesma coisa.

Além disso, em linguagens funcionais que suportam OOP (como F # e OCaml), parece-me óbvio que os programadores que usam essas linguagens usariam os mesmos padrões de design encontrados disponíveis para qualquer outra linguagem OOP. De fato, agora eu uso F # e OCaml todos os dias, e não há diferenças marcantes entre os padrões que uso nessas linguagens e os padrões que uso quando escrevo em Java.

Talvez porque você ainda esteja pensando imperativamente? Muitas pessoas, depois de lidar com linguagens imperativas a vida toda, têm dificuldade em abandonar esse hábito quando tentam uma linguagem funcional. (Eu já vi algumas tentativas engraçadas de F #, onde literalmente todas as funções eram apenas uma sequência de instruções 'let', basicamente como se você tivesse tomado um programa em C e substituído todos os pontos e vírgulas por 'let'. :))

Mas outra possibilidade pode ser que você simplesmente não tenha percebido que está resolvendo problemas trivialmente, o que exigiria padrões de design em uma linguagem OOP.

Quando você usa currying ou passa uma função como argumento para outro, pare e pense em como faria isso em uma linguagem OOP.

Existe alguma verdade na alegação de que a programação funcional elimina a necessidade de padrões de design de OOP?

Sim. :) Quando você trabalha em uma linguagem FP, não precisa mais dos padrões de design específicos de OOP. Mas você ainda precisa de alguns padrões gerais de design, como MVC ou outros itens não específicos de OOP, e precisa de alguns "novos padrões de design" específicos de FP. Todos os idiomas têm suas deficiências, e os padrões de design geralmente são como trabalhamos com eles.

De qualquer forma, você pode achar interessante tentar usar linguagens FP "mais limpas", como ML (meu favorito pessoal, pelo menos para fins de aprendizado) ou Haskell , onde você não tem a muleta OOP para recorrer quando você é confrontado com algo novo.


Como esperado, algumas pessoas se opuseram à minha definição de padrões de design como "corrigindo deficiências em uma linguagem", então aqui está minha justificativa:

Como já foi dito, a maioria dos padrões de design é específica para um paradigma de programação, ou às vezes até para uma linguagem específica. Freqüentemente, eles resolvem problemas que existem apenas nesse paradigma (consulte mônadas para PF ou fábricas abstratas para OOP).

Por que o padrão abstrato de fábrica não existe no FP? Porque o problema que ele tenta resolver não existe lá.

Portanto, se existe um problema nos idiomas OOP, o que não existe nos idiomas FP, isso claramente é uma falha nos idiomas OOP. O problema pode ser resolvido, mas seu idioma não o faz, mas requer um monte de código padrão para você contornar. Idealmente, gostaríamos que nossa linguagem de programação magicamente resolvesse todos os problemas. Qualquer problema que ainda exista existe, em princípio, uma falha na linguagem. ;)

jalf
fonte
73
Os padrões de design descrevem soluções gerais para problemas básicos. Mas é também isso que as linguagens e plataformas de programação fazem. Portanto, você usa padrões de design quando os idiomas e plataformas que você está usando não são suficientes.
Yfeldblum
135
S.Lott: Eles descrevem soluções para problemas que existem em um determinado idioma, sim. Não há padrão de design de comando nas linguagens FP, porque o problema que ele tenta resolver não existe. O que significa que eles resolvem problemas que a própria linguagem não pode resolver. Ou seja, falhas na linguagem
jalf
38
A mônada é um conceito matemático, e você a amplia com sua classificação. Certamente, você pode visualizar funções, monóides, mônadas, matrizes ou outros conceitos matemáticos como padrões de design, mas esses são mais como algoritmos e estruturas de dados ... conceitos fundamentais, independentes da linguagem.
Alexandru Nedelcu
41
Claro, as mônadas são um conceito de matemática, mas também são um padrão. O "padrão FP" das mônadas é um pouco distinto do conceito matemático das mônadas. O primeiro é um padrão usado para contornar certas "limitações" em linguagens FP pura. O último é um conceito matemático universal.
jalf
69
Observe que as mônadas em Haskell são usadas para outras coisas além do estado mutável, por exemplo, para exceções, continuações, compreensão de lista, análise, programação assíncrona e assim por diante. Mas todas essas aplicações de mônadas provavelmente poderiam ser chamadas de padrões.
JacquesB
152

Existe alguma verdade na alegação de que a programação funcional elimina a necessidade de padrões de design de OOP?

A programação funcional não é a mesma que a programação orientada a objetos. Os padrões de design orientado a objetos não se aplicam à programação funcional. Em vez disso, você possui padrões de design de programação funcional.

Para programação funcional, você não lerá os livros de padrões de design do OO; você lerá outros livros sobre padrões de design de FP.

agnóstico da linguagem

Não totalmente. Somente independente de idioma em relação aos idiomas OO. Os padrões de design não se aplicam a linguagens procedurais. Eles mal fazem sentido em um contexto de design de banco de dados relacional. Eles não se aplicam ao criar uma planilha.

um padrão de design típico de POO e seu equivalente funcional?

O acima não deveria existir. É como pedir um código processual reescrito como código OO. Ummm ... Se eu traduzir o Fortran original (ou C) em Java, não fiz nada além de traduzi-lo. Se eu reescrevê-lo totalmente em um paradigma OO, ele não será mais parecido com o Fortran ou C original - será irreconhecível.

Não há mapeamento simples do design OO ao design funcional. São maneiras muito diferentes de encarar o problema.

A programação funcional (como todos os estilos de programação) possui padrões de design. Os bancos de dados relacionais possuem padrões de design, o OO possui padrões de design e a programação procedural possui padrões de design. Tudo tem padrões de design, até a arquitetura dos edifícios.

Os padrões de design - como um conceito - são uma maneira atemporal de construção, independentemente da tecnologia ou do domínio do problema. No entanto, padrões de design específicos se aplicam a domínios e tecnologias de problemas específicos.

Todo mundo que pensa no que está fazendo descobrirá padrões de design.

S.Lott
fonte
12
MVC não é design de OO. É um projeto arquitetônico - esse padrão se aplica amplamente.
S.Lott 29/11/08
1
@Princess: programação funcional não é necessariamente mais simples. No seu exemplo, sim. Por outras coisas, o júri ainda está fora. Mas você descartou um padrão de design Java OO e adotou um padrão de design FP.
S.Lott 29/11/08
1
+1: prefiro esta resposta à resposta de Jalf acima. Embora alguns padrões de design resolvam deficiências no idioma, nem todos o fazem. Por exemplo, eu dificilmente diria que o padrão de design "desatando o nó recursivo" soluciona uma deficiência na linguagem; é apenas um idioma útil para diminuir as dependências.
precisa
9
O Java 8 incluirá encerramentos, também chamados de funções anônimas, e expressões lambda. Isso tornará o padrão de design do comando obsoleto para Java. Este é um exemplo de deficiência de idioma, não? Eles adicionaram um recurso ausente e agora você não precisa do padrão de design.
21412 Todd Chaffee
2
+1 para a frase final. Os padrões de design visam simplificar a programação e tornar os programas resultantes mais eficientes, no que eles pretendem fazer.
Sorter
46

Os comentários de Brian sobre a estreita ligação entre linguagem e padrão são objetivos,

A parte que falta nessa discussão é o conceito de idioma. O livro de James O. Coplien, "Advanced C ++", teve uma enorme influência aqui. Muito antes de descobrir Christopher Alexander e a Coluna Sem Nome (e você também não pode falar sensatamente sobre padrões sem ler Alexander), ele falou sobre a importância de dominar expressões idiomáticas para realmente aprender um idioma. Ele usou a cópia de seqüência de caracteres em C como exemplo. while(*from++ = *to++);Você pode ver isso como uma bandaid para um recurso de idioma ausente (ou recurso de biblioteca), mas o que realmente importa é que é uma unidade maior de pensamento ou expressão do que qualquer um dos suas partes.

É isso que os padrões e as línguas estão tentando fazer, para nos permitir expressar nossas intenções de forma mais sucinta. Quanto mais ricas as unidades de pensamento, mais complexos os pensamentos que você pode expressar. Ter um vocabulário rico e compartilhado em uma variedade de escalas - da arquitetura do sistema às pequenas modificações - nos permite ter conversas mais inteligentes e pensamentos sobre o que deveríamos estar fazendo.

Também podemos, como indivíduos, aprender. Qual é o ponto inteiro do exercício. Cada um de nós pode entender e usar coisas que nunca conseguiríamos pensar em nós mesmos. Idiomas, estruturas, bibliotecas, padrões, expressões idiomáticas etc. têm seu lugar no compartilhamento da riqueza intelectual.

Peter Mortensen
fonte
8
Obrigado! é disso que se trata os padrões - "divisão conceitual" para diminuir a carga cognitiva.
Randall Schulz
E as mônadas funcionais definitivamente pertencem a esta discussão.
Greg
@RandallSchulz: os recursos da linguagem (e seu uso idiomático, é claro) também se encaixariam bem na categoria de "divisão conceitual para reduzir a carga cognitiva".
Roy Tinker
39

O livro do GoF se vincula explicitamente ao OOP - o título é Design Patterns - Elementos de Software Orientado a Objetos Reutilizáveis (ênfase minha).

brilhante
fonte
26

Aqui está outro link, discutindo este tópico: http://blog.ezyang.com/2010/05/design-patterns-in-haskel/

Em seu blog, Edward descreve todos os 23 padrões GoF originais em termos de Haskell.

folone
fonte
4
O artigo não parece realmente mostrar padrões de design em Haskell, mas mostra como Haskell atende a essas necessidades sem os referidos padrões.
Fresheyeball 23/09/14
3
@Fresheyball: Depende da sua definição de padrões. O mapeamento de uma função sobre uma lista é uma variante do padrão Visitor? Eu geralmente pensei que a resposta fosse "sim". Padrões devem ir além de uma sintaxe específica. A função que está sendo aplicada pode ser quebrada como um objeto ou passada como um ponteiro de função, mas o conceito é o mesmo para mim. Você discorda?
srm
20

Quando você tenta analisar isso no nível de "padrões de design" (em geral) e "FP versus OOP", as respostas que você encontrará serão obscuras na melhor das hipóteses.

Ir um nível mais profundo em ambos os eixos, embora, e considerar os padrões de design específicos e características específicas de linguagem e as coisas se tornam mais claras.

Por exemplo, alguns padrões específicos, como Visitante , Estratégia , Comando e Observador definitivamente mudam ou desaparecem ao usar uma linguagem com tipos de dados algébricos e correspondência de padrões , fechamentos , funções de primeira classe , etc. Alguns outros padrões do livro GoF ainda 'ficar por aqui', no entanto.

Em geral, eu diria que, com o tempo, padrões específicos estão sendo eliminados por novos recursos de linguagem (ou apenas aumentando a popularidade). Este é o curso natural do design de linguagem; À medida que os idiomas se tornam mais de alto nível, abstrações que antes só podiam ser chamadas em um livro usando exemplos agora se tornam aplicativos de um recurso ou biblioteca de um idioma específico.

(Além disso: aqui está um blog recente que escrevi, que tem outros links para mais discussões sobre FP e padrões de design.)

Brian
fonte
Como você pode dizer que o padrão de visitantes "desaparece"? Não passa de "criar uma interface de visitante com vários métodos de visita" para "usar tipos de união e correspondência de padrões"?
Gabe
22
Sim, mas isso mudou de um padrão que é uma ideia de design sobre a qual você lê em um livro e aplica ao seu código, para "apenas código". Ou seja, "usar tipos de união e correspondência de padrões" é como você normalmente codifica coisas nesse idioma. (Analogia: se nenhum idioma tiver forloops e todos tiverem whileloops, então "For" poderá ser um padrão de iteração. Mas quando foré apenas uma construção suportada pelo idioma e como as pessoas codificam normalmente, não é um padrão - você não t precisa de um padrão, é código apenas, homem).
Brian
4
Em outras palavras, um teste decisivo talvez não tão ruim para "é um padrão" é: o código presente é escrito dessa maneira para um estudante de graduação do segundo ano do ensino médio com um ano de experiência em programação em seu idioma. Se você mostrar o código a eles, e eles disserem "que design inteligente", é um padrão. Se você mostrar o código a eles, e eles forem "bem, duh!", Não será um padrão. (E se você mostrar isso "visitante" para qualquer pessoa que tenha feito ML / F # / Haskell por um ano, eles vão "bem, duh!")
Brian
1
Brian: Eu acho que temos apenas definições diferentes de um "padrão". Considero qualquer abstração de design identificável como um padrão , enquanto você considera apenas abstrações não óbvias como um padrão . Só porque C # tem foreache Haskell tem mapMnão significa que eles não têm o padrão Iterator. Não vejo problema em dizer que o padrão Iterator é implementado como a interface genérica IEnumerable<T>em C # e a classe de tipo Traversableem Haskell.
Gabe
Pode ser que padrões não óbvios sejam úteis para engenheiros de software, mas todos os padrões são úteis para designers de linguagem. Ou seja, "se você estiver criando um novo idioma, inclua uma maneira limpa de expressar o padrão do iterador". Até os padrões óbvios são interessantes quando começamos a fazer a pergunta: "Existe uma sintaxe melhor para expressar essa idéia?" Afinal, é isso que leva alguém a criar um foreach.
Sr15
16

A apresentação de Norvig faz alusão a uma análise que eles fizeram de todos os padrões do GoF, e eles dizem que 16 dos 23 padrões tiveram implementações mais simples em linguagens funcionais ou eram simplesmente parte da linguagem. Portanto, presumivelmente, pelo menos sete deles eram a) igualmente complicados ou b) não estavam presentes no idioma. Infelizmente para nós, eles não são enumerados!

Eu acho que está claro que a maioria dos padrões "criacionais" ou "estruturais" no GoF são apenas truques para que os sistemas de tipos primitivos em Java ou C ++ façam o que você deseja. Mas o restante é digno de consideração, independentemente do idioma em que você programa.

Um pode ser o protótipo; Embora seja uma noção fundamental do JavaScript, ele deve ser implementado do zero em outros idiomas.

Um dos meus padrões favoritos é o padrão Null Object: representa a ausência de algo como um objeto que não faz nada do tipo apropriado. Pode ser mais fácil modelar em uma linguagem funcional. No entanto, a verdadeira conquista é a mudança de perspectiva.

Peter Mortensen
fonte
2
Que análise esquisita fazer desde que os padrões do GoF foram projetados especificamente para linguagens OOP baseadas em classe. Parece um pouco como analisar se as chaves para tubos são boas para o trabalho elétrico.
munificent
@ magnífico: Na verdade não. A orientação a objetos fornece polimorfismo; programação funcional geralmente fornece polimorfismo.
Marcin
@ Marcin, um programador de OO significa algo muito diferente pelo polimorfismo do que um programador funcional.
AndrewC
@AndrewC Eu discordo. O programador OO pode pensar que eles significam algo diferente, mas não o fazem.
28613 Marcin
3
@ Marcin Na minha experiência, um programador de OO normalmente se refere ao polimorfismo de subtipo (geralmente apenas usando Object), utilizando moldes para alcançá-lo, ou polimorfismo ad-hoc (sobrecarga etc). Quando um programador funcional diz polimorfismo, significa polimorfismo paramétrico (isto é, funciona para qualquer tipo de dado - Int, função, lista), que talvez seja mais parecido com a programação genérica do OO do que com qualquer coisa que os programadores normalmente chamam de polimorfismo.
precisa saber é
15

Eu diria que, quando você tem uma linguagem como Lisp com suporte para macros, pode criar suas próprias abstrações específicas de domínio, abstrações que geralmente são muito melhores que as soluções gerais de expressões idiomáticas.

Anders Rune Jensen
fonte
Estou completamente perdido. Para criar algo com abstrações ... O que isso significa?
Tuinstoel
2
Você pode criar abstrações específicas do domínio (mesmo as incorporadas) sem macros. As macros apenas permitem aprimorá-las adicionando sintaxe personalizada.
precisa
2
Você pode pensar no Lisp como um conjunto de Legos para a construção de linguagens de programação - é uma linguagem, mas também é uma metalinguagem. O que significa que, para qualquer domínio com problema, você pode criar um idioma personalizado que não tenha deficiências óbvias. Isso exigirá alguma prática, e Kurt Gödel pode discordar, mas vale a pena gastar algum tempo com Lisp para ver o que isso traz para a mesa (dica, macros).
Greg
9

E mesmo as soluções de padrão de design OO são específicas do idioma.

Padrões de design são soluções para problemas comuns que sua linguagem de programação não resolve para você. Em Java, o padrão Singleton resolve o problema único (simplificado).

No Scala, você tem uma construção de nível superior chamada Objeto, além da Classe. É instanciado preguiçosamente e existe apenas um. Você não precisa usar o padrão Singleton para obter um Singleton. Faz parte do idioma.

Peter Mortensen
fonte
8

Padrões são maneiras de resolver problemas semelhantes que são vistos repetidas vezes e, em seguida, são descritos e documentados. Então, não, o FP não substituirá padrões; no entanto, o FP pode criar novos padrões e tornar obsoletos alguns padrões atuais das "melhores práticas".

Edwin Buck
fonte
4
Os padrões de GoP são maneiras de resolver o problema das limitações de um tipo específico de linguagem de programação que atrapalha o seu caminho. Por exemplo: "Quero indireto nas classes e diga a elas para criar objetos" -> "Você não pode, mas pode criar objetos do tipo metaclasse chamados Factory". "Desejo envio múltiplo" -> "Você não pode, mas existe um labirinto que você pode implementar chamado Padrão de Visitante". Etc. Nenhum dos padrões faz sentido se você não estiver em um idioma OOP com limitações específicas.
Kaz
1
Não sei se "nenhum" deles faz sentido em outros idiomas, mas concordo que muitos deles não fazem sentido em outros idiomas. O Adapter e o Bridge parecem ter mais possibilidades multilíngues, diminuindo um pouco para o visitante e talvez um pouco menos para o ouvinte. No entanto, os padrões entre os idiomas sempre sofrerão com o tipo de "como fazer a operação do idioma X no idioma Y" escorando os limites naturais do idioma. Um exemplo perfeito foi o padrão Singleton, que é basicamente, como obtenho globos C no OOP? (que eu responderei, você não deveria).
Edwin Buck,
1
Segundo Kaz: Padrão não é "maneira de resolver problemas semelhantes que são vistos repetidas vezes", mas "modo de resolver problemas semelhantes que são vistos repetidas vezes E precisa ser reescrito repetidamente porque a linguagem não permite escreva apenas uma vez ". Em outras palavras, se o idioma permitiu que o padrão fosse extraído / abstratado na biblioteca / classe / módulo etc, ele deixa de ser um padrão, mas se torna uma biblioteca / classe / módulo. No FP, é muito mais fácil extrair / abstrair bit de códigos para uma função, portanto, "padrão" é mais facilmente convertido em código reutilizável, tornando-os não um padrão.
mb14 12/06
1
Sua interpretação é bem-vinda, mas o livro do GoF foi claro para definir um padrão e, se você ler os capítulos introdutórios, não diz nada sobre idiomas ou pontos fracos do idioma. Certamente, algumas linguagens têm áreas que as fazem alavancar alguns padrões com mais frequência, mas se você a escreve dez vezes (recortar e colar) ou a implementa uma vez com dez realizações (subclassificação), ou se possui uma estrutura configurada para executar um pouco dez maneiras diferentes, é apenas um detalhe de implementação do padrão que está sendo exposto.
Edwin Buck
1
Voltando a essa conversa depois de anos, acho que muitas pessoas associam o Patterns a uma linguagem de programação específica ou a um paradigma de programação específico. Eles podem ser usados ​​nesse contexto, mas existiam antes da programação. "Uma maneira atemporal de construir" discute padrões na construção de arquitetura e planejamento comunitário. Isto implica que as técnicas orientadas padrão pode ser usado fora do "limitações de uma linguagem" a menos que você quiser chamá construção civil uma linguagem de programação :)
Edwin Buck
8

Como outros já disseram, existem padrões específicos para programação funcional. Eu acho que a questão de se livrar dos padrões de design não é tanto uma questão de mudar para funcional, mas uma questão de recursos de linguagem .

Veja como Scala se livra do "padrão singleton": você simplesmente declara um objeto em vez de uma classe. Outro recurso, a correspondência de padrões, ajuda a evitar a obstrução do padrão do visitante. Veja a comparação aqui: Correspondência de padrões de Scala = Padrão de visitantes em esteróides

E Scala, como F #, é uma fusão de OO-funcional. Eu não sei sobre F #, mas provavelmente possui esse tipo de recurso.

Os fechamentos estão presentes na linguagem funcional, mas eles não precisam ser restritos a eles. Eles ajudam com o padrão de delegador.

Mais uma observação. Esse trecho de código implementa um padrão: é tão clássico e tão elementar que geralmente não pensamos nele como um "padrão", mas com certeza é:

for(int i = 0; i < myList.size(); i++) { doWhatever(myList.get(i)); }

Linguagens imperativas como Java e C # adotaram o que é essencialmente uma construção funcional para lidar com isso: "foreach".

Alemão
fonte
Eu diria que Scala inclui suporte de primeira classe para o padrão singleton. O padrão ainda está lá, mas o código padrão necessário para aplicar o padrão é bastante reduzido em comparação ao Java.
JacquesB
Se as opiniões eram como um idiota, bem ... Veja o resto das respostas. "você simplesmente declara um objeto em vez de uma classe" é tão verdadeiro que eu explicitamente chamaria de objeto literal (por exemplo, var singleton = {};). Também gosto da menção sobre o padrão foreach. Infelizmente, parece que a maioria das pessoas que respondeu / comentou sobre essa pergunta não entende de programação funcional e prefere justificar o uso de padrões de design de OOP. +1 por fornecer exemplos concretos, eu daria mais se pudesse.
Evan Plaice
@JacquesB Eu não posso comentar sobre Scala / Haskell, mas em JavaScript (isto é, híbrido funcional / imperativo) não há absolutamente nenhum clichê, você apenas ajusta a maneira como declara objetos usando combinações de sintaxe literal de objeto, funções anônimas, passando funções como primeiro membros da classe e permitindo herança múltipla (eliminando a necessidade de contratos de interface).
Evan Plaice
8

O GoF Design Patterns está codificando receitas alternativas para idiomas OO que são descendentes do Simula 67 , como Java e C ++.

A maioria dos "males" tratados pelos padrões de design são causados ​​por:

  • classes de tipo estaticamente, que especificam objetos, mas não são objetos;
  • restrição ao envio único (somente o argumento mais à esquerda é usado para selecionar um método, os argumentos restantes são considerados apenas tipos estáticos: se eles tiverem tipos dinâmicos, cabe ao método resolver isso com abordagens ad-hoc);
  • distinção entre chamadas de função regulares e chamadas de função orientadas a objetos, o que significa que funções orientadas a objetos não podem ser passadas como argumentos funcionais em que funções regulares são esperadas e vice-versa; e
  • distinção entre "tipos de base" e "tipos de classe".

Não existe um desses padrões de design que não desapareça no Common Lisp Object System, mesmo que a solução seja estruturada essencialmente da mesma maneira que no padrão de design correspondente. (Além disso, esse sistema de objetos precede o livro do GoF em mais de uma década. O Common Lisp se tornou um padrão ANSI no mesmo ano em que o livro foi publicado pela primeira vez.)

No que diz respeito à programação funcional, se os padrões se aplicam ou não, depende se a linguagem de programação funcional fornecida possui algum tipo de sistema de objetos e se é modelada após os sistemas de objetos que se beneficiam dos padrões. Esse tipo de orientação a objetos não combina bem com a programação funcional, porque a mutação de estado está na frente e no centro.

A construção e o acesso sem mutação são compatíveis com a programação funcional e, portanto, os padrões relacionados ao acesso ou construção de abstração podem ser aplicáveis: padrões como Factory, Facade, Proxy, Decorator e Visitor.

Por outro lado, os padrões comportamentais como Estado e Estratégia provavelmente não se aplicam diretamente na OOP funcional, porque a mutação de estado está em seu cerne. Isso não significa que eles não se aplicam; talvez eles de alguma forma se apliquem em combinação com os truques disponíveis para simular o estado mutável.

Kaz
fonte
2
"Os padrões de design do GoF estão codificando receitas alternativas" é simplesmente uma declaração falsa.
John Peters
7

Gostaria de acrescentar alguns artigos excelentes, mas um tanto densos, de Jeremy Gibbons: "Padrões de design como programas genéricos de tipo de dados de ordem superior" e "A essência do padrão Iterator" (ambos disponíveis aqui: http: // www. comlab.ox.ac.uk/jeremy.gibbons/publications/ ).

Ambos descrevem como construções funcionais idiomáticas cobrem o terreno coberto por padrões de design específicos em outras configurações (orientadas a objetos).

sclv
fonte
6

Você não pode ter essa discussão sem abrir sistemas de tipos.

Os principais recursos da programação funcional incluem funções como valores de primeira classe, curry, valores imutáveis ​​etc. Não me parece óbvio que os padrões de design do OO estejam se aproximando de qualquer um desses recursos.

Isso ocorre porque esses recursos não tratam dos mesmos problemas que o OOP ... são alternativas à programação imperativa. A resposta do FP ao POO reside nos sistemas de tipos de ML e Haskell ... especificamente tipos de soma, tipos de dados abstratos, módulos de ML e classes de tipo Haskell.

Mas é claro que ainda existem padrões de design que não são resolvidos pelas linguagens FP. Qual é o equivalente FP de um singleton? (Desconsiderando por um momento que singletons geralmente são um padrão terrível de se usar)

A primeira coisa que as typeclasses fazem é eliminar a necessidade de singletons.

Você poderia passar pela lista dos 23 e eliminar mais, mas não tenho tempo agora.

Jason Ganetsky
fonte
6
Como as classes de tipo (o equivalente em FP das interfaces OOP) eliminam a necessidade de singletons (o equivalente em FP do estado global)?
Gabe
4

Acho que apenas dois padrões de design do GoF foram projetados para introduzir a lógica de programação funcional na linguagem OO natural. Penso em estratégia e comando. Alguns dos outros padrões de design do GoF podem ser modificados pela programação funcional para simplificar o design e manter o objetivo.

CyaNnOrangeHead
fonte
4
O ponto principal de muitos padrões é aproveitar o polimorfismo para fazer coisas que o suporte decente aos conceitos de FP poderia permitir automaticamente. (A maioria das encarnações que eu vi do Builder, por exemplo, são apenas currying pela metade.) Quando você pode facilmente tratar as funções como valores, os padrões geralmente se simplificam ao ponto de trivialidade. Eles se tornam "passam um retorno de chamada" ou "têm um dicionário de retornos de chamada" - e diferentes classes de construtores, por exemplo, podem quase desaparecer. Na IMO, um padrão deixa de ser um padrão, uma vez que é trivial o suficiente para ser exatamente como as coisas funcionam , em vez de algo que você precisa implementar.
Chao
4

Essencialmente sim !

  • Quando um padrão contorna os recursos ausentes (funções de alta ordem, manipulação de fluxo ...) que ultimamente facilitam a composição .
  • A necessidade de reescrever a implementação de padrões repetidas vezes pode ser vista como um cheiro de linguagem .

Além disso, esta página (AreDesignPatternsMissingLanguageFeatures) fornece uma tabela de tradução "padrão / recurso" e algumas boas discussões, se você estiver disposto a cavar.

YvesgereY
fonte
3

A programação funcional não substitui os padrões de design. Os padrões de design não podem ser substituídos.

Os padrões simplesmente existem; eles surgiram ao longo do tempo. O livro do GoF formalizou alguns deles. Se novos padrões estão surgindo à medida que os desenvolvedores usam linguagens de programação funcionais, isso é empolgante, e talvez também haja livros escritos sobre eles.

ktingle
fonte
1
Padrões de design não podem ser substituídos? Isso é um pouco fechado, eu acho. Provavelmente todos podemos concordar que os padrões de design visam solucionar problemas de programação, e eu pelo menos gostaria de esperar que algum dia possamos resolver esses problemas sem padrões de design.
Metropolis
3
Qualquer padrão específico pode ser substituível, mas o conceito de padrões não pode. Lembre-se de que o termo "padrão" surgiu no campo da arquitetura .
Frank Shearar
1
Os padrões não têm como objetivo resolver problemas de programação. Padrões são maneiras que programamos. A documentação dos padrões visa ajudar a resolver problemas de programação.
Torbjørn
3
@ Torbjørn: Padrões são maneiras de programar quando o idioma atrapalha . Eles existem devido a alguma incompatibilidade entre o comportamento desejado do programa e as habilidades internas da linguagem, onde os requisitos e as habilidades não mapeiam bem ou mapeiam ambiguamente. Se não fosse por isso, não haveria padrão; você teria uma implementação que é exatamente como as coisas são feitas , e outras implementações não valeriam a pena ser consideradas.
Chao
1
Exceto que os padrões realmente existem apenas para facilitar a comunicação. Não há outro propósito. E em todas as reuniões de design em que participei ao longo dos anos, uma discussão sobre o algoritmo é o que era importante, não o padrão. O padrão raramente explicava o que realmente estava acontecendo, em qualquer sentido significativo. Explica precisamente os impactos O (n) vs O (n Log (n))? Não. Explica com que facilidade ele se encaixa na arquitetura existente? Não. As discussões em algoritmo em escala real fazem. Não estou argumentando que os padrões devam ser aposentados por si só, mas se fossem, quase nada sofreria como resultado.
3

No novo livro de 2013 chamado "Functional Programming Patterns- in Scala and Clojure", o autor Michael.B. Linn faz um trabalho decente comparando e fornecendo substituições em muitos casos para os padrões GoF e também discute os padrões funcionais mais recentes, como 'recursão da cauda', 'memorização', 'sequência lenta' etc.

Este livro está disponível na Amazon. Achei isso muito informativo e encorajador quando proveniente de um histórico de OO de algumas décadas.

manish
fonte
3

Os padrões de POO e GoF lidam com estados. OOP modela a realidade para manter a base de código o mais próxima possível dos requisitos da realidade. Os padrões de design do GoF são padrões que foram identificados para resolver problemas atômicos do mundo real. Eles lidam com o problema de estado de maneira semântica.

Como na programação funcional real, nenhum estado existe, não faz sentido aplicar os padrões GoF. Não há padrões de design funcionais da mesma maneira que existem padrões de design do GoF. Todo padrão de design funcional é artificial em contraste com a realidade, pois as funções são construções da matemática e não da realidade.

As funções não têm o conceito de tempo, pois sempre retornam o mesmo valor, independentemente do horário atual, a menos que o tempo faça parte dos parâmetros da função, o que dificulta o processamento de "solicitações futuras". Linguagens híbridas combinam esses conceitos, tornando as linguagens não linguagens de programação funcionais reais.

As linguagens funcionais estão aumentando apenas por uma coisa: as atuais restrições naturais da física. Os processadores de hoje são limitados em sua velocidade de processamento de instruções devido a leis físicas. Você vê uma estagnação na frequência do relógio, mas uma expansão nos núcleos de processamento. É por isso que o paralelismo das instruções se torna cada vez mais importante para aumentar a velocidade das aplicações modernas. Como a programação funcional, por definição, não tem estado e, portanto, não tem efeitos colaterais, é seguro processar funções com segurança em paralelo.

Os padrões GoF não são obsoletos. Eles são pelo menos necessários para modelar os requisitos do mundo real. Mas se você usar uma linguagem de programação funcional, precisará transformá-la em seus equivalentes híbridos. Finalmente, você não tem chance de criar apenas programas funcionais se usar persistência. Para os elementos híbridos do seu programa, permanece a necessidade de usar padrões GoF. Para qualquer outro elemento puramente funcional, não há necessidade de usar padrões GoF porque não há estado.

Como os padrões GoF não são necessários para a programação funcional real, isso não significa que os princípios do SOLID não devam ser aplicados. Os princípios do SOLID estão além de qualquer paradigma de linguagem.

oopexpert
fonte
2
O FP pode ter estado - apenas nenhum estado global, compartilhado ou mutável.
vt5491
2

Na programação funcional, os padrões de design têm um significado diferente. De fato, a maioria dos padrões de design de OOP é desnecessária na programação funcional devido ao alto nível de abstração e aos HOFs usados ​​como blocos de construção.

O princípio de um HOF significa que as funções podem ser passadas como argumentos para outras funções. e funções podem retornar valores.

Ali Bayat
fonte
1

Como a resposta aceita disse, OOP e FP todos têm seus padrões específicos.

No entanto, existem alguns padrões que são tão comuns que todas as plataformas de programação que eu consigo pensar deveriam ter. Aqui está uma lista (incompleta):

  • Adaptador. Mal consigo pensar em uma plataforma de programação útil que seja tão abrangente (e auto-realizada) que não precise falar com o mundo. Se isso acontecer, é definitivamente necessário um adaptador.

  • Fachada. Quaisquer plataformas de programação que possam lidar com grandes códigos-fonte devem ser capazes de modularizar. Se você criar um módulo para outras partes do programa, oculte as partes "sujas" do código e ofereça uma ótima interface.

  • Intérprete. Em geral, qualquer programa está apenas fazendo duas coisas: analisar entrada e imprimir saída. As entradas do mouse precisam ser analisadas e os widgets da janela precisam ser impressos. Portanto, ter um intérprete incorporado oferece ao programa poder adicional para personalizar as coisas.

Além disso, notei que em uma linguagem FP típica, Haskell, há algo semelhante aos padrões GoF, mas com nomes diferentes. Na minha opinião, isso sugere que eles estavam lá porque existem alguns problemas comuns a serem resolvidos nos idiomas FP e OOP.

  • Transformador e decorador de mônada. O primeiro usado para adicionar capacidade adicional a uma mônada existente; o último adiciona capacidade adicional a um objeto existente.
Earth Engine
fonte
1

Penso que cada paradigma serve a um propósito diferente e, como tal, não pode ser comparado dessa maneira.

Eu não ouvi dizer que os padrões de design do GoF são aplicáveis ​​a todos os idiomas. Ouvi dizer que eles são aplicáveis ​​a todos os idiomas OOP . Se você usa programação funcional, o domínio dos problemas que você resolve é diferente das linguagens OO.

Eu não usaria linguagem funcional para escrever uma interface de usuário, mas uma das linguagens OO, como C # ou Java, facilitaria esse trabalho. Se eu estivesse escrevendo uma linguagem funcional, não consideraria usar padrões de design OO.

Vincent Ramdhanie
fonte
1

OOP e FP têm objetivos diferentes. OOP visa encapsular as complexidades / partes móveis dos componentes de software e o FP visa minimizar a complexidade e as dependências dos componentes de software.

No entanto, esses dois paradigmas não são necessariamente 100% contraditórios e podem ser aplicados em conjunto para obter o benefício dos dois mundos.

Mesmo com uma linguagem que não suporta nativamente programação funcional como C #, você pode escrever código funcional se entender os princípios do FP. Da mesma forma, você pode aplicar os princípios de POO usando o F # se entender os princípios, padrões e práticas recomendadas de POO. Você faria a escolha certa com base na situação e no problema que tenta resolver, independentemente da linguagem de programação usada.

Dogu Arslan
fonte
1

Alguns padrões são mais fáceis de implementar em um idioma que suporta FP. Por exemplo, a estratégia pode ser implementada usando-se muito bem os fechamentos. No entanto, dependendo do contexto, você pode preferir implementar a Estratégia usando uma abordagem baseada em classe, digamos que as estratégias sejam bastante complicadas e / ou compartilhem a estrutura que você deseja modelar usando o Método do Modelo.

Na minha experiência em desenvolvimento em uma linguagem multiparadigma (Ruby), a implementação do FP funciona bem em casos simples, mas onde o contexto é mais complicado, a abordagem baseada no GoF OOP é mais adequada.

A abordagem FP não substitui a abordagem OOP, ela a complementa.

ComDubh
fonte
0

A principal característica da programação funcional, IMHO, é que você está programando apenas com expressões - expressões dentro de expressões dentro de expressões que são avaliadas até a última expressão final que "aquece a máquina quando avaliada".

A principal característica da programação orientada a objetos, IMHO, é que você está programando com objetos que possuem estado interno. Você não pode ter um estado interno em funções puras - as linguagens de programação orientadas a objetos precisam de instruções para fazer as coisas acontecerem. (Não há instruções na programação funcional.)

Você está comparando maçãs com laranjas. Os padrões de programação orientada a objetos não se aplicam à programação de funções, porque a programação funcional está programando com expressões e a programação orientada a objetos está programando com estado interno.

Jim Flood
fonte
Hmm, eu deveria ter notado que a pergunta tinha onze anos antes de responder. :-)
Jim Flood
0

Preparem-se.

Muitos irão agravar-me ouvir-me afirmar ter substituído os padrões de design e desmascarado o SOLID e o DRY. Eu não sou ninguém. No entanto, modelei corretamente a arquitetura colaborativa (de fabricação) e publiquei as regras para a criação de processos on-line, juntamente com o código e a ciência por trás dela no meu site http://www.powersemantics.com/ .

Meu argumento é que os padrões de design tentam alcançar o que a manufatura chama de "customização em massa", uma forma de processo na qual cada etapa pode ser remodelada, recomposta e estendida. Você pode pensar nesses processos como scripts não compilados. Não vou repetir meu argumento (online) aqui. Em resumo, minha arquitetura de customização em massa substitui os padrões de design ao obter essa flexibilidade sem nenhuma das semânticas confusas. Fiquei surpreso que meu modelo funcionou tão bem, mas a maneira como os programadores escrevem código simplesmente não acende uma vela sobre como a manufatura organiza o trabalho colaborativo.

  • Fabricação = cada etapa interage com um produto
  • OOP = cada etapa interage consigo mesma e com outros módulos, passando o produto de um ponto a outro como trabalhadores de escritório inúteis

Essa arquitetura nunca precisa de refatoração. Também existem regras relativas à centralização e distribuição que afetam a complexidade. Mas, para responder à sua pergunta, a programação funcional é outro conjunto de semânticas de processamento, não uma arquitetura para processos personalizados em massa onde 1) o roteamento de origem existe como um documento (script) que o usuário pode reescrever antes de disparar e 2) os módulos podem ser facilmente e adicionado ou removido dinamicamente.

Poderíamos dizer que OOP é o paradigma do "processo codificado" e que os padrões de design são maneiras de evitar esse paradigma. Mas é disso que se trata a personalização em massa. Os padrões de design incorporam processos dinâmicos como hardcode confuso. Simplesmente não faz sentido. O fato de o F # permitir a passagem de funções como parâmetro significa que linguagens funcionais e OOP tentam realizar a personalização em massa.

Quão confuso isso é para o leitor, código fixo que representa script? De maneira alguma, se você acha que os consumidores do seu compilador pagam por esses recursos, mas para mim esses recursos são desperdícios semânticos. Eles são inúteis, porque o objetivo da personalização em massa é tornar os processos dinâmicos , não apenas dinâmicos para o programador que usa o Visual Studio.

RBJ
fonte
0

Certamente, um PL funcional de alto nível (como OCaml, com classes, módulos etc.) certamente substitui as linguagens imperativas de OOP na versatilidade de tipos e no poder de expressão. As abstrações não vazam, você pode expressar a maioria das suas idéias diretamente no programa. Portanto, sim, ele substitui os padrões de design, a maioria dos quais é ridiculamente simplista em comparação aos padrões funcionais.

exa
fonte