Konstanta di Java: Pola dan Anti-Pola

1. Perkenalan

Pada artikel ini, kita akan belajar tentang penggunaan konstanta di Java dengan fokus pada pola umum dan anti-pola.

Kita akan mulai dengan beberapa konvensi dasar untuk mendefinisikan konstanta. Dari sana, kita akan beralih ke anti-pola umum sebelum menyelesaikannya dengan melihat pola-pola umum.

2. Dasar-dasar

Konstanta adalah variabel yang nilainya tidak akan berubah setelah ditentukan.

Mari kita lihat dasar-dasar untuk mendefinisikan sebuah konstanta:

private static final int OUR_CONSTANT = 1;

Beberapa pola yang akan kita lihat akan membahas keputusan pengubah akses publik atau pribadi . Kami membuat konstanta kami statis dan final dan memberinya tipe yang sesuai, apakah itu primitif Java, kelas, atau enum . Nama harus semua huruf kapital dengan kata-kata dipisahkan oleh garis bawah , kadang-kadang dikenal sebagai kasus ular berteriak. Terakhir, kami memberikan nilai itu sendiri.

3. Anti-Pola

Pertama, mari kita mulai dengan mempelajari apa yang tidak boleh dilakukan. Mari kita lihat beberapa anti-pola umum yang mungkin kita temui saat bekerja dengan konstanta Java.

3.1. Angka Ajaib

Angka ajaib adalah literal numerik dalam satu blok kode:

if (number == 3.14159265359) { // ... }

Mereka sulit dipahami oleh pengembang lain. Selain itu, jika kami menggunakan angka di seluruh kode kami, sulit untuk menangani perubahan nilainya. Sebagai gantinya kita harus mendefinisikan angka sebagai konstanta.

3.2. Kelas Konstanta Global yang Besar

Saat kita memulai sebuah proyek, mungkin terasa wajar untuk membuat kelas bernama Constants atau Utils dengan maksud untuk menentukan semua konstanta untuk aplikasi di sana. Untuk proyek yang lebih kecil, ini mungkin baik-baik saja, tetapi mari pertimbangkan beberapa alasan mengapa ini bukan solusi yang ideal.

Pertama, mari kita bayangkan kita memiliki seratus atau lebih konstanta di kelas konstanta kita. Jika kelas tidak dipertahankan, baik untuk mengikuti dokumentasi dan sesekali mengubah konstanta menjadi pengelompokan logis, itu akan menjadi sangat tidak terbaca. Kita bahkan bisa mendapatkan konstanta duplikat dengan nama yang sedikit berbeda. Pendekatan ini cenderung memberi kita masalah keterbacaan dan pemeliharaan dalam segala hal kecuali proyek terkecil.

Selain logistik untuk mempertahankan kelas Constants itu sendiri, kami juga mengundang masalah pemeliharaan lainnya dengan mendorong terlalu banyak ketergantungan dengan satu kelas konstanta global ini dan berbagai bagian lain dari aplikasi kami.

Di sisi yang lebih teknis, compiler Java menempatkan nilai konstanta ke dalam variabel referensi di kelas tempat kita menggunakannya . Jadi, jika kita mengubah salah satu konstanta kita di kelas konstanta kita dan hanya mengompilasi ulang kelas itu dan bukan kelas referensi, kita bisa mendapatkan nilai konstanta yang tidak konsisten.

3.3. Antarmuka Anti-Pola Konstan

Pola antarmuka konstan adalah ketika kita mendefinisikan antarmuka yang berisi semua konstanta untuk fungsionalitas tertentu dan kemudian memiliki kelas yang membutuhkan fungsionalitas tersebut untuk mengimplementasikan antarmuka.

Mari tentukan antarmuka konstan untuk kalkulator:

public interface CalculatorConstants { double PI = 3.14159265359; double UPPER_LIMIT = 0x1.fffffffffffffP+1023; enum Operation {ADD, SUBTRACT, MULTIPLY, DIVIDE}; }

Selanjutnya, kami akan menerapkan antarmuka CalculatorConstants kami :

public class GeometryCalculator implements CalculatorConstants { public double operateOnTwoNumbers(double numberOne, double numberTwo, Operation operation) { // Code to do an operation } }

Argumen pertama yang menentang penggunaan antarmuka konstan adalah karena hal itu bertentangan dengan tujuan antarmuka. Kita dimaksudkan untuk menggunakan antarmuka untuk membuat kontrak untuk perilaku yang akan diberikan oleh kelas penerapan kita. Saat kami membuat antarmuka yang penuh dengan konstanta, kami tidak mendefinisikan perilaku apa pun.

Kedua, menggunakan antarmuka konstan membuka kita pada masalah run-time yang disebabkan oleh bayangan bidang. Mari kita lihat bagaimana itu mungkin terjadi dengan mendefinisikan konstanta UPPER_LIMIT dalam kelas GeometryCalculator kita :

public static final double UPPER_LIMIT = 100000000000000000000.0;

Setelah kami mendefinisikan konstanta itu di kelas GeometryCalculator kami , kami menyembunyikan nilai di antarmuka CalculatorConstants untuk kelas kami. Kami kemudian bisa mendapatkan hasil yang tidak terduga.

Another argument against this anti-pattern is that it causes namespace pollution. Our CalculatorConstants will now be in the namespace for any of our classes that implement the interface as well as any of their subclasses.

4. Patterns

Earlier, we looked at the appropriate form for defining constants. Let's look at some other good practices for defining constants within our applications.

4.1. General Good Practices

If constants are logically related to a class, we can just define them there. If we view a set of constants as members of an enumerated type, we can use an enum to define them.

Let's define some constants in a Calculator class:

public class Calculator { public static final double PI = 3.14159265359; private static final double UPPER_LIMIT = 0x1.fffffffffffffP+1023; public enum Operation { ADD, SUBTRACT, DIVIDE, MULTIPLY } public double operateOnTwoNumbers(double numberOne, double numberTwo, Operation operation) { if (numberOne > UPPER_LIMIT) { throw new IllegalArgumentException("'numberOne' is too large"); } if (numberTwo > UPPER_LIMIT) { throw new IllegalArgumentException("'numberTwo' is too large"); } double answer = 0; switch(operation) { case ADD: answer = numberOne + numberTwo; break; case SUBTRACT: answer = numberOne - numberTwo; break; case DIVIDE: answer = numberOne / numberTwo; break; case MULTIPLY: answer = numberOne * numberTwo; break; } return answer; } }

In our example, we've defined a constant for UPPER_LIMIT that we're only planning on using in the Calculator class, so we've set it to private. We want other classes to be able to use PI and the Operation enum, so we've set those to public.

Let's consider some of the advantages of using an enum for Operation. The first advantage is that it limits the possible values. Imagine that our method takes a string for the operation value with the expectation that one of four constant strings is supplied. We can easily foresee a scenario where a developer calling the method sends their own string value. With the enum, the values are limited to those we define. We can also see that enums are especially well suited to use in switch statements.

4.2. Constants Class

Now that we've looked at some general good practices, let's consider the case when a constants class might be a good idea. Let's imagine our application contains a package of classes that need to do various kinds of mathematical calculations. In this case, it probably makes sense for us to define a constants class in that package for constants that we'll use in our calculations classes.

Let's create a MathConstants class:

public final class MathConstants { public static final double PI = 3.14159265359; static final double GOLDEN_RATIO = 1.6180; static final double GRAVITATIONAL_ACCELERATION = 9.8; static final double EULERS_NUMBER = 2.7182818284590452353602874713527; public enum Operation { ADD, SUBTRACT, DIVIDE, MULTIPLY } private MathConstants() { } }

The first thing we should notice is that our class is final to prevent it from being extended. Additionally, we've defined a private constructor so it can't be instantiated. Finally, we can see that we've applied the other good practices we discussed earlier in the article. Our constant PI is public because we anticipate needing to access it outside of our package. The other constants we've left as package-private, so we can access them within our package. We've made all of our constants static and final and named them in a screaming snake case. The operations are a specific set of values, so we've used an enum untuk mendefinisikannya.

Kita dapat melihat bahwa kelas konstanta tingkat paket spesifik kita berbeda dari kelas konstanta global yang besar karena dilokalkan ke paket kita dan berisi konstanta yang relevan dengan kelas paket itu.

5. Kesimpulan

Dalam artikel ini, kami membahas pro dan kontra dari beberapa pola dan anti-pola paling populer yang terlihat saat menggunakan konstanta di Java. Kami memulai dengan beberapa aturan pemformatan dasar, sebelum membahas anti-pola. Setelah mempelajari beberapa anti-pola yang umum, kami melihat pola yang sering kami lihat diterapkan pada konstanta.

Seperti biasa, kode tersedia di GitHub.