昨年F#とOCamlを学び始めて以来、デザインパターン(特にJava)が命令型言語で欠けている機能のための回避策であると主張する膨大な数の記事を読みました。私が見つけたある記事 はかなり強い主張をしています :
私が会った人のほとんどは、Gang of Fourのデザインパターンの本を読んでいます。どの言語を使用しているかに関係なく、この本は言語にとらわれず、パターンはソフトウェア工学一般に適用されるという自尊心のあるプログラマーなら誰でも知っています。これは高貴な主張です。残念ながら、それは真実からかけ離れています。
関数型言語は非常に表現力があります。 関数型言語では、デザインパターンは必要ありません。言語は非常に高水準なので、デザインパターンを完全に排除する概念でプログラミングを終了します。
関数型プログラミングの主な機能には、一級値、カレー、不変値などの機能が含まれます。OOデザインパターンがこれらの機能のいずれにも近いことは明らかではありません。
さらに、OOPをサポートする関数型言語(F#やOCamlなど)では、これらの言語を使用しているプログラマが他のすべてのOOP言語で使用可能なデザインパターンを使用することは明らかです。実際、今は毎日F#とOCamlを使っています。これらの言語で使っているパターンと、Javaで書いたときに使っているパターンの間には、驚くほどの違いはありません。
関数型プログラミングによってOOPデザインパターンが不要になるという主張に真実はありますか?もしそうなら、典型的なOOPデザインパターンとその機能的に等価なものの例を投稿またはリンクできますか?
あなたが引用したブログ投稿は、その主張を少し誇張しています。 FPは、デザインパターンの必要性を排除しません。 「デザインパターン」という用語は、FP言語で同じことを説明するために広く使用されているわけではありません。しかし、それらは存在します。関数型言語には、「問題Xに遭遇したとき、Yのように見えるコードを使用する」という形式のベストプラクティスルールがたくさんあります。これは基本的にデザインパターンです。
ただし、ほとんどのOOP固有のデザインパターンが関数型言語ではほとんど無関係であることは正しいです。
設計パターン全般は、言語の欠点を補うためにのみ存在すると言うことは、特に議論の余地があるとは思わない。そして、別の言語が同じ問題を簡単に解決できる場合、その他の言語はそのための設計パターンを必要としません。その言語のユーザーは、問題が存在することにさえ気付かないかもしれません。それは、その言語では問題ではないからです。
ギャングオブフォーは、この問題について次のように述べています。
プログラミング言語の選択は、自分の視点に影響を与えるため重要です。私たちのパターンは、Smalltalk/C++レベルの言語機能を想定しており、その選択により、簡単に実装できるものとできないものが決まります。手続き型言語を想定した場合、「継承」、「カプセル化」、および「ポリモーフィズム」と呼ばれる設計パターンが含まれている可能性があります。同様に、一部のパターンは、あまり一般的ではないオブジェクト指向言語によって直接サポートされています。たとえば、CLOSには複数のメソッドがあり、Visitorなどのパターンの必要性を減らします。実際、SmalltalkとC++には十分な違いがあり、ある言語では他の言語よりも簡単にパターンを表現できることを意味します。 (たとえば、イテレータを参照してください。)
(上記は、デザインパターンブックの概要、ページ4、段落3からの引用です)
関数型プログラミングの主な機能には、ファーストクラスの値、カリー化、不変の値などの関数が含まれます。OOデザインパターンがこれらの機能のいずれかに近似していることは私には明らかではありません。
第一級機能の近似ではない場合、コマンドパターンは何ですか? :) FP言語では、関数を引数として別の関数に渡すだけです。 OOP言語では、関数をクラスにラップする必要があります。これをインスタンス化して、そのオブジェクトを他の関数に渡すことができます。効果は同じですが、OOPではデザインパターンと呼ばれ、非常に多くのコードを必要とします。そして、カリー化しない場合、抽象的なファクトリーパターンは何ですか?パラメータを一度に少しずつ関数に渡し、最終的に呼び出すときにどのような値を出力するかを設定します。
そのため、いくつかのGoFデザインパターンがFP言語で冗長化されています。これは、より強力で使いやすい代替が存在するためです。
しかしもちろん、FP言語によって解決されるnotであるデザインパターンもあります。シングルトンに相当するFPとは何ですか? (シングルトンは一般的に使用するのが恐ろしいパターンであることをしばらく無視します)
そして、それは両方の方法でも機能します。私が言ったように、FPにはそのデザインパターンもあり、人々は通常それらをそのように考えません。
しかし、あなたはモナドに出くわしたかもしれません。 「グローバル状態に対処する」ための設計パターンでない場合、それらは何ですか?これはOOP言語では非常に単純な問題であり、同等のデザインパターンは存在しません。
「静的変数をインクリメントする」または「ソケットから読み取る」ためのデザインパターンは必要ありません。なぜなら、それはまさにあなたdoであるからです。
Monadがデザインパターンであると言うのは、整数が通常の操作とゼロ要素を持つデザインパターンであると言うのと同じくらいばかげています。いいえ、モナドは数学的パターンであり、デザインパターンではありません。
(純粋な)関数型言語では、モナドの「デザインパターン」または同じことを許可する他の方法で回避しない限り、副作用と可変状態は不可能です。
さらに、OOPをサポートする関数型言語(F#やOCamlなど)では、これらの言語を使用するプログラマーが、他のすべてのOOPで利用可能な同じデザインパターンを使用することは明らかです言語。実際、現在私は毎日F#とOCamlを使用していますが、これらの言語で使用するパターンとJavaで書くときに使用するパターンとの間に顕著な違いはありません。
おそらくあなたはまだ命令的に考えているのでしょうか?多くの人は、命令型言語を一生ずっと扱った後、関数型言語を試そうとするとその習慣をあきらめるのに苦労します。 (私はF#でかなり面白い試みを見てきましたが、文字通りevery関数は基本的にあなたがしたかのように「let」文の文字列でしたCプログラムを使用し、すべてのセミコロンを「let」に置き換えました。:))
しかし、別の可能性として、OOP言語の設計パターンを必要とする問題を些細なことで解決していることに気付いていない可能性があります。
カリー化を使用する場合、または関数を別の関数に引数として渡す場合は、OOP言語でそれをどのように行うかを考えてください。
関数型プログラミングによってOOP設計パターンが不要になるという主張に真実はありますか?
うん。 :) FP言語で作業する場合、OOP固有のデザインパターンは不要になります。しかし、MVCやその他のOOP固有ではないものなど、いくつかの一般的なデザインパターンが必要であり、代わりにいくつかの新しいFP固有の「デザインパターン」が必要です。すべての言語には欠点があり、通常、デザインパターンはそれらを回避する方法です。
とにかく、ML(少なくとも個人的なお気に入りで、少なくとも学習目的のため)やHaskellのような "クリーナー" FP言語、またはOOP何か新しいことに直面したときに頼る松葉杖。
予想通り、少数の人々が「言語の欠点を補う」というデザインパターンの定義に反対したので、ここに私の正当性があります:既に述べたように、ほとんどのデザインパターンは1つのプログラミングパラダイム、または特定の言語にさえ固有です。多くの場合、彼らはそのパラダイムにexistのみが存在する問題を解決します(FPのモナド、またはOOPの抽象ファクトリーを参照)。 FPに抽象ファクトリパターンが存在しないのはなぜですか?解決しようとする問題はそこに存在しないためです。そのため、OOP言語には問題があり、それはFP言語には存在しませんが、それはOOP言語の欠点です。この問題は解決できますが、言語では解決できませんが、回避するには大量の定型コードが必要です。理想的には、プログラミング言語でallの問題を魔法のように解消してほしい。依然として存在する問題は、原則として言語の欠点です。 ;)
関数型プログラミングがOOPデザインパターンの必要性を排除するという主張に真実はありますか?
関数型プログラミングはオブジェクト指向プログラミングと同じではありません。オブジェクト指向設計パターンは関数型プログラミングには適用されません。代わりに、関数型プログラミング設計パターンがあります。
関数型プログラミングについては、OOデザインパターンの本を読むことはなく、FPデザインパターンに関する他の本を読むことになります。
言語にとらわれない
完全ではありません。 OO言語に関しては言語非依存です。デザインパターンは手続き型言語にはまったく適用されません。それらはリレーショナルデータベース設計の文脈ではほとんど意味がありません。スプレッドシートをデザインするときには適用されません。
典型的なOOPデザインパターンとそれと同等の機能
上記は存在しないはずです。これは、OO codeに書き換えられた手続き型コードを要求するようなものです。うーん...私がオリジナルのFortran(またはC)をJavaに翻訳するならば、私はそれを翻訳する以上のことをしていません。完全にOOパラダイムに書き直すと、元のFortranやCのようには見えなくなります - 認識できなくなります。
OO DesignからFunctional Designへの単純なマッピングはありません。彼らは問題を見て非常に異なる方法です。
関数型プログラミング(すべてのスタイルのプログラミングのように)にはデザインパターンがあります。リレーショナルデータベースにはデザインパターンがあり、OOにはデザインパターンがあり、手続き型プログラミングにはデザインパターンがあります。すべてにデザインパターンがあり、建物の建築さえもあります。
概念としてのデザインパターンは、テクノロジや問題領域に関係なく、時代を超越した構築方法です。ただし、特定のデザインパターンは特定の問題領域とテクノロジに適用されます。
自分のしていることを考えている人は誰でもデザインパターンを明らかにすることでしょう。
言語とパターンの間の密接な関係についてのBrianのコメントは、要点である、
この議論の欠けている部分はイディオムの概念です。 Coplienの著書「Advanced C++」はここで大きな影響を与えました。彼がChristopher Alexanderと名前のないコラムを発見するずっと前(そしてAlexanderも読まずにパターンについて賢明に話すことはできません)、彼は真に言語を学ぶ上での熟語の習得の重要性。彼は例としてCで文字列のコピーを使いましたが、(* from ++ = * to ++);これは、言語機能(またはライブラリ機能)の欠如に対する防衛策と見なすことができますが、実際に重要なのは、それが他のどの部分よりも大きい思考単位、つまり表現単位であるということです。
それが、私たちの意図をより簡潔に表現できるようにするために、パターンと言語がやろうとしていることです。思考の単位が豊富であればあるほど、あなたが表現できる思考はより複雑になります。システムアーキテクチャーから少々いじるまで、さまざまな規模の豊かで共有された語彙を持つことで、より知的な会話をしたり、何をすべきかについて考えたりすることができます。
個人としても学ぶことができます。これが運動の全体的なポイントです。私たち一人一人が自分自身について考えることができないだろうものを理解し、使うことができます。言語、フレームワーク、図書館、パターン、慣用句などはすべて、知的財産を共有するための場所を持っています。
GOFの本は明示的にOOPに結びついています - タイトルはデザインパターン - 再利用可能な要素オブジェクト指向ソフトウェアです(強調)。 )
Peter NorvigによるDynamic Programmingのデザインパターン は、この「機能的」ではなく「動的な」言語について重点を置いています(重複があります)。
このトピックについて議論するもう一つのリンクはここにあります: http://blog.ezyang.com/2010/05/design-patterns-in-haskel/
彼のブログ記事で、EdwardはHaskellに関して23のオリジナルGoFパターンすべてを説明しています。
これを「デザインパターン」(一般的)と「FP vs. OOP」のレベルで比較しようとすると、答えはせいぜい曖昧です。
ただし、両方の軸をより深く調べ、特定のデザインパターンおよび特定の言語機能を検討すると、状況がより明確になります。
たとえば、 Visitor 、 Strategy 、 Command 、および Observer などの特定のパターンは、言語を使用すると確実に変更されたり消えたりします。 代数データ型とパターンマッチング 、 クロージャ 、 ファーストクラス関数 などを使用してください。
一般に、私は、時間が経つにつれて、特定のパターンが新しい(または単に人気が高まっている)言語機能によって取り除かれていると言うでしょう。これは言語設計の自然な流れです。言語がより高度なものになるにつれて、以前は例を使って本の中でしか呼び出せなかった抽象化が、今や特定の言語機能やライブラリの応用になります。
(さておき、ここで 最近のブログ を書いた。これにはFPやデザインパターンに関するより多くの議論へのリンクがある。)
Norvigの発表は、彼らがすべてのGoFパターンを分析したことをほのめかしており、23のパターンのうち16が関数型言語での実装が単純であるか、あるいは単に言語の一部であったと述べています。したがって、おそらくそれらのうち少なくとも7つは、a)同じくらい複雑であるか、またはb)言語に存在していませんでした。残念ながら、それらは列挙されていません。
GoFの「創造的」または「構造的」パターンの大部分は、JavaまたはC++のプリミティブ型システムにあなたが望むことをさせるためのトリックに過ぎないことは明らかだと思います。しかし、残りの言語は、あなたがどの言語でプログラムしているかに関わらず、考慮に値するものです。
プロトタイプかもしれません。これはJavaScriptの基本的な概念ですが、他の言語では最初から実装する必要があります。
私のお気に入りのパターンの1つはNull Objectパターンです。適切な種類の何もしないオブジェクトとして何かが存在しないことを表します。これは関数型言語でモデル化する方が簡単かもしれません。しかし、本当の成果は視点の変化です。
私は、LISPのようにマクロをサポートしている言語があれば、ドメイン固有の抽象化を構築することができると思います。抽象化は、一般的な慣用句の解決策よりもはるかに優れています。
そしてOOデザインパターンのソリューションでさえ言語固有のものです。デザインパターンはあなたのプログラミング言語があなたのために解決しない一般的な問題の解決策です。 Javaでは、シングルトンパターンは何かの問題(単純化された問題)を解決します。 Scalaでは、Classに加えてObjectというトップレベルの構成要素があります。それは怠惰にインスタンス化されていて、1つだけあります。シングルトンを取得するためにシングルトンパターンを使用する必要はありません。それは言語の一部です。
パターンは、何度も何度も見られ、そして記述され文書化されるようになる同様の問題を解決する方法です。いいえ、FPはパターンを置き換えません。ただし、FPは新しいパターンを作成し、現在の「ベストプラクティス」パターンを「時代遅れ」にする可能性があります。
他の人が言っているように、関数型プログラミングに特有のパターンがあります。デザインパターンを取り除くことの問題は、それほど機能的なものに切り替えることの問題ではなく、言語の機能の問題だと思います。
Scalaがどのようにして "シングルトンパターン"を取り除いているかを見てください。あなたは単にクラスの代わりにオブジェクトを宣言します。もう1つの機能、パターンマッチングは、訪問者パターンの不器用さを避けるのに役立ちます。ここで比較を参照してください。 http://andymaleh.blogspot.com/2008/04/scalas-pattern-matching-visitor-pattern.html
そしてScalaはF#のようにオブジェクト指向関数の融合です。 F#についてはわかりませんが、おそらくこのような機能があります。
クロージャは関数型言語で存在しますが、それに限定される必要はありません。彼らは委任パターンを手助けします。
もう一つの観察。このコードはパターンを実装しています。これはとても古典的で、とても要素的なので、通常は「パターン」とは見なされませんが、次のようになります。
for(int i = 0; i < myList.size(); i++) { doWhatever(myList.get(i)); }
JavaやC#などの命令型言語では、これを処理するための本質的に機能的な構成要素である「foreach」を採用しています。
GoFデザインパターンは、JavaやC++のようなSimula 67の子孫であるOO言語のための回避策レシピをコーディングしています。
デザインパターンによって扱われる「病気」の大部分は以下によって引き起こされます。
ソリューションが対応する設計パターンと基本的に同じ方法で構造化されていても、Common LISP Object Systemには表示されない設計パターンは1つもありません。 (さらに、そのオブジェクトシステムは、10年以上も前にGoFの本よりも優れています。Common LISPは、その本が最初に出版された年と同じ年にANSI規格になりました。)
関数型プログラミングに関しては、パターンが適用されるかどうかは、与えられた関数型プログラミング言語がある種のオブジェクトシステムを持っているかどうか、そしてパターンから利益を得るオブジェクトシステムをモデルにしているかどうかによって決まります。そのようなオブジェクト指向は、関数型プログラミングとうまく組み合わされません。なぜなら、状態の変化は前面と中心にあるからです。
構築および非変更アクセスは関数型プログラミングと互換性があるため、抽象化アクセスまたは構築に関連したパターン(Factory、Facade、Proxy、Decorator、Visitorなど)を適用できます。
一方で、State and Strategyのような行動パターンは、おそらく直接は機能的OOPには当てはまりません。彼らのコアこれは彼らが適用されないという意味ではありません。おそらく、それらは、ミュータブル状態をシミュレートするために利用可能なあらゆるトリックと組み合わせて何らかの形で適用されます。
Jeremy Gibbonsによる優れた、やや密度の高い論文をいくつか取り上げたいと思います。「高次データ型ジェネリックプログラムとしてのデザインパターン」と「イテレータパターンの本質」(両方ともここで利用可能です: http ://www.comlab.ox.ac.uk/jeremy.gibbons/publications/ ).
これらは両方とも、慣用的な機能構成が他の(オブジェクト指向の)設定における特定の設計パターンによってカバーされる地形をどのようにカバーするかを説明します。
型システムを立ち上げることなしにこの議論をすることはできません。
関数型プログラミングの主な機能には、一級値、カレー、不変値などの機能が含まれます。OOデザインパターンがこれらの機能のいずれにも近いことは明らかではありません。
これは、これらの機能がOOPと同じ問題に対処していないためです。それらは命令型プログラミングの代替手段です。 FPに対するOOPの答えは、MLとHaskellの型システムにあります。具体的には、sum型、抽象データ型、MLモジュール、Haskell型クラスです。
しかしもちろんFP言語によって解決されていないデザインパターンがまだあります。シングルトンのFPに相当するものは何ですか? (ちょっとシングルトンが一般的に使うのがひどいパターンであることを無視します)
型クラスが最初にすることは、シングルトンの必要性を排除することです。
あなたは23のリストを通過してさらに削除することができますが、私は今のところ時間がありません。
2つのGoFデザインパターンだけが関数型プログラミングロジックを自然なOO言語に導入するように設計されていると思います。戦略と司令について考えます。他のGoF設計パターンのいくつかは、設計を単純化し目的を維持するために関数型プログラミングによって修正することができます。
基本的に、はいです。
その上、この ページ(AreDesignPatternsMissingLanguageFeatures) は、「パターン/機能」の変換テーブルと、適切なDigディスカッションを行います。
OOPとGoFのパターンは状態を扱います。 OOPはコードベースを現実の与えられた要件にできるだけ近づけるために現実をモデル化します。 GoFデザインパターンは、原子実世界の問題を解決するために特定されたパターンです。彼らは意味論的な方法で国家の問題を扱います。
実際の関数型プログラミングでは状態が存在しないため、GoFパターンを適用することは意味がありません。 GoFのデザインパターンと同じように、機能的なデザインパターンはありません。機能は現実ではなく数学の構成要素であるため、すべての機能設計パターンは現実とは対照的に人工的です。
時間が関数パラメータの一部でない限り「未来の要求」を処理することを本当に難しくしない限り、関数は常に同じ値を返すので時間の概念を欠きます。ハイブリッド言語がこれらの概念を混在させると、言語は実際の関数型プログラミング言語ではなくなります。
関数型言語は、物理学の現在の自然な制限という1つの理由だけで増えています。今日のプロセッサは、物理的な法律により命令の処理速度が制限されています。クロック周波数は停滞していますが、プロセッシングコアは拡大しています。現代のアプリケーションの速度を上げるために、命令の並列処理がますます重要になっているのはその理由です。定義による関数型プログラミングには状態がなく、したがって副作用がないため、関数を安全に並行して処理することが安全です。
GoFパターンは時代遅れではありません。それらは少なくとも実社会の要件をモデル化するために必要です。しかし、もしあなたが関数型プログラミング言語を使うなら、あなたはそれらをそれらのハイブリッド同等物に変換しなければなりません。最後に、パーシスタンスを使用した場合、機能的なプログラムだけを作成することはできません。あなたのプログラムのハイブリッド要素のために、GoFパターンを使う必要性が残っています。純粋に機能的な他の要素は、状態がないためGoFパターンを使用する必要はありません。
GoFパターンは実際の関数型プログラミングには必要ないため、SOLIDの原則が適用されるべきではありません。 SOLIDの原則は、あらゆる言語パラダイムを超えています。
関数型プログラミングでは、デザインパターンの意味が異なります。実際、OOPデザインパターンの大部分は、抽象化とHOFはビルディングブロックとして使われます。
HOFの原則は、関数を引数として他の関数に渡すことができることを意味します。関数は値を返すことができます。
「ScalaとClojureにおける関数型プログラミングパターン」という名前の新しい2013年の著書Michael.B。 Linnは、GoFパターンの多くの場合に置き換えを比較し提供することができています。また、 'tail recursion'、 'memoization'、 'lazy sequence'などの新しい機能パターンについても説明しています。
この本はAmazonで入手できます。私はそれが非常に有益で励みになることを数十年のOO経歴から来るときに感じました。
関数型プログラミングは設計パターンを置き換えません。デザインパターンは交換できません。
パターンは単に存在します。彼らはやがて現れた。 GoFの本はそれらのいくつかを形式化した。開発者が関数型プログラミング言語を使用するにつれて新しいパターンが明るみに出るとすれば、それはエキサイティングなことであり、おそらくそれらについて書かれた本もあるでしょう。
それぞれのパラダイムはそれぞれ異なる目的を果たしているので、このように比較することはできません。
GoFのデザインパターンがすべての言語に適用できるとは聞いていません。私は彼らがすべてのOOP言語に適用できると聞いた。もしあなたが関数型プログラミングを使うなら、あなたが解決する問題の領域はOO言語とは異なります。
関数型言語を使ってユーザーインターフェイスを作成することはしませんが、C#やJavaのようなOO言語のいずれかを使用すると作業が簡単になります。私が関数型言語を書いているのなら、私はOOデザインパターンを使うことを考えません。
OOPとFPには異なる目標があり、OOPはソフトウェアコンポーネントの複雑さ/可動部分をカプセル化することを目的とし、FPはソフトウェアコンポーネントの複雑さと依存関係を最小限に抑えることを目指しますこれら2つのパラダイムは必ずしも100%矛盾しているわけではなく、両方の世界から利益を得るために一緒に適用できます。 C#のような関数型プログラミングをネイティブにサポートしない言語であっても、FP原則を理解すれば関数型コードを書くことができ、同様に_を理解すればF#を使用してOOP原則を適用できますOOP原則、パターン、およびベストプラクティス。使用するプログラミング言語に関係なく、解決しようとする状況と問題に基づいて適切な選択を行います。
受け入れられた答えが言ったように、OOPとFPはすべて特定のパターンを持っています。
しかし、私が考えることができるすべてのプログラミングプラットフォームが持つべきであるほど一般的ないくつかのパターンがあります。これが(不完全な)リストです。
アダプタ。私は、それが世界と話をする必要がないほど非常に包括的(そして自己完結的)である有用なプログラミングプラットフォームを考えることはほとんどできません。それがそうするつもりなら、アダプタは間違いなく必要です。
ファサード。大きなソースコードを処理できるプログラミングプラットフォームは、モジュール化できるはずです。プログラムの他の部分のためにモジュールを作成するのであれば、コードの「汚れた」部分を隠して、それにNiceインターフェースを与えたいでしょう。
通訳。一般的に、プログラムは入力と出力を解析するという2つのことをしています。マウス入力を解析し、ウィンドウウィジェットを印刷する必要があります。したがって、組み込みのインタプリタを持つことはプログラムに物事をカスタマイズするための追加の力を与えます。
また、私は典型的なFP言語、Haskellに気付きました。GoFパターンに似たものがありますが、名前が異なります。私の意見では、これはFPとOOPの両方の言語で解決する共通の問題がいくつかあるために存在したことを示唆しています。