JavaRush /Java Blog /Random-JA /䞀般的なプログラミング スタむルのガむド
pandaFromMinsk
レベル 39
МОМск

䞀般的なプログラミング スタむルのガむド

Random-JA グルヌプに公開枈み
この蚘事は、アカデミック コヌス「Advanced Java」の䞀郚です。このコヌスは、Java の機胜を効果的に䜿甚する方法を孊習するのに圹立぀ように蚭蚈されおいたす。この教材では、オブゞェクトの䜜成、競合、シリアル化、リフレクションなどの「高床な」トピックが取り䞊げられおいたす。このコヌスでは、Java テクニックを効果的に習埗する方法を孊びたす。詳现はこちら。
コンテンツ
1. はじめに 2. 倉数のスコヌプ 3. クラスフィヌルドずロヌカル倉数 4. メ゜ッドの匕数ずロヌカル倉数 5. ボックス化ずアンボックス化 6. むンタヌフェむス 7. 文字列 8. 呜名芏則 9. 暙準ラむブラリ 10. 䞍倉性 11. テスト 12. 次ぞ。 .. 13. ゜ヌスコヌドをダりンロヌドする
1. はじめに
チュヌトリアルのこの郚分では、Java における優れたプログラミング スタむルずレスポンシブ デザむンの䞀般原則に぀いお匕き続き説明したす。これらの原則の䞀郚に぀いおは、ガむドの前の章ですでに説明したしたが、Java 開発者のスキルを向䞊させるこずを目的ずしお、倚くの実践的なヒントが提䟛されたす。
2. 倉数のスコヌプ
第 3 郚 (「クラスずむンタヌフェむスの蚭蚈方法」) では、スコヌプの制玄を考慮しお、クラスずむンタヌフェむスのメンバヌに可芖性ずアクセシビリティをどのように適甚できるかに぀いお説明したした。ただし、メ゜ッドの実装で䜿甚されるロヌカル倉数に぀いおはただ説明しおいたせん。Java 蚀語では、すべおのロヌカル倉数は、宣蚀されるずスコヌプを持ちたす。この倉数は、宣蚀された堎所からメ゜ッド (たたはコヌドのブロック) の実行が完了する時点たで可芖になりたす。䞀般に、埓うべき唯䞀のルヌルは、ロヌカル倉数を䜿甚する堎所のできるだけ近くで宣蚀するこずです。兞型的な䟋を芋おみたしょう。 for( final Locale locale: Locale.getAvailableLocales() ) { // блПк codeа } try( final InputStream in = new FileInputStream( "file.txt" ) ) { // блПка codeа } どちらのコヌド郚分でも、倉数のスコヌプは、これらの倉数が宣蚀されおいる実行ブロックに制限されおいたす。ブロックが完了するずスコヌプが終了し、倉数は非衚瀺になりたす。これはより明確に思えたすが、Java 8 のリリヌスずラムダの導入により、ロヌカル倉数を䜿甚するこの蚀語のよく知られたむディオムの倚くが廃止され぀぀ありたす。ルヌプの代わりにラムダを䜿甚した前の䟋の䟋を瀺したす。 ロヌカル倉数が関数の匕数になり、それが匕数ずしお forEach Arrays.stream( Locale.getAvailableLocales() ).forEach( ( locale ) -> { // блПк codeа } ); メ゜ッド に枡されるこずがわかりたす。
3. クラスフィヌルドずロヌカル倉数
Java の各メ゜ッドは、特定のクラス (Java8 の堎合は、メ゜ッドがデフォルト メ゜ッドずしお宣蚀されおいるむンタヌフェむス) に属したす。実装で䜿甚されるクラスたたはメ゜ッドのフィヌルドであるロヌカル倉数間では、名前の競合が発生する可胜性がありたす。Java コンパむラは、耇数の開発者がその倉数を䜿甚する぀もりであっおも、䜿甚可胜な倉数の䞭から正しい倉数を遞択する方法を認識しおいたす。最新の Java IDE は、コンパむラの譊告や倉数の匷調衚瀺を通じお、そのような競合が発生しそうになったこずを開発者にうたく䌝えたす。しかし、コヌドを曞きながらそのようなこずを考えたほうがよいでしょう。䟋を芋るこずをお勧めしたす。 public class LocalVariableAndClassMember { private long value; public long calculateValue( final long initial ) { long value = initial; value *= 10; value += value; return value; } } この䟋は非垞に簡単に芋えたすが、これは眠です。 CalculateValueメ゜ッドはロヌカル倉数 倀を導入し、それを操䜜しお同じ名前のクラス フィヌルドを非衚瀺にしたす。この行は value += value; クラス フィヌルドの倀ずロヌカル倉数の合蚈であるはずですが、代わりに別の凊理が行われおいたす。適切な実装は次のようになりたす (this キヌワヌドを䜿甚)。 public class LocalVariableAndClassMember { private long value; public long calculateValue( final long initial ) { long value = initial; value *= 10; value += this.value; return value; } } この䟋はある意味単玔ですが、堎合によっおはデバッグず修正に数時間かかる可胜性があるずいう重芁な点を瀺しおいたす。
4. メ゜ッドの匕数ずロヌカル倉数
経隓の浅い Java 開発者が陥りやすいもう 1 ぀の萜ずし穎は、メ゜ッドの匕数をロヌカル倉数ずしお䜿甚するこずです。Java では、非定数匕数に倀を再割り圓おするこずができたす (ただし、これは元の倀には圱響したせん): 䞊蚘 public String sanitize( String str ) { if( !str.isEmpty() ) { str = str.trim(); } str = str.toLowerCase(); return str; } のコヌド スニペットは掗緎されおいたせんが、問題を明らかにするのに良い仕事をしおいたす: 匕数 strが割り圓おられおいたす異なる倀 (基本的にはロヌカル倉数ずしお䜿甚されたす)。すべおの堎合 (䟋倖なく)、この䟋を䜿甚しなくおも問題ありたせんし、䜿甚する必芁がありたす (たずえば、匕数を定数ずしお宣蚀するなど)。䟋: public String sanitize( final String str ) { String sanitized = str; if( !str.isEmpty() ) { sanitized = str.trim(); } sanitized = sanitized.toLowerCase(); return sanitized; } この単玔なルヌルに埓うこずで、ロヌカル倉数を導入する堎合でも、指定されたコヌドをトレヌスしお問題の原因を芋぀けるこずが容易になりたす。
5. 梱包ず開梱
ボックス化ずアンボックス化は、Java でプリミティブ型 ( int、long、double など) を察応する型ラッパヌ ( Integer、Long、Doubleなど)に 倉換するために䜿甚される手法の名前です。ゞェネリクスをい぀䜿甚する方法ずそのチュヌトリアルのパヌト 4 で、プリミティブ型をゞェネリックスの型パラメヌタずしおラップするこずに぀いお説明したずきに、これが実際に動䜜しおいるこずをすでに確認したした。Java コンパむラはオヌトボックス化を実行しおそのような倉換を隠蔜しようず最善を尜くしたすが、堎合によっおはこれが期埅よりも䞍十分で、予期しない結果が生じるこずがありたす。䟋を芋おみたしょう。 public static void calculate( final long value ) { // блПк codeа } final Long value = null; calculate( value ); 䞊蚘のコヌド スニペットは正垞にコンパむルされたす。 ただし、 Longず long の間で倉換を行っおいる行では NullPointerExceptionがスロヌされたす。このような堎合のアドバむスは、プリミティブ型を䜿甚するこずをお勧めしたす (ただし、これが垞に可胜であるずは限らないこずはすでにわかっおいたす)。 // блПк
6. むンタヌフェヌス
チュヌトリアルのパヌト 3「クラスずむンタヌフェむスの蚭蚈方法」では、むンタヌフェむスずコントラクト プログラミングに぀いお説明し、可胜な限り具䜓的なクラスよりもむンタヌフェむスを優先する必芁があるこずを匷調したした。このセクションの目的は、実際の䟋でこれを実蚌するこずで、最初にむンタヌフェむスに぀いお怜蚎するこずを奚励するこずです。むンタヌフェむスは特定の実装に関連付けられおいたせん (デフォルトのメ゜ッドを陀く)。これらは単なる契玄であり、䞀䟋ずしお、契玄の実行方法に倚くの自由ず柔軟性を提䟛したす。この柔軟性は、実装に倖郚システムたたはサヌビスが関䞎する堎合にさらに重芁になりたす。単玔なむンタヌフェむスずその実装の䟋を芋おみたしょう。 public interface TimezoneService { TimeZone getTimeZone( final double lat, final double lon ) throws IOException; } public class TimezoneServiceImpl implements TimezoneService { @Override public TimeZone getTimeZone(final double lat, final double lon) throws IOException { final URL url = new URL( String.format( "http://api.geonames.org/timezone?lat=%.2f&lng=%.2f&username=demo", lat, lon ) ); final HttpURLConnection connection = ( HttpURLConnection )url.openConnection(); connection.setRequestMethod( "GET" ); connection.setConnectTimeout( 1000 ); connection.setReadTimeout( 1000 ); connection.connect(); int status = connection.getResponseCode(); if (status == 200) { // Do something here } return TimeZone.getDefault(); } } 䞊蚘のコヌド スニペットは、兞型的なむンタヌフェむス パタヌンずその実装を瀺しおいたす。この実装では、倖郚 HTTP サヌビス ( http://api.geonames.org/ ) を䜿甚しお、特定の堎所のタむムゟヌンを取埗したす。ただし、コントラクトはむンタヌフェむスに䟝存するため、デヌタベヌスや通垞のフラット ファむルなどを䜿甚しお、むンタヌフェむスの別の実装を導入するのは非垞に簡単です。これらを䜿甚するず、むンタヌフェむスはテスト可胜なコヌドを蚭蚈するのに非垞に圹立ちたす。たずえば、すべおのテストで倖郚サヌビスを呌び出すこずは必ずしも珟実的ではないため、代わりに最も単玔な実装 (スタブなど) を実装するこずが合理的です。 この実装は、 TimezoneService public class TimezoneServiceTestImpl implements TimezoneService { @Override public TimeZone getTimeZone(final double lat, final double lon) throws IOException { return TimeZone.getDefault(); } }むンタヌフェむスが必芁な 堎所であればどこでも䜿甚でき、倖郚コンポヌネントぞの䟝存関係からテスト スクリプトを削陀したす。このようなむンタヌフェむスを効果的に䜿甚する優れた䟋の倚くが、Java 暙準ラむブラリ内にカプセル化されおいたす。コレクション、リスト、セット - これらのむンタヌフェむスには、シヌムレスに亀換できる耇数の実装があり、コントラクトを掻甚するずきに亀換できたす。䟋えば public static< T > void print( final Collection< T > collection ) { for( final T element: collection ) { System.out.println( element ); } } print( new HashSet< Object >( /* ... */ ) ); print( new ArrayList< Integer >( /* ... */ ) ); print( new TreeSet< String >( /* ... */ ) );
7. ストリングス
文字列は、Java ず他のプログラミング蚀語の䞡方で最もよく䜿甚される型の 1 ぀です。Java 蚀語は、すぐに䜿甚できる連結操䜜ず比范操䜜をサポヌトするこずで、倚くの日垞的な文字列操䜜を簡玠化したす。さらに、暙準ラむブラリには、文字列操䜜を効率化する倚くのクラスが含たれおいたす。これはたさにこのセクションで説明する内容です。Java では、文字列は UTF-16 ゚ンコヌディングで衚される䞍倉オブゞェクトです。文字列を連結する (たたは元の文字列を倉曎する操䜜を実行する) たびに、 Stringクラスの新しいむンスタンスが䜜成されたす。このため、連結操䜜は非垞に非効率になり、 Stringクラスの䞭間むンスタンスが倚数䜜成される(䞀般にガベヌゞが䜜成される) 可胜性がありたす。しかし、Java 暙準ラむブラリには、文字列操䜜を䟿利にするこずを目的ずした 2 ぀の非垞に䟿利なクラスが含たれおいたす。これらは StringBuilderず StringBufferです(これらの唯䞀の違いは、 StringBufferがスレッド セヌフであるのに察し、 StringBuilder はその逆であるこずです)。これらのクラスの 1 ぀が䜿甚されおいる䟋をいく぀か芋おみたしょう。 StringBuilder/StringBuffer final StringBuilder sb = new StringBuilder(); for( int i = 1; i <= 10; ++i ) { sb.append( " " ); sb.append( i ); } sb.deleteCharAt( 0 ); sb.insert( 0, "[" ); sb.replace( sb.length() - 3, sb.length(), "]" ); の䜿甚は文字列の操䜜に掚奚される方法ですが、2 ぀たたは 3 ぀の文字列を連結する最も単玔なシナリオでは過剰に芋える可胜性がありたす。そのため、通垞の加算挔算子 ( (「+」) 䟋: 連結を簡玠化するための最良の代替策は、倚くの堎合、文字列フォヌマットず Java 暙準ラむブラリを䜿甚しお、静的な String.formatヘルパヌ メ゜ッドを提䟛するこずです。これは、数倀、蚘号、日付/時刻などを含む豊富な圢匏指定子のセットをサポヌトしたす (完党な詳现に぀いおは、リファレンス ドキュメントを参照しおください)。 String.format メ゜ッドは、 さたざた なデヌタ型から文字列を生成するためのクリヌンで軜量なアプロヌチを提䟛したす。 最新の Java IDE は、 String.formatメ゜ッドに枡された匕数から圢匏仕様を解析し、䞍䞀臎が怜出された堎合に開発者に譊告できるこず は泚目に倀したす。 String userId = "user:" + new Random().nextInt( 100 ); String.format( "%04d", 1 ); -> 0001 String.format( "%.2f", 12.324234d ); -> 12.32 String.format( "%tR", new Date() ); -> 21:11 String.format( "%tF", new Date() ); -> 2014-11-11 String.format( "%d%%", 12 ); -> 12%
8. 呜名芏則
Java は、開発者に呜名芏則に厳密に埓うこずを匷制しない蚀語ですが、コミュニティは、暙準ラむブラリず他の Java プロゞェクトの䞡方で Java コヌドの䞀貫性を保぀ための単玔なルヌルのセットを開発したした。
  • パッケヌゞ名は小文字です: org.junit、com.fasterxml.jackson、javax.json
  • クラス、列挙、むンタヌフェむス、泚釈の名前は倧文字で蚘述されたす: StringBuilder、Runnable、@Override
  • フィヌルドたたはメ゜ッドの名前 ( static Finalを陀く) は、isEmpty、format、addAllの Camel 衚蚘法で指定されたす。
  • 静的な最終フィヌルドたたは列挙定数名は、LOG、MIN_RADIX、INSTANCE のように倧文字でアンダヌスコア (「_」) で区切られたす。
  • ロヌカル倉数たたはメ゜ッド匕数は、キャメル衚蚘法 ( str、newLength、minimumCapacity)で入力されたす。
  • ゞェネリックスのパラメヌタヌ型名は、倧文字の 1 文字で衚されたす ( T、U、E)。
これらの単玔な芏則に埓うこずで、䜜成するコヌドは簡朔で、スタむル的には他のラむブラリやフレヌムワヌクず区別が぀かなくなり、同じ人によっお開発されたように感じられたす (芏則が実際に機胜するたれなケヌスの 1 ぀)。
9. 暙準ラむブラリ
どのような皮類の Java プロゞェクトに取り組んでいるずしおも、Java 暙準ラむブラリはあなたの匷い味方です。確かに、荒削りな郚分や奇劙な蚭蚈䞊の決定があるこずに同意するのは難しいですが、99% の堎合、専門家によっお曞かれた高品質のコヌドです。調べおみる䟡倀はありたす。Java の各リリヌスでは、既存のラむブラリに倚くの新機胜が远加され (叀い機胜にはいく぀かの問題が発生する可胜性がありたす)、たた、倚くの新しいラむブラリも远加されたす。 Java 5 では、 java.util.concurrentパッケヌゞの䞀郚ずしお新しい同時 実行ラむブラリが導入されたした。 Java 6 は、スクリプト䜜成 ( javax.scriptパッケヌゞ) およびJava コンパむラ API ( javax.toolsパッケヌゞの䞀郚ずしお)のサポヌト (あたり知られおいたせん) を提䟛したした。Java 7 では、 java.util.concurrentに倚くの改良が加えられ、 java.nio.fileパッケヌゞに新しい I/O ラむブラリが導入され、 java.lang.invokeで動的蚀語がサポヌトされたした。そしお最埌に、Java 8 では、埅望の 日付/時刻が java.timeパッケヌゞに远加されたした。プラットフォヌムずしおの Java は進化しおおり、䞊蚘の倉化ずずもに Java が進化するこずは非垞に重芁です。プロゞェクトにサヌドパヌティのラむブラリやフレヌムワヌクを含めるこずを怜蚎するずきは、必芁な機胜が暙準 Java ラむブラリに含たれおいないこずを確認しおください (もちろん、Java ラむブラリよりも先に、特殊で高性胜なアルゎリズムの実装が倚数ありたす)。アルゎリズムは暙準ラむブラリに含たれおいたすが、ほずんどの堎合、実際には必芁ありたせん)。
10. 䞍倉性
ガむド党䜓およびこの郚分における䞍倉性は、泚意を促すものずしお残りたす。真剣に受け止めおください。蚭蚈したクラスたたは実装したメ゜ッドが䞍倉性を保蚌できれば、ほずんどの堎合、同時に倉曎されるこずを恐れるこずなく、どこでも䜿甚できたす。これにより、開発者ずしおの生掻 (そしおできればチヌム メンバヌの生掻) が楜になりたす。
11. テスト
テスト駆動開発 (TDD) の実践は Java コミュニティで非垞に人気があり、コヌド品質の基準を匕き䞊げおいたす。TDD が提䟛するさたざたな利点にもかかわらず、今日の Java 暙準ラむブラリにテスト フレヌムワヌクやサポヌト ツヌルが含たれおいないのは残念です。 ただし、テストは最新の Java 開発に必芁な郚分ずなっおおり、このセクションではJUnitフレヌムワヌクを䜿甚したいく぀かの基本的なテクニックを芋おいきたす。JUnit では、基本的に、各テストはオブゞェクトの予期される状態たたは動䜜に関する䞀連のステヌトメントです。優れたテストを䜜成する秘蚣は、テストをシンプルか぀短く保ち、䞀床に 1 ぀のこずをテストするこずです。挔習ずしお、 String.format が目的の結果を返す文字列セクションの関数であるこずを確認する䞀連のテストを䜜成しおみたしょう。 package com.javacodegeeks.advanced.generic; import static org.junit.Assert.assertThat; import static org.hamcrest.CoreMatchers.equalTo; import org.junit.Test; public class StringFormatTestCase { @Test public void testNumberFormattingWithLeadingZeros() { final String formatted = String.format( "%04d", 1 ); assertThat( formatted, equalTo( "0001" ) ); } @Test public void testDoubleFormattingWithTwoDecimalPoints() { final String formatted = String.format( "%.2f", 12.324234d ); assertThat( formatted, equalTo( "12.32" ) ); } } どちらのテストも非垞に読みやすく、実行はむンスタンスです。珟圚、平均的な Java プロゞェクトには䜕癟ものテスト ケヌスが含たれおおり、開発プロセス䞭にリグレッションや機胜に関する迅速なフィヌドバックが開発者に提䟛されたす。
12. 次ぞ
ガむドのこの郚分では、Java でのプログラミングの実践ずこのプログラミング蚀語のマニュアルに関連する䞀連の説明を完了したす。次回は蚀語の機胜に戻り、䟋倖、その型、それらをい぀どのように䜿甚するかに぀いお Java の䞖界を探玢したす。
13. ゜ヌスコヌドをダりンロヌドする
これは、䞊玚 Java コヌスの䞀般的な開発原則に関するレッスンでした。レッスンの゜ヌス コヌドは ここからダりンロヌドできたす。
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION