Corak Reka Bentuk Pelawat di Jawa

1. Gambaran keseluruhan

Dalam tutorial ini, kami akan memperkenalkan salah satu corak reka bentuk GoF tingkah laku - Pelawat.

Pertama, kami akan menerangkan tujuannya dan masalah yang cuba diselesaikan.

Seterusnya, kita akan melihat gambarajah UML Pelawat dan pelaksanaan contoh praktikal.

2. Corak Reka Bentuk Pelawat

Tujuan corak Pelawat adalah untuk menentukan operasi baru tanpa memperkenalkan modifikasi pada struktur objek yang ada.

Bayangkan bahawa kita mempunyai kompositobjek yang terdiri daripada komponen. Struktur objek tetap - kita tidak dapat mengubahnya, atau kita tidak merancang untuk menambahkan jenis elemen baru ke struktur.

Sekarang, bagaimana kita dapat menambahkan fungsi baru pada kod kita tanpa mengubah kelas yang ada?

Corak reka bentuk Pelawat mungkin menjadi jawapan. Ringkasnya, yang harus kita lakukan adalah menambahkan fungsi yang menerima kelas pelawat ke setiap elemen struktur.

Dengan cara itu, komponen kita akan membolehkan pelaksanaan pelawat untuk "mengunjunginya" dan melakukan tindakan yang diperlukan terhadap elemen tersebut.

Dengan kata lain, kami akan mengekstrak algoritma yang akan diterapkan pada struktur objek dari kelas.

Oleh itu, kami akan menggunakan prinsip Open / Closed dengan baik kerana kami tidak akan mengubah kodnya, tetapi kami masih dapat memperluas fungsi dengan menyediakan implementasi Pelawat baru .

3. Diagram UML

Pada rajah UML di atas, kami mempunyai dua hierarki pelaksanaan, pelawat khusus, dan elemen konkrit.

Pertama sekali, pelanggan menggunakan pelaksanaan Pelawat dan menerapkannya pada struktur objek. Objek komposit berulang pada komponennya dan melampirkan pelawat ke masing-masing.

Sekarang, yang sangat relevan adalah bahawa elemen konkrit (ConcreteElementA dan ConcreteElementB) menerima Pelawat, hanya membenarkannya mengunjunginya .

Terakhir, kaedah ini sama untuk semua elemen dalam struktur, ia melakukan penghantaran dua kali dengan meneruskannya (melalui kata kunci ini ) ke kaedah lawatan pengunjung.

4. Pelaksanaan

Contoh kami ialah objek Dokumen tersuai yang terdiri daripada unsur konkrit JSON dan XML; unsur-unsur mempunyai superclass abstrak yang sama, Elemen.

The Document kelas:

public class Document extends Element { List elements = new ArrayList(); // ... @Override public void accept(Visitor v) { for (Element e : this.elements) { e.accept(v); } } }

The Element kelas mempunyai satu kaedah abstrak yang menerima Pelawat muka:

public abstract void accept(Visitor v);

Oleh itu, semasa membuat elemen baru, beri nama JsonElement , kita harus menyediakan pelaksanaannya dengan kaedah ini.

Walau bagaimanapun, kerana corak Pengunjung, pelaksanaannya akan sama, jadi dalam kebanyakan kes, kami memerlukan salin-tampal kod boilerplate dari elemen lain yang sudah ada:

public class JsonElement extends Element { // ... public void accept(Visitor v) { v.visit(this); } }

Oleh kerana unsur-unsur kami membenarkannya dikunjungi oleh mana-mana pelawat, katakanlah bahawa kami ingin memproses elemen Dokumen kami , tetapi masing-masing dengan cara yang berbeza, bergantung pada jenis kelasnya.

Oleh itu, pelawat kami akan mempunyai kaedah yang berasingan untuk jenis yang diberikan:

public class ElementVisitor implements Visitor { @Override public void visit(XmlElement xe) { System.out.println( "processing an XML element with uuid: " + xe.uuid); } @Override public void visit(JsonElement je) { System.out.println( "processing a JSON element with uuid: " + je.uuid); } }

Di sini, pelawat konkrit kami menggunakan dua kaedah, masing-masing satu bagi setiap jenis Elemen .

Ini memberi kita akses ke objek struktur tertentu di mana kita dapat melakukan tindakan yang diperlukan.

5. Menguji

Untuk tujuan ujian, mari kita lihat kelas VisitorDemo :

public class VisitorDemo { public static void main(String[] args) { Visitor v = new ElementVisitor(); Document d = new Document(generateUuid()); d.elements.add(new JsonElement(generateUuid())); d.elements.add(new JsonElement(generateUuid())); d.elements.add(new XmlElement(generateUuid())); d.accept(v); } // ... }

Pertama, kami membuat Pelawat Elemen , ia mengandungi algoritma yang akan kami terapkan pada elemen kami.

Next, we set up our Document with proper components and apply the visitor which will be accepted by every element of an object structure.

The output would be like this:

processing a JSON element with uuid: fdbc75d0-5067-49df-9567-239f38f01b04 processing a JSON element with uuid: 81e6c856-ddaf-43d5-aec5-8ef977d3745e processing an XML element with uuid: 091bfcb8-2c68-491a-9308-4ada2687e203

It shows that visitor has visited each element of our structure, depending on the Element type, it dispatched the processing to appropriate method and could retrieve the data from every underlying object.

6. Downsides

As each design pattern, even the Visitor has its downsides, particularly, its usage makes it more difficult to maintain the code if we need to add new elements to the object's structure.

For example, if we add new YamlElement, then we need to update all existing visitors with the new method desired for processing this element. Following this further, if we have ten or more concrete visitors, that might be cumbersome to update all of them.

Other than this, when using this pattern, the business logic related to one particular object gets spread over all visitor implementations.

7. Conclusion

The Visitor pattern is great to separate the algorithm from the classes on which it operates. Besides that, it makes adding new operation more easily, just by providing a new implementation of the Visitor.

Furthermore, we don't depend on components interfaces, and if they are different, that's fine, since we have a separate algorithm for processing per concrete element.

Lebih-lebih lagi, Pelawat akhirnya dapat mengumpulkan data berdasarkan elemen yang dilaluinya.

Untuk melihat versi corak reka bentuk Pengunjung yang lebih khusus, lihat corak pelawat di Java NIO - penggunaan corak di JDK.

Seperti biasa, kod lengkap boleh didapati di projek Github.