30

それで、Java の学習を始めたばかりで、「参照渡し」などというものがないことを発見しました。アプリケーションを C# から Java に移植していますが、元のアプリケーションには "ref" または "out" パラメーターである int と double があります。

最初は「Integer」か「Double」を渡せると思っていたのですが、それは Reference 型なので値が変わってしまいます。しかしその後、それらの参照型は不変であることを知りました。

そこで、「MutableInteger」クラスと「MutableDouble」クラスを作成し、それらを関数に渡しました。それは機能しますが、言語の当初の設計意図に反しているに違いないと思います。

「参照渡し」は一般的に悪い設計ですか? 考え方をどう変えたらいいですか?

次のような関数を持つことは合理的と思われます。

bool MyClass::changeMyAandB(ref int a, ref int b)
{
    // perform some computation on a and b here

    if (success)
        return true;
    else return false;
}

それってデザインが悪いの?

4

8 に答える 8

16

適切にサポートされている言語での設計は悪くありませんが (*)、MutableInt2 つのメソッド間で通信するためだけにクラスを定義する必要がある場合、何かが間違っていることは確かです。

投稿した例の解決策は、2 つの int の配列を返し、nullリターンまたは例外によって失敗を通知することです。これは常にうまくいくとは限らないので、時にはあなたがしなければならない...

  • 現在のオブジェクトに属性を設定します (クラス内の 2 つのメソッドが通信する場合)。
  • メソッドが変更する可能性のある整数の配列を渡します (複数回渡される整数が多数ある場合)。
  • たとえば、計算の結果をカプセル化するヘルパー クラスを作成し(2 つの int の代わりに anと aResultを扱っている場合)、メソッドまたはコンストラクターとして実際の計算を行う場合があります。intfloat
  • あなたが提案したイディオムを使用しますが、Apache Commonsまたは別の優れたライブラリでサポートを使用することを検討してください。

(*) 言語設計が悪いだけです。Python や Go では、複数の値を返して心配する必要はありません。

于 2013-02-28T17:37:53.237 に答える
16

オブジェクト指向プログラミングは、コードを明確で理解しやすい抽象化に構造化する場合に最適です。

抽象化としての数値は不変であり、同一性を持ちません(つまり、「5」は常に「5」であり、「5 の複数のインスタンス」などはありません)。

あなたが発明しようとしているのは、可変でアイデンティティを持つ「可変数」です。この概念は少し扱いに​​くいので、それよりも意味のある抽象化(オブジェクト)を使用して問題をモデル化した方がよいでしょう。

個々の値の塊ではなく、何かを表し、特定のインターフェイスを持つオブジェクトで考えてください。

于 2013-02-28T17:46:38.753 に答える
6

参照によって値オブジェクトを渡すことは、一般的に悪い設計です。

高パフォーマンスの並べ替え操作のための配列位置の交換など、有効な特定のシナリオがあります。

この機能が必要な理由はほとんどありません。C# では、通常、OUT キーワードを使用すること自体が欠点です。のような out キーワードのいくつかの許容可能なDateTime.TryParse(datetext, out DateValue)使用法がありますが、 out パラメータの標準的な使用法は、すべてのステータスにフラグを使用するという悪い習慣を一般的にエミュレートしたいソフトウェア設計です。

于 2013-02-28T17:38:26.210 に答える
3

「参照渡し」は悪い設計ですか?

一般的ではありません。特定のシナリオを理解し、関数が何をするのか自問する必要があります。また、特に他の人のためにコーディングしている (ライブラリを配布している) 場合は、コーディング スタイルを適切に定義する必要があります。

関数が複数の出力を返す場合、参照渡しは通常有効です。ResultContainer関数が返すすべての情報を含むオブジェクトを返すことをお勧めします。次の C# の例を見てください。

bool isTokenValid(string token, out string username)

VS

AuthenticationResult isTokenValid(string token)

class AuthenticationResult {
    public bool AuthenticationResult;
    public string Username;
}

違いは、参照 (この場合outは put) パラメーターを持つメソッドは、トークンの検証またはオプションでユーザー情報を抽出するためにのみ使用できることを明確に強調していることです。したがって、パラメーターを渡す義務がある場合でも、必要がなければ破棄できます。2 番目の例は、より詳細なコードです。

もちろん、そのような方法がある場合は、2番目のデザインが望ましいです

bool doSomething(object a, ref object b, ref object c, ref object d, ... ref object z);

それらすべてをコンテナにラップするからです。

ここで明確にしましょう。Java と C# では、非プリミティブ型は常に cloned reference として渡されます。つまり、objects 自体は複製されませんが、それらへの参照のみがスタックに複製されるため、返された後にまったく異なるオブジェクトを指すことは期待できません。代わりに、オブジェクトの状態がメソッドによって変更されることを常に期待しています。それ以外の場合はclone()、オブジェクトと出来上がりです。

ここにトリックがあります:MutableIntegerまたはより良いHolderパターンは、参照によってプリミティブ値を渡すためのソリューションです。

現在idl2java、IDL に参照パラメータがある場合、CORBA コンパイラで使用されています。

あなたの特定のケースでは、あなたが示した方法があまりにも一般的であるため、デザインの良し悪しについてはお答えできません。考えてみてください。入力として、暗号化のように、マルチメディア情報に何らかの後処理関数を適用する場合は、参照渡しを使用します。私には、以下は良いデザインに見えます

encrypt(x);

VS

x = encrypt(x);
于 2013-02-28T17:49:08.090 に答える
1

呼び出し元スタックの変数を変更するメソッドは、かなり混乱する可能性があります。

理想的には、言語はこの種の問題を解決する複数の値を返すことをサポートする必要があります。

ただし、その前に、「out」パラメーターを使用する必要がある場合は、使用する必要があります。

于 2013-02-28T17:42:23.360 に答える
1

あなたが関与している悪い設計は、MutableIntegerクラスを使用することです。2 は常に 2 です。明日は 2 です。

Java/OO の標準的なパターンは、通常、そのようなアイテムをクラスのインスタンスにして、そのクラスに操作/管理させることです。

次はAtomicIntegerです。繰り返しますが、それを渡す必要がある状況は一度もありませんでしたが、多くのコードをリファクタリングしたくない場合 (あなたの質問は「良い習慣」だったので、私はあなたに苦労しなければなりませんでした)。オプション。その理由は、カプセル化の観点から、整数を別の関数にエスケープさせている場合、他の関数が同じスレッドで実行されるかどうかわからないためです。 そのような並行性が問題になっているため、アトミック参照クラスによって提供される並行性が必要になるでしょう。( も参照してくださいAtomicReference。)

于 2013-02-28T17:38:30.003 に答える
1

もちろん、これは処理している特定の問題によって異なりますが、ほとんどの場合、そのような関数が必要な場合、設計はあまりオブジェクト指向ではありません。
何を達成しようとしていますか?この番号 a と b が一緒に動作する必要がある場合、それらはクラス MyClass に属している可能性があり、必要なのはインスタンス メソッドです。何かのようなもの:

class MyClass{
     private int a;
     private int b;
     //getters/setters/constructors
     public boolean dothings(){
     // perform some computation on a and b here
        if (success)
             return true;
        else return false;
        //why not just return success?
     } 

}
于 2013-02-28T17:45:51.900 に答える
1

「参照渡し」は一般的に悪い設計ですか? 考え方をどう変えたらいいですか?値を保持するために POJO Bean を作成し、その Bean を関数に送信して新しい値を取得する必要はありません。もちろん、場合によっては、関数を呼び出すだけで値を返すことができます(常に1つだけを返したいが、out varsについて話している場合は、複数だと思います)。

伝統的に、例を変更する必要があるプロパティを持つ Bean を作成します。

class MyProps{
int val1;
int val2;//similarly can have strings etc here
public int getVal1(){
return val1;
}
public void setVal1(int p){ 
val1 = p;
}// and so on other getters & setters

}

または、任意のオブジェクトを保持するジェネリックを持つクラスを作成できます

class TVal<E>{
E val;
public E getValue(){
return val;
}
public void setValue(E p){
val = p;
}
}

クラスを使用して、コンテナーの参照渡しを行います。

public class UseIt{
    void a (){
      TVal<Integer> v1 = new TVal<Integer>();
      v1.setValue(1);//auto boxed to Integer from int
      TVal<Integer> v2 = new TVal<Integer>();
      v2.setValue(3);
      process(v1,v2);
      System.out.println("v1 " + v1 + " v2 " + v2);
    }

    void process(TVal<Integer> v,TVal<Integer> cc){
        v.setValue(v.getValue() +1);
        cc.setValue(cc.getValue() * 2);
    }
}
于 2013-02-28T18:19:58.073 に答える