ここ数年、私が使いたい言語はますます「機能的」になっています。私は今、「ハイブリッド」のような言語を使用しています:C#、F#、Scala。私は、ドメインオブジェクトに対応するクラスを使用してアプリケーションを設計し、機能的な機能を使用してコーディングをより簡単に、より正確に、そしてより安全にする(特に、コレクションでの操作時または関数の受け渡し時)のが好きです。
しかし、デザインパターンに関しては、2つの世界が「衝突」します。私が最近直面した具体的な例は、オブザーバーパターンです。アイテムが作成または変更されたときに、プロデューサーに他のコード(「コンシューマー/オブザーバー」、たとえばDBストレージ、ロガーなど)に通知してほしい。
私は最初に次のように「機能的に」行いました:
producer.foo(item => { updateItemInDb(item); insertLog(item) })
// calls the function passed as argument as an item is processed
しかし、もっと "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
2つのアプローチの賛否両論はどれですか。私はかつてFP=グルがデザインパターンは言語の制限のためだけにあると言い、それが関数型言語の数が非常に少ない理由だと言います。これはその例かもしれません。
編集:私の特定のシナリオではそれは必要ありませんが、機能的な方法で「オブザーバー」の削除と追加をどのように実装しますか? (つまり、パターンのすべての機能をどのように実装しますか?)たとえば、新しい関数を渡すだけですか?
これは、呼び出し側オブジェクトの関心の境界の外でタスクを実行するという概念を運ぶ2つの異なるアプローチの良い例です。
この例では関数型のアプローチを採用する必要があることは明らかですが、一般的には、呼び出されたオブジェクトが示す動作の複雑さによって異なります。それが本当に複雑な動作の問題であり、同様のロジックを頻繁に再適用し、関数ジェネレーターを使用してそれを明確に表現できない場合は、おそらくクラスの構成または継承に行きたいでしょう。アドホックベースで既存の動作を再利用および拡張するためのもう少し自由度があります。
ただし、私が観察したパターンの1つは、通常、開発者は最初は機能的アプローチを採用し、より細かい動作が必要になったときにクラスベースのアプローチを採用することを決定するというものです。たとえば、Djangoは、機能ベースのビュー、テンプレートローダー、テストランナーからクラスベースのビューに移行しました。
あなたの「FPグル」は部分的に正しいです。多くのOOパターンは機能的なことをするためのハックです(彼がこれが少数のFP言語が最も疑わしいと思われる理由であると主張します。)オブザーバーと戦略パターンはファーストクラスの関数をエミュレートしようとしています。ビジターパターンはパターンマッチングをシミュレートするハックです。あなたのIItemObserver
は単なる変装した関数です。アイテムを購入しない他の関数とは異なるふりをしていますあなたは何でも。
オブジェクトは、一種のデータ抽象化にすぎません。 この論文 は、そのトピックに関するいくつかの光を当てるのに役立ちます。オブジェクトは便利ですが、すべてに適しているわけではないことを認識することが重要です。二分法はありません。単に、適切な仕事に適切なツールを選択するという問題であり、関数型プログラミングでは、オブジェクトをあきらめる必要はありません。それはさておき、関数型プログラミングは単に関数を使用するだけではありません。また、副作用や突然変異を最小限に抑えることについてもです。
機能バージョンははるかに短く、保守が容易で、読みやすく、一般に、考えられるあらゆる点で非常に優れています。
多くのパターンですが、すべてのパターンからはほど遠いですあるオブザーバーなどのOOPの機能の欠如を補うため。それらは機能的にはるかによくモデル化されています。