関数型プログラミングはGoF設計パターンを置き換えるものですか?


1047

昨年F#OCamlの学習を始めて以来、デザインパターン(特にJavaの場合)が命令型言語で欠落している機能の回避策であると主張する記事を数多く読みました。私が見つけた1つの記事はかなり強い主張をしています:

私が会ったほとんどの人は、Gang of Four(GoF)によるデザインパターンの本を読んだことがあります 。自尊心のあるプログラマーなら誰でも、その本は言語にとらわれず、パターンは、使用する言語に関係なく、ソフトウェアエンジニアリング全般に当てはまることを教えてくれます。これは高貴な主張です。残念ながら、それは真実からかけ離れています。

関数型言語は非常に表現力があります。関数型言語では、言語が非常に高レベルである可能性が高いため、設計パターンは必要ありません。結局、設計パターンをすべて排除する概念でプログラミングすることになります。

関数型プログラミング(FP)の主な機能には、ファーストクラスの値、カリー化、不変値などの関数が含まれます。OOデザインパターンがこれらの機能のいずれかに近似していることは明らかではありません。

さらに、OOPをサポートする関数型言語(F#やOCamlなど)では、これらの言語を使用するプログラマーが、他のすべてのOOP言語で利用可能な同じデザインパターンを使用することは明らかです。実際、現在、私は毎日F#とOCamlを使用しており、これらの言語で使用するパターンとJavaで書くときに使用するパターンとの間に顕著な違いはありません。

関数型プログラミングによってOOP設計パターンが不要になるという主張に真実はありますか?もしそうなら、典型的なOOP設計パターンとそれに相当する機能の例を投稿またはリンクできますか?


18
Steve Yegge(steve-yegge.blogspot.com/2006/03/…)の記事をご覧になるかもしれません
Ralph

27
「この本は言語にとらわれず、パターンはソフトウェアエンジニアリング全般に適用されます」-一部の言語ではデザインパターンなどの特定のことを表現する必要がないという意味で、この本はこの主張に同意しないことに注意してください:「私たちのパターンSmalltalk / C ++レベルの言語機能を想定し、その選択によって、簡単に実装できるものと実装できないものを決定します[...] CLOSには、たとえば、訪問者(ページ331)などのパターンの必要性を減らすマルチメソッドがあります。(ページ4)
Guildenstern 2014年

6
また、十分に高水準の命令型言語では、多くのデザインパターンは必要ないことにも注意してください。
R.バーゼル2015

3
@ R.Barzell「十分に高いレベルの命令型言語」とは何ですか?ありがとう。
cibercitizen1 2015年

5
@ cibercitizen1高次関数と無名関数をサポートするダック型言語。これらの機能は、多くの設計パターンが提供することを意図されていた能力の多くを提供します。
R.バーゼル

回答:


1077

あなたが引用したブログ投稿は、その主張を少し誇張しています。FPはデザインパターンの必要性を排除しません。「デザインパターン」という用語は、FP言語で同じことを説明するために広く使用されているわけではありません。しかし、それらは存在します。関数型言語には、「問題Xに遭遇したときに、Yのようなコードを使用する」という形式のベストプラクティスルールがたくさんあります。これは、基本的にデザインパターンです。

ただし、ほとんどのOOP固有のデザインパターンは、関数型言語ではほとんど無関係です。

一般に、デザインパターンは言語の欠点を修正するためだけに存在していると言っても、特に論争の的にはならないと思います。そして、別の言語が同じ問題を簡単に解決できる場合、その他の言語はそのための設計パターンを必要としません。その言語のユーザーは、問題が存在することにさえ気付かないかもしれません。なぜなら、それはその言語では問題ではないからです。

ギャングオブフォーがこの問題について述べていることは次のとおりです。

プログラミング言語の選択は、自分の視点に影響を与えるため、重要です。私たちのパターンは、Smalltalk / C ++レベルの言語機能を想定しており、その選択により、簡単に実装できるものと実装できないものが決まります。手続き型言語を想定した場合、「継承」、「カプセル化」、「ポリモーフィズム」と呼ばれる設計パターンが含まれている可能性があります。同様に、パターンの一部は、あまり一般的ではないオブジェクト指向言語によって直接サポートされています。CLOSには、たとえば、Visitorなどのパターンの必要性を減らすマルチメソッドがあります。実際、SmalltalkとC ++の間には十分な違いがあり、一部のパターンは1つの言語で他の言語よりも簡単に表現できます。(たとえば、イテレーターを参照してください。)

(上記は、Introduction to the Design Patternsブックの4ページ、パラグラフ3からの引用です)

関数型プログラミングの主な機能には、ファーストクラスの値、カリー化、不変値などの関数が含まれます。OOデザインパターンがこれらの機能のいずれかに近似していることは明らかではありません。

ファーストクラスの関数の近似でない場合、コマンドパターンは何ですか?:) FP言語では、関数を引数として別の関数に渡すだけです。OOP言語では、関数をクラスにラップする必要があります。これをインスタンス化して、そのオブジェクトを他の関数に渡すことができます。効果は同じですが、OOPではデザインパターンと呼ばれ、かなり多くのコードが必要です。そして、カレー化されていない場合、抽象的なファクトリパターンは何ですか?関数にパラメーターを少しずつ渡して、最後に呼び出したときにどのような値が関数から出力されるかを構成します。

したがって、はい。FPF言語ではいくつかのGoFデザインパターンが冗長になっています。これは、より強力で使いやすい代替手段が存在するためです。

しかしもちろん、FP言語で解決されないデザインパターンがまだあります。シングルトンに相当するFPとは何ですか?(シングルトンは一般にひどいパターンであることを少しの間無視します。)

そして、それは両方の方法でも機能します。先ほど言ったように、FPにもデザインパターンがあります。人々は通常それらをそのように考えていないだけです。

しかし、あなたはモナドに出くわしたかもしれません。「グローバルな状態に対処する」ための設計パターンではないにしても、それらは何ですか?これはOOP言語では非常に単純な問題であり、同等のデザインパターンは存在しません。

「静的変数をインクリメントする」、または「そのソケットから読み取る」ためのデザインパターンは必要ありませ。これ、あなたすることだけなのでです。

モナドが設計パターンであると言うことは、通常の演算で整数を表し、ゼロ要素が設計パターンであると言うのと同じくらいばかげています。いいえ、モナドは数学的なパターンであり、デザインパターンではありません。

(純粋な)関数型言語では、モナドの「デザインパターン」や、同じことを可能にする他の方法で回避しない限り、副作用や変更可能な状態は不可能です。

さらに、OOPをサポートする関数型言語(F#やOCamlなど)では、これらの言語を使用するプログラマーが、他のすべてのOOP言語で利用可能な同じデザインパターンを使用することは明らかです。実際、現在私はF#とOCamlを毎日使用しており、これらの言語で使用するパターンとJavaで書くときに使用するパターンとの間に顕著な違いはありません。

おそらくあなたはまだ命令的に考えているからですか?多くの人は、生涯にわたって命令型言語を扱った後、関数型言語を試すときにその習慣をあきらめるのに苦労しています。(私はF#でかなり面白い試みをいくつか見ました。文字通りすべての関数は、基本的にはCプログラムを取得し、すべてのセミコロンを 'let'で置き換えたかのように、単に 'let'ステートメントの文字列でした。:))

しかし、別の可能性としては、OOP言語での設計パターンを必要とする問題を簡単に解決していることに気付いていない可能性があります。

カレーを使用するとき、または関数を引数として別の関数に渡すときは、停止して、OOP言語でそれを行う方法を考えてください。

関数型プログラミングによってOOP設計パターンが不要になるという主張に真実はありますか?

うん。:) FP言語で作業する場合、OOP固有のデザインパターンは必要ありません。ただし、MVCやその他の非OOP固有のものなど、いくつかの一般的なデザインパターンが必要です。代わりに、FP固有の新しい「デザインパターン」がいくつか必要です。すべての言語には欠点があり、通常、デザインパターンはそれらを回避する方法です。

とにかく、ML(少なくとも学習目的では私の個人的なお気に入り)やHaskellなどの「よりクリーンな」FP言語で手を試すのは興味深いかもしれません。新しい何かに直面しています。


予想通り、何人かの人が私のデザインパターンの定義に「言語の欠点を修正する」と異議を唱えたので、これが私の正当化です。

すでに述べたように、ほとんどの設計パターンは1つのプログラミングパラダイムに固有であり、場合によっては1つの特定の言語にも固有です。多くの場合、それらはそのパラダイムにのみ存在する問題を解決します(FPのモナド、またはOOPの抽象ファクトリーを参照)。

抽象ファクトリパターンがFPに存在しないのはなぜですか?解決しようとする問題はそこには存在しないからです。

したがって、FP言語にはない問題がOOP言語に存在する場合、それは明らかにOOP言語の欠点です。問題は解決できますが、あなたの言語では解決できませんが、問題を回避するには、あなたからの一連の定型コードが必要です。理想的には、私たちのプログラミング言語がすべての問題を魔法のように解消することを望んでいます。依然として存在する問題は、原則として言語の欠点です。;)


73
デザインパターンは、基本的な問題の一般的な解決策を示します。しかし、それはプログラミング言語とプラットフォームが行うことでもあります。したがって、使用している言語とプラットフォームが十分でない場合は、デザインパターンを使用します。
yfeldblum 2008年

135
S.Lott:ある特定の言語に存在する問題の解決策について説明しています。FP言語には、解決しようとする問題が存在しないため、コマンドデザインパターンはありません。つまり、言語自体では解決できない問題を解決します。つまり、言語の欠点
jalf

38
モナドは数学的概念であり、あなたはそれをあなたの分類で伸ばしています。もちろん、関数、モノイド、モナド、行列、その他の数学的概念を設計パターンとして表示できますが、これらはアルゴリズムやデータ構造に似ています...基本的な概念、言語に依存しません。
Alexandru Nedelcu

41
もちろん、モナドは数学の概念ですが、パターンであります。モナドの「FPパターン」は、モナドの数学の概念とは多少異なります。前者は、純粋なFP言語で特定の「制限」を回避するために使用されるパターンです。後者は普遍的な数学的概念です。
2008

69
Haskellのモナドは、例外、継続、リスト内包表記、解析、非同期プログラミングなど、変更可能な状態以外のものに使用されることに注意してください。しかし、モナドのこれらすべてのアプリケーションは、おそらくパターンと呼ばれる可能性があります。
JacquesB 2009年

152

関数型プログラミングによってOOP設計パターンが不要になるという主張に真実はありますか?

関数型プログラミングは、オブジェクト指向プログラミングと同じではありません。オブジェクト指向の設計パターンは、関数型プログラミングには適用されません。代わりに、関数型プログラミングの設計パターンがあります。

関数型プログラミングの場合、オブジェクト指向の設計パターンの本を読むことはありません。FP設計パターンに関する他の本も読んでください。

言語にとらわれない

完全にではありません。オブジェクト指向言語に関して言語にとらわれないもののみ。デザインパターンは、手続き型言語にはまったく適用されません。リレーショナルデータベースの設計コンテキストではほとんど意味がありません。スプレッドシートのデザインには適用されません。

典型的なOOP設計パターンとそれに相当する機能?

上記は存在すべきではありません。これは、OOコードとして書き換えられた手続き型コードを要求するようなものです。うーん...元のFortran(またはC)をJavaに翻訳する場合、それを翻訳する以外に何もしていません。それを完全にOOパラダイムに書き換えると、元のFortranやCのようには見えなくなり、認識できなくなります。

オブジェクト指向の設計から機能的な設計への単純なマッピングはありません。彼らは問題を見る非常に異なる方法です。

関数型プログラミング(すべてのプログラミングスタイルと同様)には、設計パターンがあります。リレーショナルデータベースには設計パターン、OOには設計パターン、手続き型プログラミングには設計パターンがあります。建物の建築も含め、すべてにデザインパターンがあります。

設計パターンは、概念としては、テクノロジーや問題のドメインに関係なく、時代を超えた構築方法です。ただし、特定の設計パターンは、特定の問題領域およびテクノロジーに適用されます。

彼らがやっていることを考えている誰もがデザインパターンを明らかにするでしょう。


12
MVCはOOデザインではありません。それは建築設計です-そのパターンはかなり広く適用されます。
S.Lott、2008年

1
@プリンセス:関数型プログラミングは必ずしも単純ではありません。あなたの例では、はい。他のものについては、陪審はまだ出ていません。しかし、Java OOデザインパターンを破棄し、FPデザインパターンを採用しました。
S.Lott、2008年

1
+1:私は上記のJalfの答えよりもこの答えを好みます。一部の設計パターンは言語の欠陥に対処しますが、すべてがそうであるわけではありません。たとえば、「再帰的な結び目を緩める」デザインパターンが言語の欠陥に対処することはほとんどありませんが、依存関係を緩和するのに役立つイディオムにすぎません。
Jon Harrop、2011

9
Java 8には、匿名関数またはラムダ式として知られるクロージャーが含まれます。これにより、コマンドデザインパターンがJavaで廃止されます。これは言語の欠点の例ですよね?彼らは足りない機能を追加し、今ではデザインパターンは必要ありません。
トッドチャフィー

2
終了文の+1。設計パターンは、プログラミングを簡素化し、結果のプログラムを意図したとおりに効率化することを目的としています。
ソーター2013

46

言語とパターンの密接な関連についてのブライアンのコメントは要点です、

この議論の欠けている部分はイディオムの概念です。James O. Coplienの著書「Advanced C ++」は、ここで大きな影響を与えました。クリストファーアレクサンダーと名前のないコラムを発見するずっと前に(そして、アレクサンダーを読まないとパターンについて賢明に話すことができません)、彼は真に言語を学ぶ上でイディオムを習得することの重要性について話しました。彼は例としてCで文字列のコピーを使用しました。while(*from++ = *to++);これは、欠落している言語機能(またはライブラリ機能)のバンドエイドと見なすことができますが、本当に重要なのは、それが他のどれよりも思考または表現の単位が大きいことです。その部分。

それが私たちの意図をより簡潔に表現できるようにするために、パターンや言語がやろうとしていることです。思考の単位が豊富であるほど、表現できる思考が複雑になります。システムアーキテクチャから少しいじくり回すまで、さまざまなスケールで豊富な共有語彙を使用することで、よりインテリジェントな会話を行い、何をすべきかについて考えることができます。

個人として学ぶこともできます。これが演習全体の要点です。自分では考えられないことを一人ひとりが理解して使うことができます。言語、フレームワーク、ライブラリ、パターン、イディオムなどはすべて、知的財産を共有する立場にあります。


8
ありがとうございました!これがパターンの概要です。認知的負担を軽減するための「概念的なチャンキング」。
Randall Schulz、

そしてFunctional Monadsは間違いなくこの議論に属しています。
グレッグ、

@RandallSchulz:言語機能(そしてもちろんその慣用的な使用法)も、「認知的負担を軽減するための概念的なチャンク化」のカテゴリーにうまく適合します。
ロイティンカー、2013

39

GoFの本はOOPと明確に結び付いています。タイトルは「デザインパターン-再利用可能なオブジェクト指向ソフトウェアの要素」です(強調は私のものです)。



26

このトピックについて説明する別のリンクを次に示します。http//blog.ezyang.com/2010/05/design-patterns-in-haskel/

エドワードは彼のブログ投稿で、23の元のGoFパターンすべてをHaskellの観点から説明しています。


4
この記事はHaskellのデザインパターンを実際に示しているようには見えませんが、Haskellがそのパターンなしでそれらのニーズにどのように対処しているかを示しています。
Fresheyeball 2014

3
@Fresheyball:パターンの定義に依存します。リストへの関数のマッピングは、Visitorパターンのバリアントですか?私は一般的に答えは「はい」だと思っていました。パターンは特定の構文を超えるものと想定されています。適用される関数は、オブジェクトとしてラップすることも、関数ポインターとして渡すこともできますが、概念は同じです。反対ですか?
srm

20

これを「一般的なデザインパターン」と「FP対OOP」のレベルで見てみると、わかる答えはせいぜいあいまいなものです。

ただし、両方の軸でより深いレベルに移動し、特定のデザインパターン特定の言語機能を検討してください。

したがって、たとえば、一部の特定のパターン(VisitorStrategyCommandObserverなど)は、代数的データタイプとパターンマッチングクロージャファーストクラス関数などを備えた言語を使用すると、確実に変更または非表示になります。しかし、「周りをくぐる」。

一般的に、新しい(または人気が高まっている)言語機能によって、特定のパターンは時間の経過とともに排除されていると私は言います。これは言語設計の自然な流れです。言語がより高水準になるにつれて、以前は例を使用して本でしか呼び出せなかった抽象化が、特定の言語機能またはライブラリのアプリケーションになりました。

(余談:ここに私が書いた最近のブログがあります。FPとデザインパターンに関するより多くの議論への他のリンクがあります。)


ビジターパターンが「消える」とはどういう意味ですか?「たくさんのVisitメソッドを使ってVisitorインターフェースを作成する」から「unionタイプとパターンマッチングを使用する」に変わるのではないでしょうか。
Gabe

22
はい、しかし、それはあなたが本で読んであなたのコードに適用するデザインアイデアであるパターンから「ただのコード」に変わった。つまり、「ユニオンタイプとパターンマッチングを使用する」とは、そのような言語で通常コーディングする方法と同じです。(分析:forループを持つ言語がなく、すべてwhileループがあるだけの場合、「For」は反復パターンである可能性があります。しかし、forがその言語でサポートされている構文であり、人々が通常コーディングする方法である場合、それはパターンではありません-あなたはそうしませんパターンが必要、それはただのコードですよね。)
Brian

4
別の言い方をすれば、「パターンであるか」の多分悪くないリトマステストは次のとおりです。この方法で書かれたコードは、CSを専攻し、言語で1年のプログラミングの経験を積んだ2年生の学部生です。あなたが彼らにコードを見せて、彼らが「それは賢いデザインだ」と言ったら、それはパターンです。あなたが彼らにコードを見せて、彼らが「まあ、まあ!」と行くなら、それはパターンではありません。(そして、この「ビジター」を1年間ML / F#/ハスケルを行った人に見せれば、彼らは「まあ、まあ!」に行くでしょう)
ブライアン

1
ブライアン:「パターン」の定義が違うだけだと思う​​。私はあることを任意の識別可能な設計の抽象化を検討するパターンあなたがするだけで非自明な抽象化を検討しながら、パターン。C#がforeachあり、Haskellが持っているからとmapMいって、Iteratorパターンがないというわけではありません。IteratorパターンはIEnumerable<T>C#ではジェネリックインターフェイスとして、TraversableHaskell では型クラスとして実装されていると言っても問題ありません。
Gabe

明白ではないパターンはソフトウェアエンジニアに役立つかもしれませんが、すべてのパターンは言語設計者に役立ちます。つまり、「新しい言語を作成する場合は、イテレータパターンを表現する簡潔な方法を含めるようにしてください。」「このアイデアを表現するためのより良い構文はありますか?」と質問を始めるとき、明白なパターンでさえ興味があります。結局のところ、それが誰かがforeachを作成する原因となっています。
srm 2015

16

Norvigのプレゼンテーションは、すべてのGoFパターンを分析したことをほのめかしており、23のパターンのうち16は関数型言語での実装が単純であるか、単に言語の一部であると述べています。したがって、おそらくそれらの少なくとも7つは、a)同等に複雑であるか、b)言語に存在していません。残念ながら、それらは列挙されていません!

GoFの「創造的」または「構造的」パターンのほとんどは、JavaまたはC ++のプリミティブ型システムに必要なことをさせるためのトリックにすぎないことは明らかだと思います。しかし、残りの部分は、どの言語でプログラミングしても考慮に値します。

1つはプロトタイプかもしれません。これはJavaScriptの基本的な概念ですが、他の言語ではゼロから実装する必要があります。

私のお気に入りのパターンの1つはNull Objectパターンです。適切な種類の何もしないオブジェクトとして何かが存在しないことを表します。これは、関数型言語でモデル化する方が簡単な場合があります。しかし、実際の成果は視点の変化です。


2
GoFパターンがクラスベースのOOP言語用に特別に設計されて以来、何を奇妙な分析をするのか。パイプレンチが電気工事に適しているかどうかを分析するようなものです。
munificent

@munificent:そうではありません。オブジェクト指向はポリモーフィズムを提供します。関数型プログラミングは通常、ポリモーフィズムを提供します。
Marcin、2012

@Marcin OOプログラマーは、関数型プログラマーとはポリモーフィズムが大きく異なることを意味します。
AndrewC 2013

@AndrewC同意しない。OOプログラマーは、それらが別のことを意味すると考えているかもしれませんが、そうではありません。
Marcin 2013

3
@Marcin私の経験では、オブジェクト指向プログラマーは通常、サブタイプの多型(多くの場合、Objectを使用する)、キャストを使用してそれを実現する、またはアドホックな多型(オーバーロードなど)を指します。関数型プログラマーがポリモーフィズムを言うとき、それらはパラメトリックポリモーフィズム(つまり、あらゆるタイプのデータ(Int、関数、リスト)で機能します)を意味します。
AndrewC 2013

15

Lispのような言語でマクロをサポートしている場合、ドメイン固有の抽象化を構築できます。抽象化は、一般的なイディオムソリューションよりもはるかに優れていることがよくあります。


私は完全に迷っています。抽象化で何かを実現する...それはどういう意味ですか?
tuinstoel

2
マクロなしでドメイン固有の抽象化(組み込みのものも含む)を構築できます。マクロは、カスタム構文を追加することでそれらをきれいにさせます。
Jon Harrop、2011

2
Lispはプログラミング言語を構築するためのLegosのセットと考えることができます。これは言語ですが、メタ言語でもあります。つまり、どの問題領域でも、明らかな欠陥のない言語をカスタム設計できるということです。これには多少の練習が必要であり、KurtGödelも反対するかもしれませんが、Lispを使ってテーブルに何がもたらされるか(ヒント、マクロ)を確認するのに少し時間をかける価値があります。
グレッグ、

9

さらに、OO設計パターンソリューションでさえ、言語固有です。

デザインパターンは、プログラミング言語では解決できない一般的な問題の解決策です。Javaでは、シングルトンパターンは何か(単純化)の問題を解決します。

Scalaには、クラスに加えてオブジェクトと呼ばれる最上位の構造があります。シングルトンを取得するためにシングルトンパターンを使用する必要はありません。言語の一部です。


8

パターンは、何度も何度も見られ、次に説明され、文書化される同様の問題を解決する方法です。したがって、FPはパターンを置き換えるつもりはありません。ただし、FPは新しいパターンを作成し、いくつかの現在の「ベストプラクティス」パターンを「廃止」する可能性があります。


4
GoPパターンは、特定のタイプのプログラミング言語の制限の問題を解決する方法です。たとえば、「クラスに間接的にアクセスして、オブジェクトを作成するように伝えたい」->「できませんが、ファクトリと呼ばれるメタクラスのようなオブジェクトを作成できます」。「複数のディスパッチが必要です」->「できませんが、ビジターパターンと呼ばれる実装可能な迷路があります」。など特定の制限のあるOOP言語を使用していない場合、どのパターンも意味がありません。
Kaz

1
他の言語で意味のある「なし」については知りませんが、それらの多くが他の言語では意味をなさないことに同意します。AdapterとBridgeは多言語の可能性を持っているようで、訪問者にとっては少し減少し、リスナーにとっては少し少ないかもしれません。ただし、言語間のパターンは常に、言語Xの操作を言語Yで実行する方法のタイプによって、言語の自然な境界が不足することになります。完璧な例はシングルトンパターンでした。これは基本的に、OOPでCグローバルを取得するにはどうすればよいですか?(私が答えます、あなたはすべきではありません)。
エドウィンバック

1
私は2番目のKaz:パターンは「何度も見られる同様の問題を解決する方法」ではなく、「何度も何度も見られる同様の問題を解決する方法であり、言語が許可していないため、何度も書き直す必要があります。一度だけ書いてください。」言い換えれば、言語がライブラリー/クラス/モジュールなどでパターンの抽出/抽出を許可した場合、それはパターンでなくなり、ライブラリー/クラス/モジュールになります。FPでは、コードのビットを関数に抽出/抽出する方がはるかに簡単なので、「パターン」は再利用可能なコードに変換しやすくなり、パターンではなくなります。
mb14 2014年

1
あなたの解釈は大歓迎ですが、GoFの本はパターンを定義することは明らかでした、そしてあなたが紹介の章を読んだとしても、それは言語や言語の弱点について何も述べていません。確かに一部の言語には、パターンをより頻繁に活用する領域がありますが、それを10回記述(カットアンドペースト)するか、10回の実現(サブクラス化)で1回実装するか、わずかに10回実行するように構成されたフレームワークがあるか異なる方法は、公開されているパターンの実装の詳細のみです。
エドウィンバック

1
何年か後にこの会話を振り返ると、多くの人がパターンを特定のプログラミング言語または特定のプログラミングパラダイムに関連付けていると思います。それらはそのような状況で使用できますが、プログラミングの前に存在していました。「時代を超越した建築方法」では、建築とコミュニティ計画の構築におけるパターンについて説明しています。これは、構築言語をプログラミング言語と呼びたくない場合を除き、パターン指向の手法を「言語の制限」の外で使用できることを意味します:)
Edwin Buck

8

他の人が言ったように、関数型プログラミングに固有のパターンがあります。デザインパターンを取り除く問題は、関数型に切り替えることではなく、言語機能の問題だと思います

Scalaが「シングルトンパターン」を排除する方法を見てみましょう。クラスの代わりにオブジェクトを宣言するだけです。別の機能であるパターンマッチングは、ビジターパターンの不格好さを回避するのに役立ちます。こちらの比較をご覧ください: Scalaのパターンマッチング=ステロイドのビジターパターン

Scalaは、F#と同様に、OO機能の融合です。F#については知りませんが、おそらくこのような機能があります。

クロージャは関数型言語で存在しますが、それらに限定する必要はありません。これらは委任者パターンを支援します。

もう一つの観察。このコードはパターンを実装します。これは非常に古典的であり、非常に要素的であるため、通常は「パターン」とは考えませんが、それは確かです。

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

JavaやC#などの命令型言語は、これを処理するために本質的に機能的な構造である「foreach」を採用しています。


Scalaにはシングルトンパターンのファーストクラスのサポートが含まれていると思います。パターンはまだ残っていますが、パターンを適用するために必要なボイラープレートコードは、Javaに比べて大幅に削減されています。
JacquesB 2009年

意見がa *******のようなものだった場合は、まあ...残りの回答を見てください。「クラスではなくオブジェクトを宣言するだけ」は真実ですが、私は明示的にオブジェクトリテラルと呼びます(つまり、var singleton = {};)。また、foreachパターンについての言及も気に入っています。残念ながら、この質問に回答/コメントしたほとんどの人は関数型プログラミングを理解しておらず、OOP設計パターンの使用を正当化するようです。具体的な例を提供するための+1、できればもっとあげます。
エヴァンプレイス

@JacquesB Scala / Haskellについてコメントすることはできませんが、JavaScript(つまり、ハイブリッド関数型/命令型)では、オブジェクトリテラル構文、匿名関数、最初に関数を渡すことの組み合わせを使用してオブジェクトを宣言する方法を調整する定型文はありませんクラスメンバー、および複数の継承を許可(インターフェイスコントラクトの必要性を排除)。
エヴァンプレイス

8

GoF デザインパターンは、JavaやC ++などのSimula 67の子孫であるOO言語の回避策レシピをコーディングしています。

設計パターンによって扱われる「病気」のほとんどは、次の原因が原因です。

  • 静的に型付けされたクラス。オブジェクトを指定しますが、それ自体はオブジェクトではありません。
  • 単一のディスパッチへの制限(左端の引数のみがメソッドの選択に使用され、残りの引数は静的型としてのみ考慮されます:動的型がある場合、アドホックアプローチでそれを分類するのはメソッド次第です)。
  • 通常の関数呼び出しとオブジェクト指向の関数呼び出しの違い。つまり、オブジェクト指向の関数は、通常の関数が期待される場所では関数の引数として渡すことができず、その逆も同様です。そして
  • 「基本型」と「クラス型」の違い。

ソリューションは対応する設計パターンと基本的に同じ方法で構造化されていますが、Common Lispオブジェクトシステムで消えないこれらの設計パターンは1つだけではありません。(さらに、そのオブジェクトシステムはGoFの本に10年以上先行しています。CommonLispは、その本が最初に出版されたのと同じ年にANSI標準になりました。)

関数型プログラミングに関する限り、パターンが適用されるかどうかは、指定された関数型プログラミング言語に何らかのオブジェクトシステムがあるかどうか、およびパターンの恩恵を受けるオブジェクトシステムをモデルにしているかどうかによって異なります。このタイプのオブジェクト指向は、関数型プログラミングとうまく混ざりません。なぜなら、状態の変化が前面と中央にあるからです。

構築および非変更アクセスは関数型プログラミングと互換性があるため、抽象化アクセスまたは構築に関係するパターンを適用できます:ファクトリ、ファサード、プロキシ、デコレータ、ビジターなどのパターン。

一方、状態や戦略などの行動パターンは、機能のOOPには直接適用されない可能性があります。これは、それらが適用されないという意味ではありません。おそらくそれらは、どういうわけか、可変状態をシミュレートするために利用可能なあらゆるトリックと組み合わせて適用されます。


2
「GoFデザインパターンは回避策のレシピをコーディングしている」というのは単なる虚偽の記述です。
John Peters

7

ジェレミー・ギボンズの優れた、しかしやや密度の高い論文をいくつか差し上げたいと思います。「高次データ型汎用プログラムとしての設計パターン」と「イテレーターパターンの本質」(どちらもこちらから入手できます:http:// www。 comlab.ox.ac.uk/jeremy.gibbons/publications/)。

これらはどちらも、他の(オブジェクト指向の)設定で特定の設計パターンによってカバーされる地形を、慣用的な機能構成がどのようにカバーするかを説明します。


6

タイプシステムを起動しないと、この議論はできません。

関数型プログラミングの主な機能には、ファーストクラスの値、カリー化、不変値などの関数が含まれます。OOデザインパターンがこれらの機能のいずれかに近似していることは明らかではありません。

これは、これらの機能がOOPと同じ問題に対応していないためです...これらは命令型プログラミングの代替手段です。OOPに対するFPの答えは、MLとHaskellの型システムにあります。具体的には、合計型、抽象データ型、MLモジュール、Haskell型クラスです。

しかしもちろん、FP言語では解決されないデザインパターンもまだあります。シングルトンに相当するFPとは何ですか?(シングルトンは一般にひどいパターンであることを少しの間無視します)

タイプクラスが最初に行うことは、シングルトンの必要性を排除することです。

23のリストを調べてさらに削除することもできますが、今は時間がありません。


6
タイプクラス(OOPインターフェイスに相当するFP)は、シングルトン(グローバル状態に相当するFP)の必要性をどのようにして排除しますか?
Gabe

4

関数型プログラミングロジックを自然なOO言語に導入するために設計されているGoFデザインパターンは2つだけだと思います。戦略と指揮について考える。他のGoF設計パターンのいくつかは、設計を簡素化して目的を維持するために関数型プログラミングによって変更できます。


4
多くのパターンの主なポイントは、ポリモーフィズムを利用して、FPの概念を適切にサポートすることで自動的に許可されることを実行することです。(たとえば、Builderで私が見たほとんどの化身はカレーの半分しかありません。)関数を値として簡単に扱えるようになると、パターンは多くの場合、単純なものに単純化されます。それらは「コールバックを渡す」または「コールバックのディクショナリを作成する」となり、たとえば、さまざまなビルダークラスがすべて消えることがあります。IMOパターンは、実装する必要があるものではなく、物事どのように機能するかを十分に自明にすると、パターンでなくなる。
cHao

4

本質的に、はい

  • パターンが欠落している機能(高次関数、ストリーム処理など)を回避する場合、その究極性によって構成が容易になります。
  • パターンの実装を何度も書き換える必要性自体は、言語の匂いと見なすことができます。

さらに、このページ(AreDesignPatternsMissingLanguageFeatures)は、「パターン/機能」の変換テーブルと、掘り下げようとする場合のいくつかの素晴らしい議論を提供します。


3

関数型プログラミングは設計パターンを置き換えるものではありません。デザインパターンを置き換えることはできません。

パターンは単に存在します。彼らは時間の経過とともに出現しました。GoFの本はそれらのいくつかを公式化した。開発者が関数型プログラミング言語を使用して新しいパターンが明らかになった場合、それは刺激的なものであり、おそらくそれらについて書かれた本もあるでしょう。


1
デザインパターンを置き換えることはできませんか?それは少し心を閉じていると思います。おそらく、デザインパターンはプログラミングの問題を解決するためのものであることは、だれもが同意できることです。少なくとも、いつかデザインパターンがなくても問題を解決できることを願っています。
メトロポリス

3
任意の特定のパターンは、交換可能かもしれませんが、コンセプトパターンができません。「パターン」という用語は建築の分野で生まれたことを覚えておいてください。
フランクShearar

1
パターンはプログラミングの問題を解決するためのものではありません。パターンはプログラムする方法です。パターンのドキュメンテーションは、プログラミングの問題の解決に役立つことを目的としています。
Torbjørn

3
@Torbjørn:パターンは、言語が邪魔になるときにプログラムする方法です。これらは、プログラムの望ましい動作と言語の組み込み能力との間にいくつかの不一致があるために存在します。要件と能力はうまくマッピングされないか、あいまいにマッピングされます。そうでなければ、パターンはありません。あなたは物事がどのように行われるかである 1つの実装を持っているでしょうし、他の実装は事実上検討する価値がありません。
cHao

1
ただし、パターンは通信を容易にするためにのみ存在します。他に目的はありません。そして、私が長年参加してきたすべての設計会議で、アルゴリズムではなくパターンの重要性が議論されました。このパターンは、意味のある意味で実際に起こっていることをほとんど説明しませんでした。O(n)対O(n Log(n))の影響を正確に説明していますか?いいえ。既存のアーキテクチャにどれほど簡単に適合するかを説明していますか?いいえ。フルスケールアルゴリズムの議論では十分です。パターン自体が廃止されるべきだと私は主張していませんが、廃止されたとしても、結果としてほとんど影響を受けません。

3

「Functional Programming Patterns- in Scala and Clojure」という名前の新しい2013年の本では、著者Michael.B。Linnは、GoFパターンの多くのケースで比較と代替を提供し、「テール再帰」、「メモ化」、「レイジーシーケンス」などの新しい機能パターンについても説明しています。

この本はAmazonで入手できます。数十年のオブジェクト指向の背景から来たときに、それは非常に有益で励みになりました。


3

OOPおよびGoFパターンは状態を扱います。OOPは現実をモデル化し、コードベースを現実の所定の要件に可能な限り近づけます。GoFデザインパターンは、原子の現実世界の問題を解決するために特定されたパターンです。彼らは状態の問題を意味論的に扱います。

実際の関数型プログラミングでは状態が存在しないため、GoFパターンを適用しても意味がありません。GoFデザインパターンと同じように機能的なデザインパターンはありません。関数は現実ではなく数学の構造であるため、すべての機能設計パターンは現実とは対照的に人工的です。

関数が「将来の要求」を処理することを本当に困難にする関数パラメーターの一部でない限り、関数は現在の時刻が何であれ常に同じ値を返すため、時間の概念に欠けています。ハイブリッド言語はこれらの概念を組み合わせており、実際の関数型プログラミング言語ではありません。

関数型言語は、物理学の現在の自然な制限という1つの理由だけで上昇しています。今日のプロセッサは、物理法則により、命令の処理速度が制限されています。クロック周波数は停滞していますが、処理コアが拡大しています。そのため、最新のアプリケーションの速度を上げるために、命令の並列処理がますます重要になっています。関数型プログラミングには定義による状態がなく、したがって副作用がないため、関数を安全に並列処理しても安全です。

GoFパターンは廃止されていません。これらは、少なくとも実際の要件をモデル化するために必要です。しかし、関数型プログラミング言語を使用する場合は、それらを同等のハイブリッド言語に変換する必要があります。最後に、永続化を使用する場合、関数型プログラムのみを作成する機会はありません。プログラムのハイブリッド要素には、GoFパターンを使用する必要性が残っています。純粋に機能する他の要素については、状態がないため、GoFパターンを使用する必要はありません。

GoFパターンは実際の関数型プログラミングには必要ないため、SOLIDの原則を適用すべきではないという意味ではありません。SOLIDの原則は、言語パラダイムを超えています。


2
FPは状態を持つことができます-グローバル、共有、または変更可能な状態はありません。
vt5491 2017

2

関数型プログラミングでは、デザインパターンの意味が異なります。実際、より高いレベルの抽象化とHOFがビルディングブロックとして使用されるため、関数型プログラミングではほとんどのOOP設計パターンは不要です。

HOFの原則は、関数を引数として他の関数に渡すことができることを意味します。関数は値を返すことができます。


1

受け入れられた回答が言ったように、OOPとFPにはすべて特定のパターンがあります。

しかし、私が考えることができるすべてのプログラミングプラットフォームが持っているべきであるほど一般的であるいくつかのパターンがあります。以下は(不完全な)リストです。

  • アダプタ。私は、世界と話す必要がないほど包括的(かつ自己実現)である便利なプログラミングプラットフォームについてはほとんど考えられません。そうする場合は、必ずアダプターが必要です。

  • ファサード。大きなソースコードを処理できるプログラミングプラットフォームは、モジュール化できる必要があります。プログラムの他の部分のモジュールを作成する場合は、コードの「ダーティ」な部分を非表示にし、優れたインターフェースを提供する必要があります。

  • 通訳。一般に、どのプログラムでも、入力の解析と出力の出力の2つだけを実行しています。マウス入力を解析し、ウィンドウウィジェットを印刷する必要があります。したがって、組み込みのインタープリターがあると、プログラムに物事をカスタマイズする追加の力が与えられます。

また、私は典型的なFP言語であるHaskellで、GoFパターンに似ているが名前が異なることに気付きました。私の意見では、FPとOOPの両方の言語で解決すべきいくつかの共通の問題があるため、それらはそこにあったと考えています。

  • モナドトランスフォーマーとデコレーター。前者は既存のモナドに能力を追加するために使用され、後者は既存のオブジェクトに能力を追加します。

1

それぞれのパラダイムは異なる目的を果たしており、このように比較することはできないと思います。

GoFのデザインパターンがすべての言語に適用できるとは聞いていません。これらはすべてのOOP言語に適用できると聞いています。関数型プログラミングを使用する場合、解決する問題の領域はオブジェクト指向言語とは異なります。

ユーザーインターフェイスの作成には関数型言語を使用しませんが、C#やJavaなどのオブジェクト指向言語の1つを使用すると、この作業が簡単になります。関数型言語を作成している場合は、OOデザインパターンの使用を検討しません。


1

OOPとFPの目標は異なります。OOPはソフトウェアコンポーネントの複雑さ/可動部分をカプセル化することを目的としており、FPはソフトウェアコンポーネントの複雑さと依存関係を最小限に抑えることを目的としています。

ただし、これら2つのパラダイムは必ずしも100%矛盾するわけではなく、両方の世界から利益を得るために一緒に適用できます。

C#のような関数型プログラミングをネイティブにサポートしていない言語でも、FPの原則を理解していれば、関数型コードを書くことができます。同様に、OOPの原則、パターン、ベストプラクティスを理解していれば、F#を使用してOOPの原則を適用できます。使用するプログラミング言語に関係なく、解決しようとする状況と問題に基づいて正しい選択をします。


1

一部のパターンは、FPをサポートする言語で実装する方が簡単です。たとえば、Strategyはクロージャーをうまく使用して実装できます。ただし、状況によっては、クラスベースのアプローチを使用して戦略を実装することを好む場合があります。たとえば、戦略自体が非常に複雑である場合や、テンプレートメソッドを使用してモデル化する構造を共有する場合などです。

マルチパラダイム言語(Ruby)で開発した私の経験では、FP実装は単純なケースでうまく機能しますが、コンテキストがより複雑な場合は、GoF OOPベースのアプローチの方が適しています。

FPアプローチはOOPアプローチを置き換えるものではなく、それを補完します。


0

関数型プログラミングの最も重要特性、私見では、あなたが何もなく、使用してプログラミングされていることである表現 -表現内の式内の式の全てが「温めマシンが評価」という最後の、最後の式に評価すること。

オブジェクト指向プログラミングの最大の特徴であるIMHOは、内部状態を持つオブジェクトを使用してプログラミングすることです。純粋な関数に内部状態を含めることはできません。オブジェクト指向プログラミング言語には、物事を起こすためのステートメントが必要です。(関数型プログラミングにはステートメントがありません。)

リンゴとオレンジを比較しています。関数型プログラミングは式を使用したプログラミングであり、オブジェクト指向プログラミングは内部状態を使用したプログラミングであるため、オブジェクト指向プログラミングのパターンは関数プログラミングには適用されません。


うーん、私は質問が答える前に11歳だったことに気づくべきでした。:-)
ジム・フラッド

0

あなた自身を支えます。

設計パターンを置き換えてSOLIDとDRYを暴いたと私が主張するのを聞くと、多くの人が悪化します。私はだれでもない。それにもかかわらず、私はコラボレーティブ(製造)アーキテクチャを正しくモデル化し、プロセスを構築するためのルールを、その背後にあるコードと科学とともにWebサイトhttp://www.powersemantics.com/で公開しました。

私の主張は、設計パターンは、製造が「マスカスタマイゼーション」と呼ぶものを達成しようとするものであり、すべてのステップを再形成、再構成、拡張できるプロセスフォームです。このようなプロセスは、コンパイルされていないスクリプトと考えることができます。ここでは(オンラインの)議論を繰り返すつもりはありません。要するに、私の大規模カスタマイズアーキテクチャは、面倒なセマンティクスを一切使用せずにその柔軟性を実現することで、デザインパターンを置き換えます。モデルが上手く機能していることに驚きましたが、プログラマーがコードを書く方法は、製造がどのように共同作業を組織化するかについてのキャンドルを保持していません。

  • 製造=各ステップは1つの製品と相互作用します
  • OOP =各ステップはそれ自体および他のモジュールと相互作用し、役に立たないオフィスワーカーのようにポイントツーポイントで製品を渡します

このアーキテクチャはリファクタリングを必要としません。複雑さに影響を与える集中化と配布に関するルールもあります。しかし、あなたの質問に答えるために、関数型プログラミングは別の一連の処理セマンティクスであり、1)ソースルーティングが(スクリプト)ドキュメントとして存在し、起動前に書き直すことができ、2)モジュールが簡単かつ動的に追加または削除されます。

OOPは「ハードコードされたプロセス」のパラダイムであり、デザインパターンはそのパラダイムを回避する方法であると言えます。しかし、それがマスカスタマイゼーションのすべてです。設計パターンは動的プロセスを乱雑なハードコードとして具体化します。意味がありません。F#が関数をパラメーターとして渡すことができるという事実は、関数型言語とOOP 言語が同様に、大規模なカスタマイズ自体を達成しようとする試みを意味します。

読者にとって、スクリプトを表すハードコードはどれほど混乱しますか?コンパイラのコンシューマがそのような機能にお金を払うと思っても、私にはまったくそのような機能は意味のない無駄です。大量カスタマイズのポイントは、Visual Studioを使用するプログラマーにとって動的ではなく、プロセス自体を動的にすることので、それらは無意味です。


0

(OCamlのような、クラス、モジュールなどの)高水準の関数型PLは、型の多様性と表現力の点でOOP命令型言語よりも確実に優先されます。抽象化はリークせず、ほとんどのアイデアをプログラムで直接表現できます。したがって、はい、それは設計パターンに取って代わります。そのほとんどは、とにかく機能パターンに比べて途方もなく単純化されています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.