1. Gambaran keseluruhan
Dalam tutorial ini, kami akan memperkenalkan salah satu corak reka bentuk GoF tingkah laku - corak State.
Pada mulanya, kami akan memberikan gambaran keseluruhan tujuannya dan menjelaskan masalah yang cuba diselesaikannya. Kemudian, kita akan melihat rajah UML Negeri dan pelaksanaan contoh praktikal.
2. Pola Reka Bentuk Negeri
Idea utama corak State adalah membiarkan objek untuk mengubah tingkah lakunya tanpa mengubah kelasnya. Juga, dengan menerapkannya, kod tersebut harus tetap bersih tanpa banyak pernyataan if / else.
Bayangkan kita mempunyai bungkusan yang dihantar ke pejabat pos, bungkusan itu sendiri boleh dipesan, kemudian dihantar ke pejabat pos dan akhirnya diterima oleh pelanggan. Sekarang, bergantung pada keadaan sebenar, kami ingin mencetak status penghantarannya.
Pendekatan paling mudah adalah dengan menambahkan beberapa boolean flags dan menggunakan pernyataan sederhana jika / lain dalam setiap kaedah kami di kelas. Itu tidak akan menyulitkannya dalam senario sederhana. Walau bagaimanapun, ini mungkin menyulitkan dan mencemarkan kod kita apabila kita akan mendapatkan lebih banyak keadaan untuk diproses yang akan menghasilkan lebih banyak lagi jika / lain-lain penyataan.
Selain itu, semua logik untuk setiap negara akan tersebar di semua kaedah. Sekarang, di sinilah corak Negara dapat dipertimbangkan untuk digunakan. Terima kasih kepada corak reka bentuk Negara, kita dapat merangkumi logik dalam kelas khusus, menerapkan Prinsip Tanggungjawab Tunggal dan Prinsip Terbuka / Tertutup, mempunyai kod yang lebih bersih dan dapat dikendalikan.
3. Diagram UML

Dalam rajah UML, kita melihat bahawa kelas Konteks mempunyai Negeri yang berkaitan yang akan berubah semasa pelaksanaan program.
Konteks kami akan mewakilkan tingkah laku tersebut kepada pelaksanaan negara. Dengan kata lain, semua permintaan yang masuk akan ditangani oleh pelaksanaan konkrit negara.
Kami melihat bahawa logik dipisahkan dan menambahkan keadaan baru adalah mudah - ia datang untuk menambahkan pelaksanaan Negeri lain jika diperlukan.
4. Pelaksanaan
Mari merancang aplikasi kami. Seperti yang telah disebutkan, paket dapat dipesan, dihantar dan diterima, oleh itu kita akan mempunyai tiga keadaan dan kelas konteks.
Pertama, mari kita tentukan konteks kita, iaitu kelas Pakej :
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 dapat kita lihat, ini berisi rujukan untuk mengurus keadaan, perhatikan metode sebelumnyaState (), nextState () dan printStatus () di mana kita menyerahkan tugas ke objek keadaan. Negeri akan dihubungkan antara satu sama lain dan setiap negeri akan menetapkan satu sama lain berdasarkan rujukan ini yang diteruskan ke kedua-dua kaedah tersebut.
Pelanggan akan berinteraksi dengan kelas Pakej , namun dia tidak perlu berurusan dengan menetapkan keadaan, yang perlu dilakukan oleh pelanggan adalah pergi ke keadaan berikutnya atau sebelumnya.
Seterusnya, kita akan mempunyai PackageState yang mempunyai tiga kaedah dengan tandatangan berikut:
public interface PackageState { void next(Package pkg); void prev(Package pkg); void printStatus(); }
Antaramuka ini akan dilaksanakan oleh setiap kelas keadaan konkrit.
Keadaan konkrit pertama akan dipesan :
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, kita menunjukkan keadaan seterusnya yang akan berlaku setelah pakej dipesan. Keadaan yang diperintahkan adalah keadaan akar kami dan kami menandainya dengan jelas. Kita dapat melihat dalam kedua kaedah bagaimana peralihan antara keadaan ditangani.
Mari 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 hubungan antara negeri-negeri. Pakej ini mengubah keadaannya dari yang diperintahkan untuk dikirimkan, 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 boleh kembali ke keadaan sebelumnya.
Kami telah melihat ada beberapa pembayaran kerana satu negeri tahu tentang yang lain. Kami menjadikan mereka bergandingan erat.
5. Menguji
Mari lihat bagaimana pelaksanaannya berkelakuan. Pertama, mari kita sahkan sama ada peralihan persediaan 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 dengan cepat apakah pakej kami boleh kembali dengan keadaannya:
@Test public void givenDeliveredPackage_whenPrevState_thenStateOrdered() { Package pkg = new Package(); pkg.setState(new DeliveredState()); pkg.previousState(); assertThat(pkg.getState(), instanceOf(OrderedState.class)); }
Selepas itu, mari kita sahkan perubahan keadaan dan lihat bagaimana pelaksanaan kaedah printStatus () mengubah pelaksanaannya pada waktu 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.
Oleh kerana kita telah mengubah keadaan konteks kita, tingkah laku itu berubah tetapi kelasnya tetap sama. Serta API yang kami gunakan.
Juga, peralihan antara negeri telah berlaku, kelas kami mengubah keadaannya dan seterusnya tingkah lakunya.
6. Kelemahan
Kekurangan corak keadaan adalah hasil semasa melaksanakan peralihan antara negeri. Itu menjadikan negara menjadi keras, yang merupakan amalan buruk pada amnya.
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
Corak reka bentuk keadaan sangat bagus apabila kita ingin mengelakkan penyataan primitif jika / lain . Sebaliknya, kami mengeluarkan logik untuk memisahkan kelas dan membiarkan objek konteks kami mewakilkan tingkah laku kepada kaedah yang dilaksanakan di kelas negeri. Selain itu, kita dapat memanfaatkan peralihan antara negara, di mana satu keadaan dapat mengubah keadaan konteksnya.
Secara umum, corak reka bentuk ini bagus untuk aplikasi yang agak sederhana, tetapi untuk pendekatan yang lebih maju, kita dapat melihat tutorial Spring's State Machine.
Seperti biasa, kod lengkap boleh didapati di projek GitHub.