テーマ。論理凝集をどのように解消するか。
これが最初の DRY ではない状況。
# $use_real_db optional. using mock DB for default.
sub test_object {
my ($expect, $use_real_db) = @_;
if ($use_real_db) {
my $object = new My::Object(real_db());
my $actual = $object->do_something($expect);
is($actual, $expect, 'test something');
} else {
my $object = new My::Object(mock_db());
my $actual = $object->do_something($expect);
is($actual, $expect, 'test something');
}
}
続いて、一見よさげだからやりがちだが、ダメな抽象化。
# $use_real_db optional. using mock DB for default.
sub test_object {
my ($expect, $use_real_db) = @_;
my $object = $use_real_db ? new My::Object(real_db())
: new My::Object(mock_db());
my $actual = $object->do_something($expect);
is($actual, $expect, 'test something');
}
ディスパッチャとして働くルーチンとその違いを受けて働く共通部分を分離した抽象化。
# $use_real_db optional. using mock DB for default.
sub test_object {
my ($expect, $use_real_db) = @_;
if ($use_real_db) {
test_object_impl(real_db());
} else {
test_object_impl(mock_db());
}
}
sub test_object_impl {
my $dbh = shift;
my $object = new My::Object($dbh);
my $actual = $object->do_something($text);
is($actual, $expect, 'test something');
}
サンプルコードなので、さほど問題はなさそうに見えたり、最後の例が過度に冗長に見えたりするかもしれない。
でも、これが区別できてない人は繰り返しのメンテナンスの結果、条件分岐のネストしたコードを書きやすい。問題は、$use_real_db による制御と混ぜてよいもの、いけないものが一見して分からない点にある。そのため、結果として本来混ぜるべきではない処理が手続き的に追加されることを許してしまいやすい。
小さいことだけど、ここに書かれていることの意味がわかる人が継続して書いたコードと、わかっていない人が継続して書いたコードの最終的なコードのできの違いは、とても大きい。