@ Laive comment に関連して私が尋ねたこの前の質問をフォローアップしたかったのですが、別の質問をせずにそれを行うための優れた方法を考えることができなかったので、ここに行きます。
前の質問の文脈で、多くのユーザーがパラメーターを作成する方法を提案しました。多くのパラメーターを持つメソッドのオブジェクト。他の人は、プランにはおそらく多くの設定があってはいけないと指摘しました。
私はこの手法が正しく設計されていることを前提としており、メソッドには1や2だけでなく、適切な数のパラメータが必要であると判断されています。
オプションのパラメーターがある場合、パラメーターオブジェクトを作成するには、オブジェクトパラメーターに何らかの方法が必要です。オプションパラメータとしてのあるものが「ない」ことを宣言し、コードでその場合を処理する必要があります。
私はこのトピックについてブログなどを調査しましたが、「オプションの値が存在しない」というフラグとしてnullを受け入れるメソッドシグネチャを持つことは好ましくないようです。
「フィールドが設定されていません」という値を持つオプションのフィールドを持つparamオブジェクトと、「オプションのパラメーターが設定されていない」ことを表すためにnullが有効な値である明示的なパラメーターを持つメソッドの違いは何ですか。他ではない?
一般に、オプションの値を示すためにnull
を使用することはお勧めできません。戻り値、オブジェクトプロパティ、ローカル変数、その他のコンテキストのいずれであっても、それは悪い考えです。したがって、パラメータは一般的なルールの1つのケースにすぎません。
では、なぜそれが悪い考えなのでしょうか? 2つの理由:
null
が許可されているかどうかを型システムで示すことはできません。null
です。これは、2つの理由で参照がnullになる可能性があることを意味します。
null
ですコードは2つのケースを区別できないため、コードがバグと正当な値を区別できないため、バグが気付かれない場合があります。したがって、オプションの値を示す他のアプローチが推奨されます。
同じ問題があるため、null許容プロパティを持つパラメーターオブジェクトを使用することは、実際にはあまり良くありません。バグのために、または意図的にプロパティがnullであるかどうかがわかりません。したがって、これがより良いというあなたの前提に同意しません。
Nullを使用する場合の問題は、nullが有効な値であることを文書化し、オブジェクトがnullになる可能性があることを覚えておく必要があることです。また、メソッドを使用しているユーザーは、ドキュメントを常に参照して、そのパラメーターがnullであるかどうかを確認する必要があります。
代わりに、Parameters
オブジェクトがある場合、これらのオプションのフィールドはOptional<Foo> fooParam
として保存でき、そのパラメータークラスのユーザーは欠損値を明示的にチェックして処理する必要があります。
// this is just sketchy code to get the idea across
class Parameters {
// if this is specific to one method/class, you could initialize this to the default anyway.
private Optional<Foo> fooParam = Optional.ofNullable(null);
private final String required1;
private final String required2;
public Parameters(String required1, String required2) {
// you know what to do
}
public void setFooParam(Foo param) {
fooParam = Optional.ofNullable(param):
}
public Optional<Foo> getFooParam() {
return fooParam;
}
}
// somewhere else
public void myMethod(Parameters params) {
Foo foo = params.getFooParam().orElse(DEFAULT_FOO);
// ...
}
そのため、nullの明示的なチェックは行われず、コードのユーザーはmyMethod
のパラメーターが何であるかを気にする必要はありません。
オプションパラメータとしてのnullの主な問題は、コードが読みにくくなることです。たとえば、6つのパラメーターを持つメソッドがあり、最後の5つはオプションであるとします。
foo("example", null, null, 1, null null);
それを見ると、提供しているパラメーターと提供していないパラメーターを区別するのは、それほど簡単ではありません。何が起こっているのかを理解するには、メソッドの定義を数えて見てみる必要があります。しかし、実際には2つの問題があります。パラメータが多すぎると、おそらく大きなパラメータになります。同じタイプの複数の異なるパラメーター(varargsではない)をとる関数がある場合は常に、精神的に解析するのがより困難になります。
foo(1, 2, 3, 56, 77, 99);
foo("left", "right", "up", "down", "charm", "strange");
Nullはオブジェクト参照の有効な値であるため、区別可能なパラメーターを区別できないパラメーターに変換します。簡単な例で、この最後のポイントを実証できます。
次のような多くのパラメーターを持つ関数を想定します。
void drawRectangle(Point center, Float width, Float height, Color fill, Color stroke, Scale xScale, Scale yScale);
入力されたすべてのパラメーターの例は、次のようになります。
drawRectangle(point(3, 20), 30.0f, 20.0f, RED, BLACK, scale(0, 100, 0, 1000), scale(0, 100, 500, 0));
これは確かに醜いです。あまりにも多くのことを行っていますが、xパラメータがyより前にあり、その塗りつぶしがストロークより前にあることを覚えている場合は、幅パラメータが何であるかをすばやく確認できます。タイプPoint
のものが中心であり、次元がフロートなどであることを知っています。
ここで、すべてのパラメーターがオプションであり、nullがデフォルトを表すと仮定します。これはこのようなものには少し遠いですが、説明の目的で私と一緒にください:
drawRectangle(null, null, 20.0f, null, BLACK, null, null);
これを見ると、何が指定されているのかを理解するのに時間がかかります。 null
はすべてのオブジェクトタイプの有効な値であるため、タイプ情報はなくなりました。そこにフロートが見えますが、それはどの次元ですか?私は減速し、おそらく定義を引き上げる必要があります。