

RubyでClassを作っているのですが、どちらのインターフェイスの方が良いのか悩んでいます。

パターンA

[code]

    class Hoge
      def piyo=(piyo_data)
        #do_something
        @piyo = piyo_data
      end
    
      def fuga=(fuga_data)
        #do_something
        @fuga = fuga_data
      end
    
      def send
        Api.call({
          piyo: @piyo,
          fuga: @fuga
        })
      end
    end
    
    # Usage
    hoge = Hoge.new
    hoge.piyo = piyo
    hoge.fuga = fuga
    hoge.send
    
[/code]

パターンB

[code]

    class Hoge
      def send(piyo_data, fuga_data)
        Api.call({
          piyo: piyo(piyo_data),
          fuga: fuga(fuga_data)
        })
      end
    
      private
      def piyo(piyo_data)
        #do_something
        piyo_data
      end
    
      def fuga(fuga_data)
        #do_something
        fuga_data
      end
    end
    
    # Usage
    hoge = Hoge.new
    hoge.send(piyo, fuga)
    
[/code]

パターンAは、いちいち各メソッドでデータをセットしていくのが使う側からすると面倒くさそうに見えます。パターンBは、引数が増えてきたときにわかりにくそうに見えます。


CC BY-SA 3.0
2016-10-31T05:36:53.807
30010
2018-07-22T03:59:05.013
2016-10-31T06:31:13.467
19380
19380
question
ruby
classのインターフェイスについて


パターンBはそもそもインスタンスを作る意味がないですね。  
クラスメソッドで良いです(sendを一度しかしないなら)。

[code]

    class Hoge
      def self.send(piyo_data, fuga_data)
        piyopiyo = do_something(piyo_data)
        fugaguga = do_something(fuga_data)
        Api.call(piyo: piyopiyo, fuga: fugaguga)
      end
    end
    
    Hoge.send('foo', 'bar')
    
[/code]

`piyo_data`, `fuga_data` を `send` 以外でも使う場合はインスタンスを生成する必要があります。  
その場合はいちいち代入するよりも `initialize` でやったほうが良いでしょう。

[code]

    class Hoge
      def initialize(piyo_data, fuga_data)
        @piyo_data, @fuga_data = piyo_data, fuga_data
      end
    
      def send
        piyopiyo = do_something(piyo_data)
        fugaguga = do_something(fuga_data)
        Api.call(piyo: piyopiyo, fuga: fugaguga)
      end
    
      def inspect
        "piyo_data: #{@piyo_data}, fuga_data: #{@fuga_data}"
      end
    end
    
    hoge = Hoge.new('foo', 'bar')
    hoge.send
    hoge.inspect
    
[/code]

まぁ、何が「良い」のかは場合によるので、このような抽象的なコードの場合には一概には言えないと思います。

追記)

簡単に「場合による」と書いてしまいましたが、どのような場合によるのかほんの一例を上げておきます

  * sendにはどれくらいのコストがかかるのか(処理時間等)
  * sendはどれくらいの頻度で実行されることを想定しているのか 
    * 1日に数回?
    * 1秒間に数千回?
  * do_somethingは重い処理か比較的軽い処理か(1秒間に数千回実行してもそれほど問題にならない?)
  * do_something後 `piyo_data`, `fuga_data` の値は変更されるのか、されないのか 
    * 変更される場合、もとの値を保持しておく必要があるか
  * do_somethingはsendの直前に実行する必要があるか(実行時刻やsend先の状態によって結果がかるのか)、それとも `piyo_data`, `fuga_data` が与えられたらすぐに実行してよいのか
  * etc.

いろいろ書いてしまいましたが、もし、個人や少人数のチームで開発している場合には、とりあえず自分が「良い」と思う形で実装してしてしまって、問題が起きたらリファクタリング(インターフェースが変わるので正確にはリファクタリングではないけど)すればいいと思います。

最初にすべての条件を想定したものを作ってしまうと、結局使わない機能ができてしまったりリファクタリングが困難になってしまうことがよくあります。  
案ずるより産むが易し！  
とりあえず、最低限のものを作って徐々に改善していくのが個人的には良いと思います。

ただ、publicに公開するライブラリを作っている、大規模なプロジェクトでインターフェースを気軽に変えられない、などの場合は話は変わってきますが。


CC BY-SA 4.0
2018-07-13T16:58:00.467
45591
2018-07-22T03:59:05.013
2018-07-22T03:59:05.013
28630
28630
30010
answer


ライブラリとして提供するのであれば、あらゆる手段を用意するというのがRubyの文化に近いと思います。

[code]

    class Hoge
      def initialize(piyo_data = nil, fuga_data = nil)
        piyo = piyo_data if piyo_data
        fuga = fuga_data if fuga_data
      end
    
      def piyo=(piyo_data)
        @piyo = piyo(piyo_data)
      end
    
      def fuga=(fuga_data)
        @fuga = fuga(fuga_data)
      end
    
      def send(piyo_data = nil, fuga_data = nil)
        Api.call({
          piyo: (piyo_data ? piyo(piyo_data) : @piyo),
          fuga: (fuga_data ? fuga(fuga_data) : @fuga)
        })
      end
    
      private
      def piyo(piyo_data)
        #do_something
        piyo_data
      end
    
      def fuga(fuga_data)
        #do_something
        fuga_data
      end
    end
    
    # Usage
    # 初期化時に設定
    hoge = Hoge.new(piyo, hoge)
    hoge.send
    # 後から設定
    hoge.piyo = piyo
    hoge.fuga = fuga
    hoge.send
    # 呼び出し時に設定
    hoge.send(piyo, fuga)
    
[/code]


CC BY-SA 4.0
2018-07-13T19:59:47.890
45594
2018-07-13T19:59:47.890
7347
30010
answer