Java:不変から不変への変換
-
07-07-2019 - |
質問
私が取り組んでいるフレームワークに構文糖衣を提供する方法を考えてきました。 Immitableオブジェクトのみを扱いたい。
不変オブジェクトがあり、それの修正バージョンを作成したいとします。あなたの見解では、単一の静的ファクトリメソッドを持つインスタンス化不可能なクラスはオブジェクト指向の原則を破るでしょうか?
文字列を使用した例:
public final class LOWERCASE { private LOWERCASE() {} public static String string( final String STRING ) { return STRING.toLowerCase(); } }
したがって、この例から次のように書くことができます:
String lowercaseString = LOWERCASE.string( targetString );
非常に読みやすいと思う
そのようなアプローチに反対する条件はありますか
解決
メソッドごとに1つのクラスを作成することはお勧めできません。代わりに、StringUtilsなどの名前の静的のみのメソッドクラスを作成し、メソッドを実装できます。このように呼び出す:
String lowerCaseString = StringUtils.lowercase(targetString);
これは、入力中にインテリセンスのヘルプも提供します。そうしないと、クラスのリストが大きくなりすぎます。この単純な例でも、CulutureInfoを考慮する必要がある状況にも対応できるように、複数の小文字クラスを実装する必要があります。
これがオブジェクト指向の原則を決して壊すとは思いませんし、それが悪い設計でもありません。他の言語、たとえばRubyでは、メソッドを直接Stringクラスに追加できます。 !で終わるメソッド元のオブジェクトが変更されたことを示します。他のすべてのメソッドは、変更されたコピーを返します。 Ruby on RailsフレームワークはStringクラスにいくつかのメソッドを追加し、これが良いテクニックであるかどうかについて議論があります。それは間違いなく便利です。
他のヒント
通常、不変オブジェクトでは、オブジェクトの修正バージョンを返すメソッドがあります。したがって、不変のコレクションがある場合は、sort()メソッドを使用して、ソートされた新しいコレクションを返すことができます。ただし、Stringクラスではタッチできないため、Stringの例ではこれは不可能です。
あなたのアプローチは非常に読みやすく、このようなエッジケースの場合、完全にうまくいくと思います。自分で記述する不変オブジェクトの場合、オブジェクト自体にメソッドがあります。
C#の不変オブジェクトに関するEric Lippertのシリーズはところで、かなり良い。
私の意見では、そのようなファクトリクラスの唯一のポイントは、クラスがさまざまな種類の不変オブジェクトを提供した場合です。
例:
public final class Lowercase {
private Lowercase() {}
public static String string( final String string ) {
return new String( string.toLowerCase() );
}
public static Foo foo( final Foo f ) {
boolean isLowerCase = true;
return new Foo(f, isLowerCase );
}
}
それ以外の場合は、StringクラスのtoLowerCase()のような不変クラス自体にメソッドを実装する必要があります。
いずれにしても、これはオブジェクト指向の原則を破るとは思わない。
エリックに同意します。不変オブジェクトには、静的メソッドではなく、オブジェクトの修正バージョンを返すメソッドが常に必要です。 JDKにも例があります:
- String.subString(int)
- BigDecimal.movePointLeft(int)
この方法で行うことには、変更するインスタンスをメソッドの引数として渡す必要がないという利点があります。 StringやIntegerなどのクラスの場合、ラッパークラスを使用することをお勧めします。次に、そのようなオブジェクトをいつ作成するかを制御できます(ラッパークラスのコンストラクターまたはクラスのメソッドの1つによって)。 Integerクラスを使用する場合、誰でもインスタンスを作成できるため、これを制御するのははるかに複雑です。
一方で、あなたは apache commons-langパッケージのStringUtils 。あなたはこのようなものを作りたかったと思うので、これを見てください。 (車輪を再発明しないでください)
新しいString()
はコードの匂いです- String
の不変性のため、ほとんど常には不要です>。 String
インスタンスに値が設定されると、そのインスタンスに異なる値が設定されることはありません。
次のメソッドでは、 new String()
は冗長です:
public static String string( final String string ) {
return new String( string.toLowerCase() );
}
toLowerCase()
は新しい(異なる) String
インスタンスを返します- new String()
はここで原因以外に有益なことは何もしません toLowerCase()
String
インスタンスの正確な値を持つ別のオブジェクト作成
概念を示す小さなGroovyスクリプトがあります(私は願っています-これはスクリプト言語のJavaです):
String a = 'CamelCase'
String b = a.toLowerCase()
println "a='${a}'"
println "b='${b}'"
制作
a='CamelCase'
b='camelcase'
a
は変わらなかったことに注意してください-これは不変です。 b
は新しい String
値です。
BigDecimal.movePointLeft()
または BigDecimal
上の BigDecimal
を返す他のメソッドについても同様です-それらは新しいインスタンスです、元のインスタンスを変更せずに残します。
OK、質問に答えましょう:
アプリケーションで有用な目的を実行する一連の Strings
の操作を保持するのは良い考えです。おそらくファクトリを使用することは、 String
のようなものには必要ありませんが、構築に多少の努力を要する別の不変のクラスには必要かもしれません。
String
のように基本クラスを拡張できない場合、@ kgiannakakisで説明されている staticメソッド
sクラスで問題ありません。
それ以外の場合、「不変」がクラスはアプリケーションの一部であり、クラスの宣言/定義にアクセスでき、 BigDecimal
、 String
などのモデルで新しいインスタンスを返すメソッドは好ましい。これは本質的に、@ Erik Hesselink、@ Bruno Conde、@ reallyinsaneが言ったことです。