web-dev-qa-db-ja.com

「with(variable)」ブロックにforループ構文を使用すると、アンチパターンになりますか?

私はfor- loopsをいじり、delphiのwithキーワードを思い出して、次のパターン(IntelliJ IDEAでライブテンプレートとして定義されています)を思い付きました。

for ($TYPE$ $VAR$ = $VALUE$; $VAR$ != null; $VAR$ = null) {
    $END$
}

生産的なコードで使用する必要がありますか?これらの1文字の変数のような一時的なショートカット変数をラムダで作成したり、ループを処理したりするのに便利だと思います。さらに、ブロックで使用する変数が最初にnullかどうかをチェックします。 swingアプリケーションで以下のケースを検討してください:

// init `+1` button
for (JButton b = new JButton("+1"); b != null; add(b), b = null) {
    b.setForeground(Color.WHITE);
    b.setBackground(Color.BLACK);
    b.setBorder(BorderFactory.createRaisedBevelBorder());
    // ...
    b.addActionListener(e -> {
        switch (JOptionPane.showConfirmDialog(null, "Are you sure voting +1?")) {
            case JOptionPane.OK_OPTION:
                // ...
                break;
            default:
                // ...
                break;
        }
    });
}
6

これはアンチパターンではありません。これは、一般的に使用されている手法であり、何らかの問題があるためです。このコードは、「一般的に使用される」基準を満たしていません。

ただし、問題があります。ここにいくつかの問題があります:

  • 誤解を招く:ループ構造を使用していますが、2回以上実行されることはありません。
  • チェックを非表示にする:!= nullチェックは、配置されている場所を見逃しがちです。隠しコードは理解するのが難しいです。また、条件が実際に必要かどうか、または最初の反復後にループを終了するためにその条件だけが必要かどうかも明確ではありません。 (チェックに関するステートメントは、それが必要な状況があることを示していますが、例ではそうではないので、newは決してnullを返しません。)
  • アクションを非表示にする:add(b)ステートメントはさらによく非表示になります。それはあなたがそれを期待するであろう位置でさえ連続的ではありません。
  • 不要:ローカル変数を宣言するだけで済みます。その名前を表示したくない場合は、blockステートメントを使用してスコープを制限できますが、thatはアンチパターン(または少なくともコードのにおい)、関数を抽出する必要があることを示します。
33
Sebastian Redl

あなたがしていることは、コードを難読化することであり、それを明確にすることではありません。この「ループ」は一度しか実行されないことを理解するには、多くの努力が必要です。代わりに、ステートメントブロックを使用して変数のスコープを制限できます。

{
    JButton b = new JButton("+1");
    b.setForeground(Color.WHITE);
    ...
}

ラムダを使用している場合は、静的with関数を定義して、次のように使用することもできます。

with(new JButton("+1"), b -> {
    b.setForeground(Color.WHITE);
    ... 
});

これは少しわかりやすいかもしれませんが、同じエラーを発生させる単純なブロックと比較すると、新しいエラーが発生する可能性がないため、多くの間接参照です。

29
amon