Apa nama dari pola (anti) berikut? Apa kelebihan dan kekurangannya?

28

Selama beberapa bulan terakhir, saya tersandung beberapa kali selama teknik / pola berikut. Namun, sepertinya saya tidak dapat menemukan nama tertentu, saya juga tidak 100% yakin tentang semua kelebihan dan kekurangannya.

Polanya berbunyi sebagai berikut:

Dalam antarmuka Java, seperangkat metode umum didefinisikan seperti biasa. Namun, menggunakan kelas dalam, instance default bocor melalui antarmuka.

public interface Vehicle {
    public void accelerate();
    public void decelerate();

    public static class Default {
         public static Vehicle getInstance() {
             return new Car(); // or use Spring to retrieve an instance
         }
    }
 }

Bagi saya, tampaknya keuntungan terbesar terletak pada kenyataan bahwa seorang pengembang hanya perlu tahu tentang antarmuka dan bukan implementasinya, misalnya jika dia ingin segera membuat contoh.

 Vehicle someVehicle = Vehicle.Default.getInstance();
 someVehicle.accelerate();

Selain itu, saya telah melihat teknik ini digunakan bersama-sama dengan Spring untuk secara dinamis memberikan contoh tergantung pada konfigurasi. Dalam hal ini, sepertinya ini juga dapat membantu dengan modularisasi.

Namun demikian, saya tidak dapat menghilangkan perasaan bahwa ini adalah penyalahgunaan antarmuka karena menggabungkan antarmuka dengan salah satu implementasinya. (Prinsip inversi ketergantungan dll.) Adakah yang bisa menjelaskan kepada saya bagaimana teknik ini disebut, serta kelebihan & kekurangannya?

Memperbarui:

Setelah beberapa waktu untuk dipertimbangkan, saya memeriksa kembali dan memperhatikan bahwa versi pola singleton berikut ini digunakan jauh lebih sering. Dalam versi ini, instance statis publik diekspos melalui antarmuka yang diinisialisasi hanya sekali (karena bidangnya final). Selain itu, instance hampir selalu diambil menggunakan Spring atau pabrik generik yang memisahkan antarmuka dari implementasi.

public interface Vehicle {
      public void accelerate();
      public void decelerate();

      public static class Default {
           public static final Vehicle INSTANCE = getInstance();

           private static Vehicle getInstance() {
                return new Car(); // or use Spring/factory here
           }
      }
 }

 // Which allows to retrieve a singleton instance using...
 Vehicle someVehicle = Vehicle.Default.INSTANCE;

Singkatnya: tampaknya ini adalah pola pabrik tunggal / custom, yang pada dasarnya memungkinkan untuk mengekspos instance atau singleton melalui antarmuka-nya. Sehubungan dengan kerugian, beberapa telah disebutkan dalam jawaban & komentar di bawah ini. Sejauh ini, keuntungan tampaknya terletak pada kenyamanannya.

Jérôme
sumber
semacam pola tunggal?
Bryan Chen
Saya pikir Anda benar bahwa antarmuka tidak boleh "tahu" tentang implementasinya. Mungkin Vehicle.Defaultharus dipindahkan ke namespace paket sebagai kelas pabrik, mis VehicleFactory.
augurar
7
Ngomong-ngomong,Vehicle.Default.getInstance() != Vehicle.Default.getInstance()
Konrad Morawski
20
Antipattern di sini adalah menggunakan antipattern analogi mobil, yang terkait erat dengan antipattern analogi hewan. Sebagian besar perangkat lunak tidak memiliki roda atau kaki.
Pieter B
@PieterB: :-)))) Anda membuat hari saya!
Doc Brown

Jawaban:

24

Default.getInstanceadalah IMHO hanya bentuk yang sangat spesifik dari metode pabrik , dicampur dengan konvensi penamaan yang diambil dari pola tunggal (tetapi tanpa implementasi yang terakhir). Dalam bentuk saat ini, ini merupakan pelanggaran terhadap " prinsip tanggung jawab tunggal ", karena antarmuka (yang sudah melayani tujuan mendeklarasikan API kelas) mengambil tanggung jawab tambahan untuk menyediakan contoh default, yang akan jauh lebih baik ditempatkan di tempat terpisah. VehicleFactorykelas.

Masalah utama yang disebabkan oleh konstruk ini adalah bahwa ia menginduksi ketergantungan siklik antara Vehicledan Car. Misalnya, dalam formulir saat ini tidak akan mungkin untuk ditempatkan Vehicledi satu perpustakaan, dan Cardi yang lain. Menggunakan kelas pabrik yang terpisah dan menempatkan Default.getInstancemetode di sana akan menyelesaikan masalah itu. Mungkin juga merupakan ide yang baik untuk memberikan nama yang berbeda pada metode itu, untuk mencegah kebingungan terkait dengan lajang. Jadi hasilnya bisa seperti

VehicleFactory.createDefaultInstance()

Doc Brown
sumber
Terima kasih atas jawaban anda! Saya setuju bahwa contoh khusus ini merupakan pelanggaran terhadap SRP. Namun, saya tidak yakin apakah ini masih merupakan pelanggaran jika penerapannya diambil secara dinamis. Misalnya dengan menggunakan Spring (atau kerangka kerja serupa) untuk mengambil contoh tertentu yang didefinisikan oleh misalnya file konfigurasi.
Jérôme
1
@ Jérôme: IMHO, kelas non-bersarang yang dinamai VehicleFactorylebih konvensional daripada konstruk bersarang Vehicle.Default, terutama dengan metode "getInstance" yang menyesatkan. Meskipun mungkin untuk menghindari ketergantungan siklik dengan menggunakan Spring, saya pribadi lebih suka varian pertama hanya karena akal sehat. Dan, tetap saja memberi Anda pilihan untuk memindahkan pabrik ke komponen yang berbeda, mengubah implementasi setelahnya untuk bekerja tanpa Spring dll.
Doc Brown
Seringkali, alasan untuk menggunakan antarmuka daripada kelas adalah untuk memungkinkan kelas yang memiliki tujuan lain dapat digunakan oleh kode yang membutuhkan implementasi antarmuka. Jika kelas implementasi standar dapat memenuhi semua kebutuhan kode yang hanya membutuhkan sesuatu yang mengimplementasikan antarmuka dalam mode "normal", menentukan cara membuat instance akan tampak hal yang berguna untuk dilakukan antarmuka.
supercat
Jika Mobil adalah kendaraan standar yang sangat generik, ini bukan pelanggaran SRP. Kendaraan masih hanya memiliki satu alasan untuk berubah, misalnya ketika Google menambahkan autodrive()metode. Mobil Anda yang sangat generik kemudian harus berubah untuk menerapkan metode itu, misalnya dengan melempar NotImplementedException, tetapi itu tidak memberi alasan tambahan untuk berubah pada Kendaraan.
user949300
@ user949300: SRP bukan konsep yang "bisa dibuktikan secara matematis". Dan keputusan desain perangkat lunak tidak selalu "hitam putih". Kode asli tidak begitu buruk sehingga tidak dapat digunakan dalam kode produksi, tentu saja, dan mungkin ada kasus-kasus di mana kenyamanan melebihi ketergantungan siklik, seperti yang telah dicatat oleh OP sendiri dalam "pembaruan" -nya. Jadi tolong baca jawaban saya sebagai "pertimbangkan jika menggunakan kelas pabrik yang terpisah tidak membantu Anda dengan lebih baik". Perhatikan juga penulis mengubah pertanyaan aslinya sedikit setelah saya memberikan jawaban saya.
Doc Brown
3

Ini terlihat seperti pola Objek Null bagi saya.

Tetapi itu sangat tergantung pada bagaimana Carkelas diimplementasikan. Jika diimplementasikan dengan cara "netral", maka itu jelas merupakan Objek Null. Itu berarti, jika Anda dapat menghapus semua pemeriksaan nol pada antarmuka dan membuat instance kelas ini alih-alih setiap kemunculannya null, maka kode masih harus bekerja dengan benar.

Juga jika ini adalah pola ini, maka tidak ada masalah untuk membuat kopling ketat antara antarmuka itu sendiri dan implementasi objek nol. Karena objek nol bisa ada di mana-mana di mana antarmuka kata digunakan

Euforia
sumber
2
Halo dan terima kasih atas jawabannya. Meskipun saya cukup yakin bahwa dalam kasus saya ini tidak digunakan untuk menerapkan pola "Objek Null", ini adalah penjelasan yang menarik.
Jérôme