2016-04-06 4 views
0

Javaから来ている間にFacadeを慣用Rubyに実装しようとしています。私はRailsのActiveRecordがクラスメソッドをfind_by(criteria)のように使うのが好きで、そのタスクのリポジトリパターンは使用していないことがわかります。Rubyで慣用的にファサードを作成する

My Facadeは、特定のWebサービスをいくつかの方法でラップします。私のオリジナルのアイデアは、それが(模倣による学習)ActiveRecordに似たAPIです作ることだった。

class MyEntity 
    # .... 

    def get_name 
    @loaded_name + @loaded_surname 
    end 

    def delete 
    @entity_access_service.delete(@id) 
    end 

    def save 
    @entity_access_service.save(@id, @loaded_name , @loaded_surname) 
    end 

    def self.find(id) 
    data = @entity_access_service.get_data_for(id) 
    MyEntity.new(data) #Or whatever way to populate my entity 
    end 
end 

これ、理論的には、偉大な仕事になります。

e = MyEntity.find(10) 
p e.get_name 
e.delete 

または:

e = MyEntity.new(some stuff) 
e.save 

質問: savedeleteのインスタンスメソッドが動作するためには、私はいくつかが必要ですEntityAccessServiceのインスタンスを取得する方法このインスタンスは、隔離された環境でテストするために模擬可能でなければなりません。それを行う正しい方法は何ですか?

私のテストは、できるだけシンプルで、奇妙なハックはありません。私が実装しようとしていることはかなり些細なようです。

私はそれを行うには、いくつかのオプションを考えています

  1. は、アプリケーションで作成されたすべてのエンティティによって使用されるentity_access_serviceを保持するクラスレベルの変数を持ちます。この場合、このフィールドはどこで初期化する必要がありますか?たとえば、

    class MyEntity 
        @@entity_access_service = nil 
    end 
    
    # Somewhere else (where?): 
    MyEntity.entity_access_service = MyEntityService.new(some_params_from_env) 
    

    このようにして、私のテストでは、最初にそれを初期化/模擬しなければならないでしょう。

  2. 1に似ていますが、クラスで初期化します。これは奇妙に見えます。特に、テストでENVパラメータがまったく入力されていないことがわかっている場合は、

  3. entity_serviceを設定するための追加のコンストラクタ/属性があります。 saveにはこのフィールドが初期化されていないため、これは機能しません。

  4. Repositoryクラスを作成します。これはかなりうまくいくでしょうが、Rubyの人々がそうでないようです。

+0

また、メソッド定義とメソッド呼び出しの両方で空のかっこを省略することができます。 – Stefan

+0

@Stefan Done :) – bezmax

+1

あなたはあなたが*すべき*というわけではありません。それは、snake_caseの強い好みよりはるかに多くの意見/個人的な選択です。 –

答えて

1

のActiveRecordの例に続き、あなたのクラス自体に、またはあなたの他のクラスが派生する基本クラスのメソッドを作成することができます。

ActiveRecordは、すべてのモデルがデータベースにアクセスするために使用する接続オブジェクトを返すメソッドActiveRecord::Base.connectionを提供します。あなたが似たような操作を行うことができます。

class MyEntity 
    .... 

    def self.entity_access_service 
     # return your service object 
    end 

    def self.find(id) 
     MyEntity.entity_access_service.get_data_for(id) 
     MyEntity.new(data) # Or whatever way to populate my entity 
    end 

    def save() 
     MyEntity.entity_access_service.save(@id, @loadedName, @loadedSurname) 
    end 
end 

は限り初期化が進むにつれて、アプリ内の初期化ステップを持っている必要がありますどちらか(とテストスイート)サービスの資格情報を設定ファイルから読み込まれ、あなたのMyEntityオブジェクトに渡され、 またはあなたentity_access_service方法を遅延、それは非常に一般的なRubyのイディオムを使用して最初のアクセスで返すオブジェクトを作成することができます:あなたは、クラスレベルのアクセサメソッドであなたのクラスレベルのインスタンス変数をすることができ包むことで、こと

def self.entity_access_service 
    @entity_access_service || = # build entity_access_service object 
end 

注意を0の使用を避けるはrecommended best practiceです。

+0

私はRspecを使用しているので、どこでこのサービスを模擬すべきですか?私はinstance-doubleを作成し、テストを実行する前にMyEntity.service_fieldに直接書き込むべきであることを訂正していますか?あるいは、私はこのフィールドの "セッター"として何らかの 'self.initialize_entity_service(service)'メソッドを作るべきですか?あるいは、私はMyEntityクラスの部分的な倍を作るべきですか? 'self.entity_access_service'メソッド全体を嘲笑していますか? – bezmax

+0

はい!instance_doubleに!あなたがダブルをどのように提供するかに対する答えは、あなたのapiをどのように見せたいかによって決まります。クラスが上記のアクセスサービス自体をインスタンス化する上記のメソッドに従うならば、アクセスサービスをインスタンス化し、インスタンスdoubleを返すどのような呼び出しでもモックすることができます。また、アクセスサービスの設定ツールを提供し、beforeブロックを使用してテストで値を設定することもできます。私はおそらく、クラスのインスタンス化のパスに頼るだろう。クラスセッターのパスはおそらくどこかのイニシャライザーの大きなセットを意味します。 – AndyV

関連する問題