Corak Perintah di Jawa

1. Gambaran keseluruhan

Pola arahan adalah corak reka bentuk tingkah laku dan merupakan sebahagian daripada senarai corak reka bentuk rasmi GoF. Sederhananya, pola bermaksud merangkum dalam objek semua data yang diperlukan untuk melakukan tindakan tertentu (perintah), termasuk metode apa yang harus dipanggil, argumen metode, dan objek yang menjadi milik metode tersebut.

Model ini membolehkan kita mencabut objek yang menghasilkan perintah dari pengguna mereka , jadi itulah sebabnya corak ini biasanya dikenali sebagai pola pengeluar-pengguna.

Dalam tutorial ini, kita akan belajar bagaimana menerapkan pola perintah di Java dengan menggunakan pendekatan berorientasi objek dan fungsi objek, dan kita akan melihat dalam kes penggunaan apa yang berguna.

2. Pelaksanaan Berorientasikan Objek

Dalam pelaksanaan klasik, corak perintah memerlukan pelaksanaan empat komponen: Perintah, Penerima, Invoker, dan Klien .

Untuk memahami bagaimana corak berfungsi dan peranan yang dimainkan oleh setiap komponen, mari buat contoh asas.

Mari kita anggap bahawa kita ingin mengembangkan aplikasi fail teks. Dalam kes seperti itu, kita harus menerapkan semua fungsi yang diperlukan untuk melakukan beberapa operasi terkait file teks, seperti membuka, menulis, menyimpan file teks, dan sebagainya.

Oleh itu, kita harus membahagikan aplikasinya kepada empat komponen yang disebutkan di atas.

2.1. Kelas Perintah

Perintah adalah objek yang peranannya adalah untuk menyimpan semua informasi yang diperlukan untuk melakukan tindakan , termasuk metode untuk memanggil, argumen metode, dan objek (dikenal sebagai penerima) yang menerapkan metode tersebut.

Untuk mendapatkan idea yang lebih tepat tentang bagaimana objek perintah berfungsi, mari mulakan pengembangan lapisan perintah mudah yang merangkumi hanya satu antara muka tunggal dan dua pelaksanaan:

@FunctionalInterface public interface TextFileOperation { String execute(); }
public class OpenTextFileOperation implements TextFileOperation { private TextFile textFile; // constructors @Override public String execute() { return textFile.open(); } }
public class SaveTextFileOperation implements TextFileOperation { // same field and constructor as above @Override public String execute() { return textFile.save(); } } 

Dalam kes ini, antara muka TextFileOperation menentukan API objek perintah, dan dua pelaksanaannya, OpenTextFileOperation dan SaveTextFileOperation, melakukan tindakan konkrit. Yang pertama membuka fail teks, sementara yang kedua menyimpan fail teks.

Jelas untuk melihat fungsi objek perintah: perintah TextFileOperation merangkum semua maklumat yang diperlukan untuk membuka dan menyimpan fail teks, termasuk objek penerima, kaedah memanggil, dan argumen (dalam kes ini, tidak ada argumen yang diperlukan, tetapi mereka boleh).

Perlu ditekankan bahawa komponen yang melakukan operasi fail adalah penerima ( contoh TextFile ) .

2.2. Kelas Penerima

Penerima adalah objek yang melakukan sekumpulan tindakan padu . Ini adalah komponen yang melakukan tindakan sebenar ketika kaedah perintah () dijalankan .

Dalam kes ini, kita perlu menentukan kelas penerima, yang berperanan untuk memodelkan objek TextFile :

public class TextFile { private String name; // constructor public String open() { return "Opening file " + name; } public String save() { return "Saving file " + name; } // additional text file methods (editing, writing, copying, pasting) } 

2.3. Kelas Invoker

Invoker adalah objek yang tahu bagaimana melaksanakan perintah yang diberikan tetapi tidak tahu bagaimana perintah tersebut telah dilaksanakan. Ia hanya mengetahui antara muka perintah.

Dalam beberapa kes, penyerang juga menyimpan dan memerintahkan arahan, selain melaksanakannya. Ini berguna untuk melaksanakan beberapa ciri tambahan, seperti rakaman makro atau buat asal dan buat semula fungsi.

Dalam contoh kita, menjadi jelas bahawa mesti ada komponen tambahan yang bertanggungjawab untuk memanggil objek perintah dan melaksanakannya melalui kaedah perintah eksekusi () . Di sinilah kelas invoker mula bermain .

Mari lihat pelaksanaan asas invoker kami:

public class TextFileOperationExecutor { private final List textFileOperations = new ArrayList(); public String executeOperation(TextFileOperation textFileOperation) { textFileOperations.add(textFileOperation); return textFileOperation.execute(); } }

The TextFileOperationExecutor kelas hanya lapisan nipis abstraksi yang decouples objek arahan daripada pengguna mereka dan panggilan kaedah yang terkandung dalam TextFileOperation objek arahan.

Dalam kes ini, kelas juga menyimpan objek perintah dalam Senarai . Sudah tentu, ini tidak wajib dalam pelaksanaan corak, kecuali kita perlu menambahkan beberapa kawalan lebih lanjut pada proses pelaksanaan operasi.

2.4. Kelas Pelanggan

Klien adalah objek yang mengendalikan proses pelaksanaan perintah dengan menentukan perintah apa yang harus dilaksanakan dan pada tahap apa proses untuk melaksanakannya.

Oleh itu, jika kita ingin menjadi ortodoks dengan definisi formal corak, kita mesti membuat kelas pelanggan dengan menggunakan kaedah utama yang biasa :

public static void main(String[] args) { TextFileOperationExecutor textFileOperationExecutor = new TextFileOperationExecutor(); textFileOperationExecutor.executeOperation( new OpenTextFileOperation(new TextFile("file1.txt")))); textFileOperationExecutor.executeOperation( new SaveTextFileOperation(new TextFile("file2.txt")))); } 

3. Pelaksanaan Objek-Fungsional

Sejauh ini, kami telah menggunakan pendekatan berorientasi objek untuk melaksanakan corak perintah, yang semuanya baik dan baik.

Dari Java 8, kita dapat menggunakan pendekatan objek-fungsi, berdasarkan ungkapan lambda dan rujukan kaedah, untuk membuat kod sedikit lebih padat dan kurang verbose .

3.1. Menggunakan Lambda Expressions

Oleh kerana antara muka TextFileOperation adalah antara muka yang berfungsi, kita dapat menyampaikan objek perintah dalam bentuk ekspresi lambda kepada penyerang , tanpa harus membuat contoh TextFileOperation secara eksplisit:

TextFileOperationExecutor textFileOperationExecutor = new TextFileOperationExecutor(); textFileOperationExecutor.executeOperation(() -> "Opening file file1.txt"); textFileOperationExecutor.executeOperation(() -> "Saving file file1.txt"); 

The implementation now looks much more streamlined and concise, as we've reduced the amount of boilerplate code.

Even so, the question still stands: is this approach better, compared to the object-oriented one?

Well, that's tricky. If we assume that more compact code means better code in most cases, then indeed it is.

As a rule of thumb, we should evaluate on a per-use-case basis when to resort to lambda expressions.

3.2. Using Method References

Similarly, we can use method references for passing command objects to the invoker:

TextFileOperationExecutor textFileOperationExecutor = new TextFileOperationExecutor(); TextFile textFile = new TextFile("file1.txt"); textFileOperationExecutor.executeOperation(textFile::open); textFileOperationExecutor.executeOperation(textFile::save); 

In this case, the implementation is a little bit more verbose than the one that uses lambdas, as we still had to create the TextFile instances.

4. Conclusion

Dalam artikel ini, kami mempelajari konsep utama pola perintah dan bagaimana menerapkan pola di Jawa dengan menggunakan pendekatan berorientasi objek dan kombinasi ekspresi lambda dan rujukan kaedah.

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