Kaedah Pembangun Java vs Kilang Statik

1. Gambaran keseluruhan

Pembina Java adalah mekanisme lalai untuk mendapatkan contoh kelas yang diinisialisasi sepenuhnya. Bagaimanapun, mereka menyediakan semua infrastruktur yang diperlukan untuk menyuntikkan kebergantungan, sama ada secara manual atau automatik.

Walaupun begitu, dalam beberapa kes penggunaan tertentu, lebih baik menggunakan kaedah kilang statik untuk mencapai hasil yang sama.

Dalam tutorial ini, kita akan menyoroti kebaikan dan keburukan menggunakan kaedah kilang statik berbanding pembangun Java lama .

2. Kelebihan Kaedah Kilang Statik Terhadap Konstruktor

Dalam bahasa berorientasikan objek seperti Java, apa yang salah dengan pembina? Secara keseluruhan, tiada apa-apa. Walaupun begitu, Item Berkesan Java Block 1 yang terkenal dengan jelas menyatakan:

"Pertimbangkan kaedah kilang statik dan bukannya pembina"

Walaupun ini bukan peluru perak, berikut adalah alasan paling kuat yang menyokong pendekatan ini:

  1. Pembina tidak mempunyai nama yang bermakna , jadi mereka selalu terhad pada konvensyen penamaan standard yang dikenakan oleh bahasa. Kaedah kilang statik boleh mempunyai nama yang bermakna , oleh itu dengan jelas menyampaikan apa yang mereka lakukan
  2. Kaedah kilang statik dapat mengembalikan jenis yang sama yang menerapkan kaedah, subtipe, dan juga primitif , sehingga mereka menawarkan rangkaian jenis pengembalian yang lebih fleksibel
  3. Kaedah kilang statik dapat merangkum semua logik yang diperlukan untuk pra-pembinaan keadaan yang diinisialisasi sepenuhnya , sehingga mereka dapat digunakan untuk memindahkan logik tambahan ini keluar dari pembina. Ini menghalang pembina melaksanakan tugas lebih jauh, selain daripada hanya memulakan bidang
  4. Kaedah kilang statik boleh digunakan kaedah kawalan , dengan corak Singleton menjadi contoh ciri yang paling menyerlah

3. Kaedah Kilang Statik di JDK

Terdapat banyak contoh kaedah kilang statik di JDK yang menunjukkan banyak kelebihan yang dinyatakan di atas. Mari kita terokai beberapa daripadanya.

3.1. The String Kelas

Oleh kerana String interning terkenal , sangat tidak mungkin kita akan menggunakan konstruktor String class untuk membuat objek String baru . Walaupun begitu, ini betul-betul sah:

String value = new String("Baeldung");

Dalam kes ini, konstruktor akan membuat objek String baru , yang merupakan tingkah laku yang diharapkan.

Sebagai alternatif, jika kita ingin membuat objek String baru menggunakan kaedah kilang statik , kita dapat menggunakan beberapa implementasi berikut dari metode valueOf () :

String value1 = String.valueOf(1); String value2 = String.valueOf(1.0L); String value3 = String.valueOf(true); String value4 = String.valueOf('a'); 

Terdapat beberapa pelaksanaan nilaiOf () yang terlalu banyak . Masing-masing akan mengembalikan objek String baru , bergantung pada jenis argumen yang diteruskan ke metode (misalnya int , long , boolean , char, dan sebagainya).

Nama itu menyatakan dengan jelas apa kaedah yang dilakukan. Ini juga berpegang pada standar yang mapan dalam ekosistem Jawa untuk menamakan kaedah kilang statik.

3.2. The Pilihan Kelas

Contoh lain kaedah kilang statik yang kemas di JDK adalah kelas Pilihan . Kelas ini menerapkan beberapa kaedah kilang dengan nama yang cukup bermakna , termasuk kosong () , () , dan ofNullable () :

Optional value1 = Optional.empty(); Optional value2 = Optional.of("Baeldung"); Optional value3 = Optional.ofNullable(null);

3.3. The Collections Kelas

Mungkin sekali contoh wakil yang paling kaedah kilang statik dalam JDK adalah Collections kelas. Ini adalah kelas tidak cepat yang hanya menggunakan kaedah statik.

Sebilangan besar kaedah ini adalah kaedah kilang yang juga mengembalikan koleksi, setelah menerapkan koleksi algoritma kepada koleksi yang dibekalkan.

Berikut adalah beberapa contoh kaedah kilang yang biasa:

Collection syncedCollection = Collections.synchronizedCollection(originalCollection); Set syncedSet = Collections.synchronizedSet(new HashSet()); List unmodifiableList = Collections.unmodifiableList(originalList); Map unmodifiableMap = Collections.unmodifiableMap(originalMap); 

Bilangan kaedah kilang statik di JDK benar-benar luas, jadi kami akan menyimpan senarai contoh pendek untuk kepentingan singkat.

Walaupun begitu, contoh di atas harus memberi kita idea yang jelas tentang bagaimana kaedah kilang statik yang terdapat di Jawa.

4. Kaedah Kilang Statik Tersuai

Sudah tentu, kita dapat melaksanakan kaedah kilang statik kita sendiri. Tetapi bilakah benar-benar layak untuk melakukannya, daripada membuat contoh kelas melalui pembina biasa?

Mari lihat contoh mudah.

Mari kita pertimbangkan kelas Pengguna yang naif ini:

public class User { private final String name; private final String email; private final String country; public User(String name, String email, String country) { this.name = name; this.email = email; this.country = country; } // standard getters / toString }

Dalam kes ini, tidak ada amaran yang dapat dilihat untuk menunjukkan bahawa kaedah kilang statik lebih baik daripada pembangun standard.

Bagaimana jika kita mahu semua contoh Pengguna mendapat nilai lalai untuk bidang negara ?

Sekiranya kita menginisialisasi bidang dengan nilai lalai, kita juga harus memfaktorkan semula konstruktor, sehingga menjadikan reka bentuknya lebih kaku.

Kita boleh menggunakan kaedah kilang statik sebagai gantinya:

public static User createWithDefaultCountry(String name, String email) { return new User(name, email, "Argentina"); }

Inilah cara kami mendapatkan contoh Pengguna dengan nilai lalai yang diberikan ke medan negara :

User user = User.createWithDefaultCountry("John", "[email protected]");

5. Logik Bergerak keluar dari Pembina

Kelas Pengguna kami dapat dengan cepat berubah menjadi reka bentuk yang cacat jika kami memutuskan untuk menerapkan ciri-ciri yang memerlukan penambahan logik lebih lanjut ke pembangun (loceng penggera harus dibunyikan pada masa ini).

Anggaplah bahawa kita ingin memberikan kelas kemampuan untuk mencatat masa di mana setiap objek Pengguna dibuat.

Sekiranya kita memasukkan logik ini ke dalam konstruktor, kita akan melanggar Prinsip Tanggungjawab Tunggal . Kami akan berakhir dengan konstruktor monolitik yang lebih banyak daripada memulakan bidang.

Kami dapat memastikan reka bentuk kami bersih dengan kaedah kilang statik:

public class User { private static final Logger LOGGER = Logger.getLogger(User.class.getName()); private final String name; private final String email; private final String country; // standard constructors / getters public static User createWithLoggedInstantiationTime( String name, String email, String country) { LOGGER.log(Level.INFO, "Creating User instance at : {0}", LocalTime.now()); return new User(name, email, country); } } 

Inilah cara kami membuat contoh Pengguna yang ditingkatkan :

User user = User.createWithLoggedInstantiationTime("John", "[email protected]", "Argentina");

6. Instantiation yang Dikendalikan Instance

Seperti yang ditunjukkan di atas, kita dapat merangkum potongan logik ke dalam kaedah kilang statik sebelum mengembalikan objek Pengguna yang diinisialisasi sepenuhnya . Dan kita dapat melakukan ini tanpa mencemarkan pembina dengan tanggungjawab melaksanakan pelbagai tugas yang tidak berkaitan.

For instance, suppose we want to make our User class a Singleton. We can achieve this by implementing an instance-controlled static factory method:

public class User { private static volatile User instance = null; // other fields / standard constructors / getters public static User getSingletonInstance(String name, String email, String country) { if (instance == null) { synchronized (User.class) { if (instance == null) { instance = new User(name, email, country); } } } return instance; } } 

The implementation of the getSingletonInstance() method is thread-safe, with a small performance penalty, due to the synchronized block.

In this case, we used lazy initialization to demonstrate the implementation of an instance-controlled static factory method.

It's worth mentioning, however, that the best way to implement a Singleton is with a Java enum type, as it's both serialization-safe and thread-safe. For the full details on how to implement Singletons using different approaches, please check this article.

As expected, getting a User object with this method looks very similar to the previous examples:

User user = User.getSingletonInstance("John", "[email protected]", "Argentina");

7. Conclusion

In this article, we explored a few use cases where static factory methods can be a better alternative to using plain Java constructors.

Moreover, this refactoring pattern is so tightly rooted to a typical workflow that most IDEs will do it for us.

Of course, Apache NetBeans, IntelliJ IDEA, and Eclipse will perform the refactoring in slightly different ways, so please make sure first to check your IDE documentation.

As with many other refactoring patterns, we should use static factory methods with due caution, and only when it's worth the trade-off between producing more flexible and clean designs and the cost of having to implement additional methods.

Seperti biasa, semua contoh kod yang ditunjukkan dalam artikel ini terdapat di GitHub.