Pola Desain Negara di Jawa

1. Ikhtisar

Dalam tutorial ini, kami akan memperkenalkan salah satu pola desain GoF behavioral - pola Status.

Pertama, kami akan memberikan gambaran umum tentang tujuannya dan menjelaskan masalah yang coba dipecahkannya. Kemudian, kita akan melihat diagram UML State dan implementasi contoh praktisnya.

2. Sebutkan Pola Desain

Ide utama dari pola State adalah untuk memungkinkan objek mengubah perilakunya tanpa mengubah kelasnya. Selain itu, dengan mengimplementasikannya, kode harus tetap bersih tanpa banyak pernyataan if / else.

Bayangkan kita memiliki paket yang dikirim ke kantor pos, paket itu sendiri sudah bisa dipesan, lalu dikirim ke kantor pos dan akhirnya diterima oleh klien. Sekarang, tergantung pada keadaan sebenarnya, kami ingin mencetak status pengirimannya.

Pendekatan paling sederhana adalah dengan menambahkan beberapa flag boolean dan menerapkan pernyataan if / else sederhana dalam setiap metode kita di kelas. Itu tidak akan memperumitnya dalam skenario sederhana. Namun, ini mungkin mempersulit dan mencemari kode kita ketika kita akan mendapatkan lebih banyak status untuk diproses yang akan menghasilkan lebih banyak pernyataan if / else.

Selain itu, semua logika untuk setiap negara bagian akan tersebar di semua metode. Sekarang, di sinilah pola Negara dapat dipertimbangkan untuk digunakan. Berkat pola desain Negara, kami dapat merangkum logika dalam kelas khusus, menerapkan Prinsip Tanggung Jawab Tunggal dan Prinsip Terbuka / Tertutup, memiliki kode yang lebih bersih dan lebih dapat dipelihara.

3. Diagram UML

Dalam diagram UML, kita melihat bahwa konteks kelas memiliki terkait Negara yang akan berubah selama eksekusi program.

Konteks kami akan mendelegasikan perilaku ke implementasi negara. Dengan kata lain, semua permintaan yang masuk akan ditangani oleh implementasi konkrit negara.

Kita melihat logika yang terpisah dan menambahkan negara-negara baru sederhana - datang ke menambahkan lain Negara pelaksanaan jika diperlukan.

4. Implementasi

Mari mendesain aplikasi kita. Seperti yang telah disebutkan, paket dapat dipesan, dikirim, dan diterima, oleh karena itu kami akan memiliki tiga status dan kelas konteks.

Pertama, mari kita tentukan konteks kita, itu akan menjadi kelas Paket :

public class Package { private PackageState state = new OrderedState(); // getter, setter public void previousState() { state.prev(this); } public void nextState() { state.next(this); } public void printStatus() { state.printStatus(); } }

Seperti yang bisa kita lihat, ini berisi referensi untuk mengelola state, notice beforeState (), nextState () dan metode printStatus () di mana kita mendelegasikan pekerjaan ke objek state. Status akan ditautkan satu sama lain dan setiap status akan disetel satu sama lain berdasarkan referensi ini yang diteruskan ke kedua metode.

Klien akan berinteraksi dengan kelas Package , namun dia tidak perlu berurusan dengan pengaturan status, yang harus dilakukan klien adalah pergi ke status berikutnya atau sebelumnya.

Selanjutnya, kita akan memiliki PackageState yang memiliki tiga metode dengan tanda tangan berikut:

public interface PackageState { void next(Package pkg); void prev(Package pkg); void printStatus(); }

Antarmuka ini akan diimplementasikan oleh setiap kelas negara beton.

Status beton pertama akan menjadi OrderedState :

public class OrderedState implements PackageState { @Override public void next(Package pkg) { pkg.setState(new DeliveredState()); } @Override public void prev(Package pkg) { System.out.println("The package is in its root state."); } @Override public void printStatus() { System.out.println("Package ordered, not delivered to the office yet."); } }

Di sini, kami menunjuk ke keadaan berikutnya yang akan terjadi setelah paket dipesan. Status terurut adalah status root kami dan kami menandainya secara eksplisit. Kita dapat melihat di kedua metode bagaimana transisi antar kondisi ditangani.

Mari kita lihat kelas DeliveredState :

public class DeliveredState implements PackageState { @Override public void next(Package pkg) { pkg.setState(new ReceivedState()); } @Override public void prev(Package pkg) { pkg.setState(new OrderedState()); } @Override public void printStatus() { System.out.println("Package delivered to post office, not received yet."); } }

Sekali lagi, kita melihat keterkaitan antar negara bagian. Paket mengubah statusnya dari dipesan menjadi terkirim, pesan di printStatus () juga berubah.

Status terakhir adalah ReceivedState :

public class ReceivedState implements PackageState { @Override public void next(Package pkg) { System.out.println("This package is already received by a client."); } @Override public void prev(Package pkg) { pkg.setState(new DeliveredState()); } }

Di sinilah kita mencapai keadaan terakhir, kita hanya dapat mengembalikan ke keadaan sebelumnya.

Kami sudah melihat ada imbalan karena satu negara bagian tahu tentang yang lain. Kami membuat mereka berpasangan erat.

5. Pengujian

Mari kita lihat bagaimana implementasi berperilaku. Pertama, mari kita verifikasi apakah transisi penyiapan berfungsi seperti yang diharapkan:

@Test public void givenNewPackage_whenPackageReceived_thenStateReceived() { Package pkg = new Package(); assertThat(pkg.getState(), instanceOf(OrderedState.class)); pkg.nextState(); assertThat(pkg.getState(), instanceOf(DeliveredState.class)); pkg.nextState(); assertThat(pkg.getState(), instanceOf(ReceivedState.class)); }

Kemudian, periksa cepat apakah paket kami dapat kembali dengan statusnya:

@Test public void givenDeliveredPackage_whenPrevState_thenStateOrdered() { Package pkg = new Package(); pkg.setState(new DeliveredState()); pkg.previousState(); assertThat(pkg.getState(), instanceOf(OrderedState.class)); }

Setelah itu, mari verifikasi perubahan status dan lihat bagaimana implementasi metode printStatus () mengubah implementasinya saat runtime:

public class StateDemo { public static void main(String[] args) { Package pkg = new Package(); pkg.printStatus(); pkg.nextState(); pkg.printStatus(); pkg.nextState(); pkg.printStatus(); pkg.nextState(); pkg.printStatus(); } }

Ini akan memberi kita output berikut:

Package ordered, not delivered to the office yet. Package delivered to post office, not received yet. Package was received by client. This package is already received by a client. Package was received by client.

Karena kami telah mengubah keadaan konteks kami, perilakunya berubah tetapi kelasnya tetap sama. Serta API yang kami gunakan.

Juga, transisi antar negara bagian telah terjadi, kelas kami mengubah statusnya dan akibatnya perilakunya.

6. Kerugian

Kelemahan pola negara bagian adalah hasil ketika menerapkan transisi antar negara bagian. Itu membuat negara menjadi hardcode, yang secara umum merupakan praktik yang buruk.

But, depending on our needs and requirements, that might or might not be an issue.

7. State vs. Strategy Pattern

Both design patterns are very similar, but their UML diagram is the same, with the idea behind them slightly different.

First, the strategy pattern defines a family of interchangeable algorithms. Generally, they achieve the same goal, but with a different implementation, for example, sorting or rendering algorithms.

In state pattern, the behavior might change completely, based on actual state.

Next, in strategy, the client has to be aware of the possible strategies to use and change them explicitly. Whereas in state pattern, each state is linked to another and create the flow as in Finite State Machine.

8. Conclusion

Pola desain status sangat bagus ketika kita ingin menghindari pernyataan if / else primitif . Sebaliknya, kami mengekstrak logika untuk memisahkan kelas dan membiarkan objek konteks mendelegasikan perilaku ke metode yang diterapkan di kelas negara bagian. Selain itu, kita dapat memanfaatkan transisi antar negara bagian, di mana satu negara bagian dapat mengubah keadaan konteksnya.

Secara umum, pola desain ini sangat bagus untuk aplikasi yang relatif sederhana, tetapi untuk pendekatan yang lebih maju, kita dapat melihat tutorial Spring's State Machine.

Seperti biasa, kode lengkap tersedia di proyek GitHub.