1

これは正しいですか?

コード例:

void foo(E param){

    if(param == null){
        // do this
    }else{
        // do that
    }
//...
}

まず最初に、変数を null にできるようにすることについて議論している人々がソフトウェアに複雑さを追加するいくつかの投稿を読みまし

想い(私の想い)

初心者の私の考えは間違っているかもしれませんが、これを解決するのを手伝ってもらいたいです。

変数が nullであるかどうかを確認することは、その変数が であるコンテキストに応じて正しいと思います。例を挙げて説明しましょう。

悪い使い方:

私たちのプロジェクトには Command パターンが実装されており、一連の命令があります。次に、コンソールに命令を書き込み、それを解釈する必要があります。それを解析しようとするインタープリタークラスがあるため、インタープリターが適切な命令を見つけることができない場合、null を返します。

コード例:

public class Interpreter {

    public static final String LINE_SEPARATOR = System.getProperty("line.separator");
    private static final List<Instruction> listInstructions = loadInstructions();


    /**
     * Generates a new instruction according to the user input
     * @param line - A string with the user input
     * @return The instruction read from the given line. If the instruction is not correct, then it returns null.
     */
    public static Instruction generateInstruction(String line) throws WrongInstructionFormatException
    {
        Instruction instru = null;
        Iterator<Instruction> it = listInstructions.iterator();
        int i = 0;
        while(it.hasNext() && i <9 && instru == null)
        {
            try
            {
                instru = it.next().parse(line);
            }
            catch(WrongInstructionFormatException e)
            {

            }
            finally
            {
                i++;
            }
        }
        return instru;
     }
//...




public class MainClass
{
    /**
     * MainFunction
     */
        public void mainFuction()
        {

            Instruction instru;
            while(true)
            {
                instru = Interpreter.generateInstruction(orden);
                if(instru == null) <------
                    // Error.
                else
                    instru.execute();

            }

        }
  }

この場合、インタープリターは null を返してはならず、例外を返してはならないため、これを間違って実装していると思います私が明確であることを願っています。そうでない場合は、後でコメントで明確にしたいと思います

正しい使い方: (編集済み)

私たちのプロジェクトのメイン クラスには、両方をインスタンス化できる、またはインスタンス化できない一連の属性があります。1 つをインスタンス化したか、両方をインスタンス化したか、どちらもインスタンス化しなかったかに応じて、1 つ、2 つ、またはまったくアクションを実行しない一連の関数が存在します。

public class MainClass extends Observable
{
    private A a;
    private B b; // This attribute allows you to start making a log in a txt file
    private C c; // This attribute allows you to start making a log in a DB

    public MainClass(A a){ //....}
    public MainClass(A a, B b) { // .....}
    public MainClass(A a, C c) { // .....}
    public MainClass(A a, B b, C c){ //.....}


    public void log(String update)
    {
        if(this.b != null) <-----
            // Save the update in the txt file
        if(this.c != null) <-----
            // Save the update in a db row
    }
}

さて、なぜこれが正しいのですか?前のケースとは異なり、属性に null 値が含まれている可能性があるため、正しいと思います。


これで議論が始まります。他の人が同じ疑問を持っている場合は、それが助けになることを願っています.

4

3 に答える 3

3

null に関するアドバイスは、null をチェックするのではなく、生成したり許可したりしないことです。明らかに、プログラムに null がある場合は、それらをチェックする必要があります。

2 番目の例では、アドバイスに従う方法は、null 許容フィールドを持たないようにクラスを実装する方法を見つけることです。クラスにオプションの属性があるのはなぜですか? これらの属性で何をしますか? オプションの属性を持たないようにクラスを構造化する方法はありますか (おそらく複数のクラスに分割することによって)。

if2 番目の例では、テストで属性の無効性を使用しています。オブジェクト指向設計で行う標準的なことは、条件をポリモーフィズムに置き換えることです。それを試していただけますか?

編集:さて、ポリモーフィズムを使用して2番目のケースを解決する方法を次に示します。aこの場合は関係ないので、フィールドを削除しました。

public class MainClass extends Observable {
    private final Collection<? extends LogDestination> logDestinations;

    public MainClass() {
        logDestinations = Collections.emptySet();
    }

    public MainClass(B b) {
        logDestinations = Collections.singleton(new TextFileLog(b));
    }

    public MainClass(C c) {
        logDestinations = Collections.singleton(new DatabaseLog(c));
    }

    public MainClass(B b, C c) {
        logDestinations = Arrays.asList(new TextFileLog(b), new DatabaseLog(c));
    }

    public void log(String update) {
        for (LogDestination logDestination : logDestinations) {
            logDestination.log(update);
        }
    }
}

interface LogDestination {
    public void log(String update);
}

class TextFileLog implements LogDestination {
    private final B b;

    public TextFileLog(B b) {
        this.b = b;
    }

    @Override
    public void log(String update) {
        // Save the update in the txt file
    }
}

class DatabaseLog implements LogDestination {
    private final C c;

    public DatabaseLog(C c) {
        this.c = c;
    }

    @Override
    public void log(String update) {
        // Save the update in a db row
    }
}

抽象的に言えば、これが行っていることは、作成する を選択するという形で、ロジック内の唯一の選択肢をコンストラクターにプッシュするLogDestinationことです。それらが作成されると、ポリモーフィズムが一般的な呼び出しを特定のメソッドにディスパッチすることで、完全に均一に扱うことができます。オブジェクトを設定するさまざまな方法に対してさまざまなコンストラクターがあるため、条件付きロジックはまったく必要ありません。

于 2013-09-24T14:47:43.503 に答える
2

予想される状況や回復可能な状況の場合、Exceptionvs リターンをスローすることnullは、ほぼ完全にスタイルの問題です。あなたが提示した議論は区別するのに適したものであり、あなたにとって理にかなっているなら、私はそれを支持します.

もちろん、例外をフロー制御ツールとして使用する必要があると言っているわけではありませんが、説明しているような状況では、完全に合理的に聞こえます。

于 2013-09-24T14:47:26.157 に答える
1

私は2000以上のクラスを持つプログラムを持っています。クラスあたり約 20 個の関数を想定すると、40,000 個の関数になります。それらの多くは、それぞれ数行のコードしかありません (例: get/set 関数)。各関数が null 引数などをチェックする場合、検証だけに多くのコードが費やされます (処理される多くの例外に対して同様の引数を作成できます)。また、null のみをチェックする場合は、(javadoc) 契約が満たされていることを確認するために完全な検証を行うこともできます。たとえば、関数に渡されるオブジェクトのリストを繰り返し処理する、null のチェック、範囲の問題などです。このアプローチにはパフォーマンスの問題があることに注意してください。

代わりに、javadoc (契約による設計) で指定されていない限り、関数の引数に null を使用したり、null の戻り値を使用したりすることは許可されないという、プロジェクト全体に適用される合意されたポリシーを持つことを好みます (契約による設計)。(javadoc) コントラクトに従い、関数を呼び出す前に検証チェックを行うのは呼び出し元の責任です。ただし、複数の引数を取るコンストラクター (不完全なオブジェクトが作成されないようにするため) と、入力引数の処理方法が非常に複雑ないくつかの関数の検証チェックを検討します。関数の直接の呼び出し元は、関数を呼び出す直前に検証チェックを行う必要がないことに注意してください。代わりに、呼び出しチェーンに沿った他の関数の少なくとも 1 つがそれを行う必要があります。ノート:

于 2013-09-24T18:51:28.387 に答える