5

ここで、このばかばかしいほど醜いメソッドをクリーンアップしようとしています。これはリファクタリングを求めていますが、どのような構造がこれに最適なのかわかりません (つまり、case ステートメント、または単に慎重にフォーマットされたif thenステートメント) 。

一見すると、適切に配置されたいくつかwhenの 's を持つ case ステートメントの理想的な場所のように見えますが、私の理解では、case ステートメントは 2 つではなく 1 つの変数に対してのみ使用でき、irb をさまざまにいじることができます。ハッシュまたは配列を使用してこれらのステートメントを試すことは、ここでもあまり光を当てていません。

これをどのように行いますか?このような複数のブール値をチェックするときに、このようなコードを回避するための Ruby の一般的なトリックはありますか?

  def has_just_one_kind_of_thing?(item, controller)
    if (controller == 'foos' && item.widgets.blank?) || (controller == 'foos' && item.doohickeys.blank?) || (controller == 'bars' && item.widgets.blank?) || (controller == 'bars' && item.doohickeys.blank?) || (controller == 'bazes' && item.widgets.blank?) || (controller == 'bazes' && item.contraptions.blank?)
      return true
    else 
      return false
    end
  end
4

4 に答える 4

13

みんな-ここでは、1つのメソッド内でフロー制御をいじるだけでは解決できないポリモーフィズムが必要なようです。

拡張されたバージョンから始める私の試みは次のとおりです。

def has_just_one_kind_of_thing?(item, controller)
  (controller == 'foos' && item.widgets.blank?)    || 
  (controller == 'foos' && item.doohickeys.blank?) || 
  (controller == 'bars' && item.widgets.blank?)    || 
  (controller == 'bars' && item.doohickeys.blank?) || 
  (controller == 'bazes' && item.widgets.blank?)   || 
  (controller == 'bazes' && item.contraptions.blank?)
end

したがって、3 つの異なる動作があります - コントローラーごとに 1 つ.... 各コントローラーがコントローラー固有の意思決定を含むのに十分なほどスマートであり、コントローラーの名前だけでなく実際のコントローラーを渡す場合、メソッドを次のように減らします。

def has_just_one_kind_of_thing?(item, controller)
  controller.has_just_one_kind_of_thing?(item)
end

これには、各コントローラーが、コントローラーの種類に応じて関連するアイテムの処理を行う必要があります。それでは、foos、bars、bazes のそれぞれにメソッドを定義しましょう。has_just_one_kind_of_thing?

foo の例:

def has_just_one_kind_of_thing?(item)
   item.widgets.blank? || item.doohickeys.blank?
end

ベーズの例:

def has_just_one_kind_of_thing?(item)
   item.widgets.blank? || item.contraptions.blank?
end

戻りたいすべてのコントローラーでfalse、「定数メソッド」パターンを適用するだけです。

def has_just_one_kind_of_thing?(item)
  false
end

このコードはさらに高速に実行されます。これは、コントローラーの種類ほど多くのチェックを行う必要がないためです。コントローラーに対して 1 つのメソッド ディスパッチを行うだけです。

したがって、解釈されたルビーの方が高速です-また、jrubyまたはメソッドのディスパッチを大幅に最適化できる他のルビーの1つでさえ、はるかに高速になる可能性があります...

おそらくもっとスマートにすることもできますが、このメソッドがどのクラスに存在するか、および について他のいくつかのことを知る必要がありitemます。

代替のリファクタリングは、itemスマートにして、 の各タイプに異なるメソッドを持たせることでしitemた。繰り返しになりますが、どちらが最適かを判断するには、オブジェクト モデルについて詳しく知る必要があります...

それでも初切りです。

于 2009-11-23T02:26:21.503 に答える
10

もしかして、こういうこと?

def has_just_one_kind_of_thing?(item, controller)
    return case controller
      when 'foos', 'bars'
        item.widgets.blank? || item.doohickeys.blank?
      when 'bazes'
        item.widgets.blank? || item.contraptions.blank?
      else 
        false
    end
  end

外側の return は必要ないかもしれません (Ruby が何を必要としているのかは完全にはわかりません。私はまだかなり慣れていません)。

于 2009-11-23T01:27:42.427 に答える
4

まず、Ruby はデフォルトで常に最後のステートメントの値を返すため、if/else を取り除くことができます。

def has_just_one_kind_of_thing?(item, controller)
    (controller == 'foos' && item.widgets.blank?) || (controller == 'foos' && item.doohickeys.blank?) || (controller == 'bars' && item.widgets.blank?) || (controller == 'bars' && item.doohickeys.blank?) || (controller == 'bazes' && item.widgets.blank?) || (controller == 'bazes' && item.contraptions.blank?)
end

次に、読みやすくするために再フォーマットしましょう。

def has_just_one_kind_of_thing?(item, controller)
  (controller == 'foos' && item.widgets.blank?)    || 
  (controller == 'foos' && item.doohickeys.blank?) || 
  (controller == 'bars' && item.widgets.blank?)    || 
  (controller == 'bars' && item.doohickeys.blank?) || 
  (controller == 'bazes' && item.widgets.blank?)   || 
  (controller == 'bazes' && item.contraptions.blank?)
end

これで、アルゴリズムのパターンを簡単に確認できるようになりました。ここで 2 つの質問がされているようです: コントローラーは [foos|bars|bazes] のいずれかであり、widget または doohickeys は空白です。最初の質問を因数分解しましょう。

def has_just_one_kind_of_thing?(item, controller)
  %w[foos bars bazes].include?(controller)  &&
    (item.widgets.blank? || item.doohickeys.blank?)
end

これにより、メソッドが管理可能なサイズに縮小されます。しかし、メソッド名から、widget または doohickeys のいずれかに項目があり、両方ともどちらでもない場合を探していると推測できます。その場合は、XOR の方が適している可能性があります。

def has_just_one_kind_of_thing?(item, controller)
  %w[foos bars bazes].include?(controller)  &&
    (item.widgets.blank? ^ item.doohickeys.blank?)
end
于 2009-11-23T01:29:56.273 に答える
3

ド・モルガンのブール論理定理には 2 つのステートメントがあります。

1. (A and B) は not(notA or notB) と同等

not(A and B) は (notA or notB) と同等です

2. (A or B) は not(notA and notB) と同等

not(A または B) は (notA および notB) と同等です

..

  1. ある場合はサンドイッチを購入する (2 ドル未満でサーモンである) = ある場合はサンドイッチを購入しない (2 ドル以上またはサーモンではない)。

  2. (V がオンまたはアバターがオン) の場合はテレビを見る = (V がオフでアバターがオフ) の場合はテレビを見ない。

さらにブール代数、とりわけ、

  1. (A と B) または (A と C) = A と (B または C)

  2. (A または B) または C = A または B または C


元のロジック:

  (controller == 'foos' && item.widgets.blank?)    || 
  (controller == 'foos' && item.doohickeys.blank?) || 

  (controller == 'bars' && item.widgets.blank?)    || 
  (controller == 'bars' && item.doohickeys.blank?) || 

  (controller == 'bazes' && item.widgets.blank?)   || 
  (controller == 'bazes' && item.contraptions.blank?)


リダクション
(foos and widgets) or (foos and hickeys) = foos and (widgets or hickeys):

  (
    (controller == 'foos' &&
      (item.widgets.blank? || item.doohickeys.blank?)
    ) || 

    (controller == 'bars' &&
      (item.widgets.blank? || item.doohickeys.blank?)
    ) || 

    (controller == 'bazes' &&
      (item.widgets.blank? || item.contraptions.blank?)
    )
  )


リダクション
(foos and items) or (bars and items) = (foos or blocks) and items:

  (
    (controller == 'foos' || controller == 'bars') &&
    (item.widgets.blank? || item.doohickeys.blank?)
  ) ||

  (controller == 'bazes' &&
    (item.widgets.blank? || item.contraptions.blank?)
  )


ロジック削減により、元の 6 行から 3.5 行に削減されました。この演習では、de Morgan の単純なブール代数操作は使用しませんでしたが、de Morgan の操作は他の状況でも頻繁に適用できます。

たくさんの論理ステートメントを書くたびに、なぜ私はそのようなトラブルを経験しなければならないのでしょうか? 縮小されたロジックは元のロジックと似ておらず、適切な自己文書化コードではありません。

丁度!簡略化されたロジックにより、より単純な用語で見ることができます。元のロジックは、最初に必要だったより単純なロジックとは似ていません。

于 2009-11-23T06:19:22.997 に答える