OOPフリーのデザインパターン

Lispで書いて、OOPにまったく慣れていない頃、私は自分のコードに適用できるデザインパターンを見つけようとしました。 そして、いつも不気味なクラススキームに出くわしました。 その結果、これらのパターンは関数型プログラミングには適用できないと結論付けました。



今私はPythonで書いており、OOPに精通しています。 そして、そのパターンは今ではずっと明確になっています。 しかし、私はまだクラスの拡散スキームに後戻りしています。 多くのパターンは機能的なパラダイムでうまく機能します。 いくつかの例を説明します。

パターンの古典的な実装は行いません。 それらに詳しくない人は、ウィキペディアまたは他の情報源に興味があるかもしれません。



オブザーバー


一部のオブジェクトがメッセージをサブスクライブできるようにし、何らかの方法でこれらのメッセージを送信する必要があります。

「メール」である辞書によって実装されます。 キーはニュースレターの名前で、値は購読者のリストです。

from collections import defaultdict mailing_list = defaultdict(list) def subscribe(mailbox, subscriber): #   subscriber     mailbox mailing_list[mailbox].append(subscriber) def notify(mailbox, *args, **kwargs): #    mailbox,    for sub in mailing_list[mailbox]: sub(*args, **kwargs)
      
      





これで、ニュースレターの任意の機能にサインアップできます。 主なことは、同じ配布グループに含まれる機能のインターフェイスに互換性があることです。

 def fun(insert): print 'FUN %s' % insert def bar(insert): print 'BAR %s' % insert
      
      







機能をニュースレターに登録します。

 >>> subscribe('insertors', fun) >>> subscribe('insertors', bar) >>> subscribe('bars', bar)
      
      







コードの任意の場所で、これらのニュースレターの通知をトリガーし、すべてのサブスクライバーがイベントに応答することを確認します。

 >>> notify('insertors', insert=123) FUN 123 BAR 123 >>> notify('bars', 456) BAR 456
      
      







テンプレートメソッド


アルゴリズムのフレームワークを指定し、ユーザーがアルゴリズムの特定のステップを再定義できるようにする必要があります。

map、filter、reduceなどの高階関数は、本質的にそのようなパターンです。 しかし、同じことを自分で行う方法を見てみましょう。

 def approved_action(checker, action, obj): # ,     obj  action, #   checker    if checker(obj): action(obj) import os def remove_file(filename): approved_action(os.path.exists, os.remove, filename) import shutil def remove_dir(dirname): approved_action(os.path.exists, shutil.rmtree, dirname)
      
      





ファイルとフォルダーを削除する機能があり、削除するものがあるかどうかを事前に確認します。

「パターン」を直接呼び出すことがパターンと矛盾するように思われる場合は、カリー化を使用して関数を定義できます。 さて、アルゴリズムのすべての部分ではない「再定義」の可能性をヒープに導入してください。

 def approved_action(obj, checker=lambda x: True, action=lambda x: None): if checker(obj): action(obj) from functools import partial remove_file = partial(approved_action, checker=os.path.exists, action=os.remove) remove_dir = partial(approved_action, checker=os.path.exists, action=shutil.rmtree) import sys printer = partial(approved_action, action=sys.stdout.write)
      
      







状態


オブジェクトの状態に応じて、オブジェクトのさまざまな動作を提供する必要があります。

申請のプロセスを記述する必要があると想像してみましょう。これには数サイクルの承認が必要になる場合があります。

 from random import randint # ,      . #       #  randint  ,  -       def start(claim): print u' ' claim['state'] = 'analyze' def analyze(claim): print u' ' if randint(0, 2) == 2: print u'   ' claim['state'] = 'processing' else: print u' ' claim['state'] = 'clarify' def processing(claim): print u'   ' claim['state'] = 'close' def clarify(claim): if randint(0, 4) == 4: print u'   ' claim['state'] = 'close' else: print u' ' claim['state'] = 'analyze' def close(claim): print u' ' claim['state'] = None #   .       state = {'start': start, 'analyze': analyze, 'processing': processing, 'clarify': clarify, 'close': close} #     def run_claim(): claim = {'state': 'start'} #   while claim['state'] is not None: #  ,     fun = state[claim['state']] #    fun(claim)
      
      





ご覧のとおり、コードの主要部分は、パターンのアプリケーションのオーバーヘッドではなく、「ビジネスロジック」によって占められています。 オートマトンは、状態ディクショナリの関数を追加/置換するだけで簡単に拡張および変更できます。



数回実行して、動作することを確認します。

 >>> run_claim()                     >>> run_claim()            
      
      







チーム


タスクは「コールバック」を整理することです。 つまり、呼び出されたオブジェクトはそのコードから呼び出し元にアクセスできます。

このパターンは、静的言語の制限のために発生したようです。 機能的労働者は彼にパターンの称号さえ与えなかったでしょう。 関数があります-好きな場所に渡し、保存して、呼び出してください。

 def foo(arg1, arg2): #   print 'FOO %s, %s' (arg1, arg2) def bar(cmd, arg2): #  .      foo... print 'BAR %s' % arg2 cmd(arg2 * 2) # ...  
      
      







コマンドパターンの初期タスクでは、いくつかのパラメーターをコマンドオブジェクトに事前に転送する機能もあります。 都合次第で、カレーで決めるか......

 >>> from functools import partial >>> bar(partial(foo, 1), 2) BAR 2 FOO 1, 4
      
      





...ラムダでラップするか

 >>> bar(lambda x: foo(x, 5), 100) BAR 100 FOO 200, 5
      
      







一般的な結論


抽象クラス、具象クラス、インターフェースなどの庭をフェンスで囲む必要はありません。 関数を最初のクラスのオブジェクトとして処理する可能性は最小限であるため、同じデザインパターンをかなり簡潔に使用できます。 時には気付かないうちに:)



All Articles