JavaRush /Java Blog /Random-ID /Pola Desain di Jawa
Viacheslav
Level 3

Pola Desain di Jawa

Dipublikasikan di grup Random-ID
Pola atau pola desain sering kali merupakan bagian pekerjaan pengembang yang diabaikan, sehingga membuat kode sulit dipelihara dan diadaptasi dengan persyaratan baru. Saya sarankan Anda melihat apa itu dan bagaimana penggunaannya di JDK. Secara alami, semua pola dasar dalam satu atau lain bentuk telah ada di sekitar kita sejak lama. Mari kita lihat di ulasan ini.
Pola Desain di Java - 1
Isi:

Templat

Salah satu persyaratan paling umum dalam lowongan adalah “Pengetahuan tentang pola.” Pertama-tama, ada baiknya menjawab pertanyaan sederhana - “Apa itu Pola Desain?” Pola diterjemahkan dari bahasa Inggris sebagai “template”. Artinya, ini adalah pola tertentu yang menjadi dasar kita melakukan sesuatu. Hal yang sama juga berlaku dalam pemrograman. Ada beberapa praktik dan pendekatan terbaik yang sudah ada untuk memecahkan masalah umum. Setiap programmer adalah seorang arsitek. Bahkan ketika Anda hanya membuat beberapa kelas atau bahkan satu, itu tergantung pada Anda berapa lama kode tersebut dapat bertahan dalam perubahan persyaratan, seberapa nyaman kode tersebut digunakan oleh orang lain. Dan di sinilah pengetahuan tentang template akan membantu, karena... Ini akan memungkinkan Anda dengan cepat memahami cara terbaik menulis kode tanpa menulis ulang. Seperti yang Anda ketahui, programmer adalah orang yang malas dan lebih mudah untuk menulis sesuatu dengan baik segera daripada mengulanginya beberapa kali) Pola juga mungkin terlihat mirip dengan algoritma. Tapi mereka punya perbedaan. Algoritme terdiri dari langkah-langkah spesifik yang menjelaskan tindakan yang diperlukan. Pola hanya menggambarkan pendekatannya, tetapi tidak menggambarkan langkah-langkah implementasinya. Polanya berbeda, karena... memecahkan masalah yang berbeda. Biasanya kategori berikut dibedakan:
  • Generatif

    Pola-pola ini memecahkan masalah dalam membuat pembuatan objek menjadi fleksibel

  • Struktural

    Pola-pola ini memecahkan masalah membangun hubungan antar objek secara efektif

  • Perilaku

    Pola-pola ini memecahkan masalah interaksi efektif antar objek

Untuk mempertimbangkan contoh, saya sarankan menggunakan kompiler kode online repl.it.
Pola Desain di Java - 2

Pola penciptaan

Mari kita mulai dari awal siklus hidup objek - dengan penciptaan objek. Templat generatif membantu membuat objek dengan lebih nyaman dan memberikan fleksibilitas dalam proses ini. Salah satu yang paling terkenal adalah " Pembangun ". Pola ini memungkinkan Anda membuat objek kompleks selangkah demi selangkah. Di Jawa, contoh yang paling terkenal adalah StringBuilder:
class Main {
  public static void main(String[] args) {
    StringBuilder builder = new StringBuilder();
    builder.append("Hello");
    builder.append(',');
    builder.append("World!");
    System.out.println(builder.toString());
  }
}
Pendekatan lain yang terkenal dalam membuat suatu objek adalah dengan memindahkan pembuatannya ke dalam metode terpisah. Metode ini seolah-olah menjadi sebuah pabrik objek. Itu sebabnya pola ini disebut " Metode Pabrik ". Di Java misalnya, pengaruhnya bisa dilihat di kelas java.util.Calendar. Kelas itu sendiri Calendarbersifat abstrak, dan untuk membuatnya digunakan metode getInstance:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Calendar calendar = Calendar.getInstance();
    System.out.println(calendar.getTime());
    System.out.println(calendar.getClass().getCanonicalName());
  }
}
Hal ini sering kali terjadi karena logika di balik pembuatan objek bisa jadi rumit. Misalnya, dalam kasus di atas, kita mengakses kelas dasar Calendar, dan sebuah kelas dibuat GregorianCalendar. Jika kita melihat konstruktornya, kita dapat melihat bahwa implementasi yang berbeda dibuat tergantung pada kondisinya Calendar. Namun terkadang satu metode pabrik saja tidak cukup. Terkadang Anda perlu membuat objek berbeda agar bisa cocok satu sama lain. Templat lain akan membantu kita dalam hal ini - “ Pabrik abstrak ”. Dan kemudian kita perlu membuat pabrik berbeda di satu tempat. Pada saat yang sama, keuntungannya adalah detail implementasi tidak penting bagi kami, mis. tidak masalah pabrik spesifik mana yang kita dapatkan. Hal utama adalah hal itu menciptakan implementasi yang tepat. Contoh luar biasa:
Pola Desain di Java - 3
Artinya, bergantung pada lingkungan (sistem operasi), kita akan menerima pabrik tertentu yang akan membuat elemen yang kompatibel. Sebagai alternatif dari pendekatan berkreasi melalui orang lain, kita dapat menggunakan pola “ Prototype “. Esensinya sederhana - objek baru dibuat menurut gambar dan rupa objek yang sudah ada, mis. sesuai dengan prototipe mereka. Di Java, semua orang telah menemukan pola ini - ini adalah penggunaan antarmuka java.lang.Cloneable:
class Main {
  public static void main(String[] args) {
    class CloneObject implements Cloneable {
      @Override
      protected Object clone() throws CloneNotSupportedException {
        return new CloneObject();
      }
    }
    CloneObject obj = new CloneObject();
    try {
      CloneObject pattern = (CloneObject) obj.clone();
    } catch (CloneNotSupportedException e) {
      //Do something
    }
  }
}
Seperti yang Anda lihat, penelepon tidak mengetahui cara clone. Artinya, membuat suatu objek berdasarkan prototipe merupakan tanggung jawab objek itu sendiri. Hal ini berguna karena tidak mengikat pengguna dengan implementasi objek templat. Nah, yang terakhir dalam daftar ini adalah pola “Singleton”. Tujuannya sederhana - untuk menyediakan satu contoh objek untuk keseluruhan aplikasi. Pola ini menarik karena sering menunjukkan masalah multithreading. Untuk melihat lebih mendalam, lihat artikel ini:
Pola Desain di Java - 4

Pola struktural

Dengan terciptanya objek, hal itu menjadi lebih jelas. Sekaranglah waktunya untuk melihat pola struktural. Tujuan mereka adalah membangun hierarki kelas dan hubungan mereka yang mudah didukung. Salah satu pola yang pertama dan terkenal adalah “ Deputi ” (Proxy). Proksi memiliki antarmuka yang sama dengan objek sebenarnya, sehingga tidak ada bedanya bagi klien untuk bekerja melalui proksi atau secara langsung. Contoh paling sederhana adalah java.lang.reflect.Proxy :
import java.util.*;
import java.lang.reflect.*;
class Main {
  public static void main(String[] arguments) {
    final Map<String, String> original = new HashMap<>();
    InvocationHandler proxy = (obj, method, args) -> {
      System.out.println("Invoked: " + method.getName());
      return method.invoke(original, args);
    };
    Map<String, String> proxyInstance = (Map) Proxy.newProxyInstance(
        original.getClass().getClassLoader(),
        original.getClass().getInterfaces(),
        proxy);
    proxyInstance.put("key", "value");
    System.out.println(proxyInstance.get("key"));
  }
}
Seperti yang Anda lihat, dalam contoh kami memiliki yang asli - ini adalah salah satu HashMapyang mengimplementasikan antarmuka Map. Kami selanjutnya membuat proxy yang menggantikan yang asli HashMapuntuk bagian klien, yang memanggil metode putand get, menambahkan logika kami sendiri selama panggilan. Seperti yang bisa kita lihat, interaksi dalam pola terjadi melalui antarmuka. Namun terkadang pengganti saja tidak cukup. Dan kemudian pola " Dekorator " dapat digunakan. Dekorator disebut juga pembungkus atau pembungkus. Proxy dan dekorator sangat mirip, tetapi jika Anda melihat contohnya, Anda akan melihat perbedaannya:
import java.util.*;
class Main {
  public static void main(String[] arguments) {
    List<String> list = new ArrayList<>();
    List<String> decorated = Collections.checkedList(list, String.class);
    decorated.add("2");
    list.add("3");
    System.out.println(decorated);
  }
}
Tidak seperti proxy, dekorator membungkus dirinya di sekitar sesuatu yang diteruskan sebagai masukan. Proksi dapat menerima apa yang perlu diproksi dan juga mengatur masa pakai objek yang diproksi (misalnya, membuat objek yang diproksi). Ada pola menarik lainnya - “ Adaptor ”. Hal ini mirip dengan dekorator - dekorator mengambil satu objek sebagai masukan dan mengembalikan pembungkus objek ini. Bedanya, tujuannya bukan untuk mengubah fungsionalitas, melainkan untuk mengadaptasi satu antarmuka ke antarmuka lainnya. Java memiliki contoh yang sangat jelas mengenai hal ini:
import java.util.*;
class Main {
  public static void main(String[] arguments) {
    String[] array = {"One", "Two", "Three"};
    List<String> strings = Arrays.asList(array);
    strings.set(0, "1");
    System.out.println(Arrays.toString(array));
  }
}
Pada input kami memiliki array. Selanjutnya, kita membuat adaptor yang membawa array ke antarmuka List. Saat bekerja dengannya, kami sebenarnya bekerja dengan sebuah array. Oleh karena itu, menambahkan elemen tidak akan berfungsi, karena... Array asli tidak dapat diubah. Dan dalam hal ini kita akan mendapatkan UnsupportedOperationException. Pendekatan menarik berikutnya untuk mengembangkan struktur kelas adalah pola Komposit . Menariknya, sekumpulan elemen tertentu yang menggunakan satu antarmuka disusun dalam hierarki seperti pohon tertentu. Dengan memanggil metode pada elemen induk, kita mendapat panggilan ke metode ini pada semua elemen turunan yang diperlukan. Contoh utama dari pola ini adalah UI (baik java.awt atau JSF):
import java.awt.*;
class Main {
  public static void main(String[] arguments) {
    Container container = new Container();
    Component component = new java.awt.Component(){};
    System.out.println(component.getComponentOrientation().isLeftToRight());
    container.add(component);
    container.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
    System.out.println(component.getComponentOrientation().isLeftToRight());
  }
}
Seperti yang bisa kita lihat, kita telah menambahkan komponen ke container. Dan kemudian kami meminta wadah untuk menerapkan orientasi komponen yang baru. Dan container, mengetahui komponen apa saja yang ada di dalamnya, mendelegasikan eksekusi perintah ini ke semua komponen turunan. Pola menarik lainnya adalah pola “ Jembatan ”. Disebut demikian karena menggambarkan hubungan atau jembatan antara dua hierarki kelas yang berbeda. Salah satu hierarki ini dianggap sebagai abstraksi dan yang lainnya dianggap sebagai implementasi. Hal ini disorot karena abstraksi itu sendiri tidak melakukan tindakan, namun mendelegasikan eksekusi ini ke implementasi. Pola ini sering digunakan ketika terdapat kelas "kontrol" dan beberapa jenis kelas "platform" (misalnya Windows, Linux, dll.). Dengan pendekatan ini, salah satu hierarki ini (abstraksi) akan menerima referensi ke objek hierarki lain (implementasi) dan akan mendelegasikan pekerjaan utama kepada objek tersebut. Karena semua implementasi akan mengikuti antarmuka umum, maka implementasi tersebut dapat dipertukarkan dalam abstraksi. Di Jawa, contoh nyatanya adalah java.awt:
Pola Desain di Java - 5
Untuk informasi lebih lanjut, lihat artikel " Pola di Java AWT ". Di antara pola struktural, saya juga ingin mencatat pola “ Fasad ”. Esensinya adalah untuk menyembunyikan kompleksitas penggunaan perpustakaan/kerangka kerja di balik API ini di balik antarmuka yang nyaman dan ringkas. Misalnya, Anda dapat menggunakan JSF atau EntityManager dari JPA sebagai contoh. Ada juga pola lain yang disebut " Flyweight ". Esensinya adalah jika objek yang berbeda mempunyai keadaan yang sama, maka dapat digeneralisasikan dan disimpan tidak pada setiap objek, melainkan pada satu tempat. Dan kemudian setiap objek akan dapat mereferensikan bagian umum, yang akan mengurangi biaya memori untuk penyimpanan. Pola ini sering kali melibatkan pra-cache atau pemeliharaan kumpulan objek. Menariknya, kita juga mengetahui pola ini sejak awal:
Pola Desain di Java - 6
Dengan analogi yang sama, kumpulan string dapat dimasukkan di sini. Anda dapat membaca artikel tentang topik ini: " Pola Desain Kelas Terbang ".
Pola Desain di Java - 7

Pola perilaku

Jadi, kita mengetahui bagaimana objek dapat dibuat dan bagaimana hubungan antar kelas dapat diatur. Hal paling menarik yang tersisa adalah memberikan fleksibilitas dalam mengubah perilaku objek. Dan pola perilaku akan membantu kita dalam hal ini. Salah satu pola yang paling sering disebutkan adalah pola “ Strategi ”. Di sinilah kajian pola dalam buku “ Head First.Design Patterns ” dimulai. Dengan menggunakan pola "Strategi", kita dapat menyimpan di dalam objek bagaimana kita akan melakukan tindakan, mis. objek di dalamnya menyimpan strategi yang dapat diubah selama eksekusi kode. Ini adalah pola yang sering kita gunakan saat menggunakan pembanding:
import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
    Comparator<String> comparator = Comparator.comparingInt(String::length);
    Set dataSet = new TreeSet(comparator);
    dataSet.addAll(data);
    System.out.println("Dataset : " + dataSet);
  }
}
Sebelum kita - TreeSet. Ia memiliki perilaku TreeSetmenjaga urutan elemen, mis. mengurutkannya (karena ini adalah SortedSet). Perilaku ini memiliki strategi default, yang kita lihat di JavaDoc: mengurutkan dalam "pengurutan alami" (untuk string, ini adalah urutan leksikografis). Ini terjadi jika Anda menggunakan konstruktor tanpa parameter. Tapi kalau kita mau ubah strateginya, kita bisa lewati Comparator. Dalam contoh ini, kita dapat membuat kumpulan kita sebagai new TreeSet(comparator), dan kemudian urutan elemen penyimpanan (strategi penyimpanan) akan berubah ke yang ditentukan dalam pembanding. Menariknya, ada pola yang hampir sama yang disebut “ Negara ”. Pola “Status” mengatakan bahwa jika kita memiliki beberapa perilaku pada objek utama yang bergantung pada status objek tersebut, maka kita dapat mendeskripsikan status itu sendiri sebagai objek dan mengubah status objek tersebut. Dan mendelegasikan panggilan dari objek utama ke negara bagian. Pola lain yang kita ketahui dari mempelajari dasar-dasar bahasa Java adalah pola “ Perintah ”. Pola desain ini menunjukkan bahwa perintah yang berbeda dapat direpresentasikan sebagai kelas yang berbeda. Pola ini sangat mirip dengan pola Strategi. Namun dalam pola Strategi, kami mendefinisikan ulang bagaimana tindakan tertentu akan dilakukan (misalnya, mengurutkan TreeSet). Dalam pola “Perintah”, kami mendefinisikan ulang tindakan apa yang akan dilakukan. Perintah pattern ada bersama kita setiap hari ketika kita menggunakan thread:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Runnable command = () -> {
      System.out.println("Command action");
    };
    Thread th = new Thread(command);
    th.start();
  }
}
Seperti yang Anda lihat, command mendefinisikan tindakan atau perintah yang akan dieksekusi di thread baru. Pola “rantai tanggung jawab ” juga patut dipertimbangkan . Pola ini juga sangat sederhana. Pola ini mengatakan bahwa jika ada sesuatu yang perlu diproses, maka Anda dapat mengumpulkan penangan dalam sebuah rantai. Misalnya, pola ini sering digunakan di server web. Pada masukan, server memiliki beberapa permintaan dari pengguna. Permintaan ini kemudian melewati rantai pemrosesan. Rantai penangan ini mencakup filter (misalnya, tidak menerima permintaan dari daftar hitam alamat IP), penangan otentikasi (hanya mengizinkan pengguna yang berwenang), penangan header permintaan, penangan caching, dll. Tapi ada contoh yang lebih sederhana dan lebih mudah dimengerti di Java java.util.logging:
import java.util.logging.*;
class Main {
  public static void main(String[] args) {
    Logger logger = Logger.getLogger(Main.class.getName());
    ConsoleHandler consoleHandler = new ConsoleHandler(){
		@Override
            public void publish(LogRecord record) {
                System.out.println("LogRecord обработан");
            }
        };
    logger.addHandler(consoleHandler);
    logger.info("test");
  }
}
Seperti yang Anda lihat, Penangan ditambahkan ke daftar penangan logger. Ketika logger menerima pesan untuk diproses, setiap pesan tersebut melewati rantai penangan (dari logger.getHandlers) untuk logger tersebut. Pola lain yang kita lihat setiap hari adalah “ Iterator ”. Esensinya adalah untuk memisahkan kumpulan objek (yaitu kelas yang mewakili struktur data. Misalnya, List) dan traversal dari kumpulan ini.
import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
    Iterator<String> iterator = data.iterator();
    while (iterator.hasNext()) {
      System.out.println(iterator.next());
    }
  }
}
Seperti yang Anda lihat, iterator bukan bagian dari koleksi, namun diwakili oleh kelas terpisah yang melintasi koleksi. Pengguna iterator bahkan mungkin tidak mengetahui koleksi apa yang sedang diiterasi, mis. koleksi apa yang dia kunjungi? Pola Pengunjung juga patut dipertimbangkan . Pola pengunjung sangat mirip dengan pola iterator. Pola ini membantu Anda melewati struktur objek dan melakukan tindakan pada objek tersebut. Konsepnya agak berbeda. Iterator melintasi koleksi sehingga klien yang menggunakan iterator tidak peduli koleksi apa yang ada di dalamnya, hanya elemen dalam urutan yang penting. Pengunjung maksudnya ada hierarki atau struktur tertentu dari objek yang kita kunjungi. Misalnya, kita dapat menggunakan pemrosesan direktori terpisah dan pemrosesan file terpisah. Java mempunyai implementasi out-of-the-box dari pola ini dalam bentuk java.nio.file.FileVisitor:
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.*;
class Main {
  public static void main(String[] args) {
    SimpleFileVisitor visitor = new SimpleFileVisitor() {
      @Override
      public FileVisitResult visitFile(Object file, BasicFileAttributes attrs) throws IOException {
        System.out.println("File:" + file.toString());
        return FileVisitResult.CONTINUE;
      }
    };
    Path pathSource = Paths.get(System.getProperty("java.io.tmpdir"));
    try {
      Files.walkFileTree(pathSource, visitor);
    } catch (AccessDeniedException e) {
      // skip
    } catch (IOException e) {
      // Do something
    }
  }
}
Terkadang ada kebutuhan bagi beberapa objek untuk bereaksi terhadap perubahan pada objek lain, dan kemudian pola “Pengamat” akan membantu kita . Cara paling mudah adalah dengan menyediakan mekanisme berlangganan yang memungkinkan beberapa objek memantau dan merespons peristiwa yang terjadi di objek lain. Pola ini sering digunakan pada berbagai Pendengar dan Pengamat yang bereaksi terhadap peristiwa berbeda. Sebagai contoh sederhana, kita dapat mengingat implementasi pola ini dari JDK versi pertama:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Observer observer = (obj, arg) -> {
      System.out.println("Arg: " + arg);
    };
    Observable target = new Observable(){
      @Override
      public void notifyObservers(Object arg) {
        setChanged();
        super.notifyObservers(arg);
      }
    };
    target.addObserver(observer);
    target.notifyObservers("Hello, World!");
  }
}
Ada pola perilaku lain yang berguna - “ Mediator ”. Hal ini berguna karena dalam sistem yang kompleks membantu menghilangkan hubungan antara objek yang berbeda dan mendelegasikan semua interaksi antar objek ke beberapa objek, yang merupakan perantara. Salah satu aplikasi paling mencolok dari pola ini adalah Spring MVC, yang menggunakan pola ini. Anda dapat membaca lebih lanjut tentang ini di sini: " Musim Semi: Pola Mediator ". Anda sering dapat melihat hal yang sama dalam contoh java.util.Timer:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Timer mediator = new Timer("Mediator");
    TimerTask command = new TimerTask() {
      @Override
      public void run() {
        System.out.println("Command pattern");
        mediator.cancel();
      }
    };
    mediator.schedule(command, 1000);
  }
}
Contohnya lebih mirip pola perintah. Dan inti dari pola “Mediator” tersembunyi dalam implementasi Timer'a. Di dalam timer ada antrian tugas TaskQueue, ada thread TimerThread. Kami, sebagai klien kelas ini, tidak berinteraksi dengan mereka, tetapi berinteraksi dengan Timerobjek, yang, sebagai respons terhadap panggilan kami ke metodenya, mengakses metode objek lain yang menjadi perantaranya. Secara lahiriah mungkin tampak sangat mirip dengan "Fasad". Namun yang membedakan adalah ketika Facade digunakan, komponen-komponennya tidak mengetahui keberadaan Facade tersebut dan saling berbicara satu sama lain. Dan ketika “Mediator” digunakan, komponen-komponen tersebut mengetahui dan menggunakan perantara tersebut, namun tidak saling menghubungi secara langsung. Perlu dipertimbangkan pola “ Metode Templat ”, polanya jelas dari namanya. Intinya adalah bahwa kode tersebut ditulis sedemikian rupa sehingga pengguna kode (pengembang) diberikan beberapa templat algoritma, yang langkah-langkahnya dapat didefinisikan ulang. Hal ini memungkinkan pengguna kode untuk tidak menulis keseluruhan algoritme, tetapi hanya memikirkan cara melakukan satu atau beberapa langkah algoritme ini dengan benar. Misalnya, Java memiliki kelas abstrak AbstractListyang mendefinisikan perilaku iterator dengan List. Namun, iteratornya sendiri menggunakan metode daun seperti: get, set, remove. Perilaku metode ini ditentukan oleh pengembang turunannya AbstractList. Jadi, iterator di AbstractList- adalah templat untuk algoritma iterasi pada sheet. Dan pengembang implementasi spesifik AbstractListmengubah perilaku iterasi ini dengan mendefinisikan perilaku langkah-langkah spesifik. Pola terakhir yang kami analisa adalah pola “ Snapshot ” (Momento). Esensinya adalah untuk mempertahankan keadaan tertentu dari suatu objek dengan kemampuan untuk memulihkan keadaan tersebut. Contoh yang paling dikenal dari JDK adalah serialisasi objek, yaitu. java.io.Serializable. Mari kita lihat sebuah contoh:
import java.io.*;
import java.util.*;
class Main {
  public static void main(String[] args) throws IOException {
    ArrayList<String> list = new ArrayList<>();
    list.add("test");
    // Save State
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    try (ObjectOutputStream out = new ObjectOutputStream(stream)) {
      out.writeObject(list);
    }
    // Load state
    byte[] bytes = stream.toByteArray();
    InputStream inputStream = new ByteArrayInputStream(bytes);
    try (ObjectInputStream in = new ObjectInputStream(inputStream)) {
      List<String> listNew = (List<String>) in.readObject();
      System.out.println(listNew.get(0));
    } catch (ClassNotFoundException e) {
      // Do something. Can't find class fpr saved state
    }
  }
}
Pola Desain di Java - 8

Kesimpulan

Seperti yang kita lihat dari ulasan, ada banyak variasi pola. Masing-masing dari mereka memecahkan masalahnya sendiri. Dan mengetahui pola-pola ini dapat membantu Anda memahami pada waktunya bagaimana menulis sistem Anda agar fleksibel, dapat dipelihara, dan tahan terhadap perubahan. Dan terakhir, beberapa tautan untuk menyelami lebih dalam: #Viacheslav
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION