Apakah "antarmuka statis" adalah praktik yang baik?

13

Saya baru saja memperhatikan ada pilihan untuk memiliki metode statis di antarmuka. Sama seperti dengan bidang antarmuka statis, ada perilaku yang menarik: Ini tidak diwariskan.

Saya tidak yakin apakah ini berguna dalam antarmuka aktual yang akan diterapkan. Namun, ini memungkinkan programmer untuk membuat antarmuka yang hanya mencakup hal-hal statis, seperti misalnya kelas utilitas.

Contoh sederhana hanyalah sebuah amplop untuk konstanta global. Dibandingkan dengan kelas, Anda dapat dengan mudah melihat pelat ketel yang hilang public static finalseperti yang diasumsikan (membuatnya kurang bertele-tele).

public interface Constants {
    String LOG_MESSAGE_FAILURE = "I took an arrow to the knee.";
    int DEFAULT_TIMEOUT_MS = 30;
}

Anda juga bisa membuat sesuatu yang lebih kompleks, seperti pseudo-enum kunci konfigurasi ini.

public interface ConfigKeys {
    static createValues(ConfigKey<?>... values) {
        return Collections.unmodifiableSet(new HashSet(Arrays.asList(values)));
    }

    static ConfigKey<T> key(Class<T> clazz) {
        return new ConfigKey<>(clazz);
    }

    class ConfigKey<T> {
        private final Class<T> type;
        private ConfigKey(Class<T> type) {
            this.type = type;
        }
        private Class<T> getType() {
            return type;
        }
    }
}

import static ConfigKeys.*;
public interface MyAppConfigKeys {
    ConfigKey<Boolean> TEST_MODE = key(Boolean.class);
    ConfigKey<String> COMPANY_NAME = key(String.class);

    Set<ConfigKey<?>> VALUES = createValues(TEST_MODE, COMPANY_VALUE);

    static values() {
        return VALUES;
    }
}

Anda juga dapat membuat beberapa "kelas" utilitas dengan cara ini. Namun, dalam utilitas, seringkali berguna untuk menggunakan metode pembantu pribadi atau terlindungi, yang tidak mungkin dilakukan di kelas.

Saya menganggapnya sebagai fitur baru yang bagus, dan terutama fakta bahwa anggota statis tidak diwarisi adalah konsep yang menarik yang diperkenalkan ke antarmuka saja.

Saya ingin tahu apakah Anda dapat menganggapnya sebagai praktik yang baik. Sementara gaya kode dan praktik terbaik tidak aksiomatis dan ada ruang untuk berpendapat, saya pikir biasanya ada alasan yang sah untuk mendukung pendapat tersebut.

Saya lebih tertarik dengan alasan (tidak) menggunakan pola seperti ini.


Perhatikan bahwa saya tidak bermaksud mengimplementasikan antarmuka tersebut. Mereka hanyalah sebuah amplop untuk konten statis mereka. Saya hanya bermaksud menggunakan konstanta atau metode dan mungkin menggunakan impor statis.

Vlasec
sumber
1
Reaksi brengsek lutut saya untuk ini adalah bahwa hal itu hanya dapat menyebabkan hal-hal buruk. Meskipun metode statis publik pada dasarnya hanya fungsi global namespace. Rasanya seperti mengizinkan kode implementasi apa pun dalam Antarmuka bertentangan dengan tujuannya sebagai definisi tidak adanya implementasi kontrak. Saya katakan biarkan implementasi parsial untuk kelas Abstrak. Saya perlu membaca mengapa ini ditambahkan.
Adrian
Aku harus pergi sekarang, tapi besok aku akan menulis sesuatu. Singkatnya adalah bahwa sebelum Interface memiliki tujuan yang jelas yang jelas terpisah dari tujuan AbstractClass. Sekarang mereka tumpang tindih dengan cara yang melanggar definisi bahasa agnostik Antarmuka. Selain itu ada beberapa kasus di mana Anda tidak dapat mewarisi dari banyak antarmuka lagi jika mereka menerapkan metode standar. Jadi sekarang ada pengecualian untuk beberapa antarmuka pewarisan di mana sebelumnya tidak ada. Karena Java telah menyimpang secara drastis dari definisi umum sebuah antarmuka ...
Adrian
itu juga dapat membingungkan pengembang baru yang mencoba untuk mendapatkan pegangan yang tepat pada konsep OOP mendasar seperti Antarmuka, AbstractClases, kapan harus menggunakan komposisi, bukan warisan, ... dll.
Adrian
Aliester, Anda berbicara tentang defaultmetode. Saya berbicara tentang staticmetode dan bidang. Itu tidak diwariskan, jadi mereka tidak merusak banyak warisan.
Vlasec

Jawaban:

1

Contoh sederhana hanyalah sebuah amplop untuk konstanta global.

Menempatkan konstanta pada antarmuka disebut Constant Interface Antipatternkonstanta karena seringkali hanya sekedar detail implementasi. Kelemahan lebih lanjut dirangkum dalam artikel Wikipedia pada antarmuka Konstan .

Anda juga dapat membuat beberapa "kelas" utilitas dengan cara ini.

Memang dokumen Java menyatakan bahwa metode statis dapat membuatnya lebih mudah untuk mengatur metode helper, misalnya ketika memanggil metode helper dari metode default.

Markus Pscheidt
sumber
1
Saya tidak berpikir ini sebenarnya adalah Jawaban atas pertanyaan yang diajukan karena Anda bisa melakukan kedua hal ini tanpa fungsi baru ini.
Adrian
Ah, jadi menurut dokumentasi, ini dimaksudkan sebagai metode pembantu. Namun, itu juga bersifat publik, yang bertentangan dengannya. Jadi mungkin ini dimaksudkan juga untuk membantu kelas yang mengimplementasikan antarmuka. Tetapi mereka tidak diwariskan, jadi Anda harus menggunakan impor statis untuk membuatnya merasa seperti Anda mewarisinya. Yah, terima kasih sudah mencarinya di dokumen.
Vlasec
2
Sama sekali tidak ada yang salah dengan menempatkan konstanta di antarmuka, jika konstanta tersebut entah bagaimana merupakan bagian dari API yang dijelaskan oleh antarmuka. Satu-satunya hal yang merupakan antipattern adalah menempatkan konstanta ke antarmuka tanpa metode dan membuat kelas mengimplementasikan antarmuka hanya untuk merujuk ke konstanta tanpa awalan kelas. Ini merupakan penyalahgunaan antarmuka dan, sejak diperkenalkannya impor statis, sepenuhnya usang.
Michael Borgwardt
1

Seperti yang dinyatakan dalam pertanyaan, antarmuka tidak dimaksudkan untuk diimplementasikan (atau dipakai). Jadi kita bisa membandingkannya dengan kelas yang memiliki konstruktor pribadi:

  • Kelas tidak dapat diperpanjang tanpa super()untuk memanggil, tetapi antarmuka dapat "diimplementasikan"
  • Kelas tidak dapat di-instantiated tanpa refleksi, sementara antarmuka dapat dengan mudah dipakai sebagai kelas anonim sesederhana ini: new MyInterface() {};

Perbandingan ini mendukung kelas karena memungkinkan lebih banyak kontrol. Yang mengatakan, kelas tidak dimaksudkan untuk menjadi pemegang barang-barang statis, Anda akan mengharapkannya memiliki instance. Ya, tidak ada pilihan yang sempurna.

Ada juga hal aneh tentang warisan dari "antarmuka statis":

  • Kelas "implementing" mewarisi bidang, tetapi tidak mewarisi metode statis
  • Antarmuka yang diperluas tidak mewarisi anggota statis sama sekali
  • (Sementara kelas yang memperluas kelas mewarisi setiap anggota non-pribadi ... dan karena itu tidak dapat diperpanjang dengan antarmuka, ada lebih sedikit ruang untuk kebingungan)

Ini mungkin alasan untuk menganggapnya sebagai pola yang buruk dan tetap menggunakan kelas statis untuk kasus ini. Namun, bagi seseorang yang kecanduan gula sintaksis mungkin masih menggoda bahkan dengan kerugiannya.

Vlasec
sumber
... Bagaimanapun, konvensi pengkodean tim dapat membahas masalah-masalah itu. Bagi saya, sementara itu tidak mengikat tangan programmer hampir sekuat kelas dengan konstruktor pribadi, masih menanggung risiko lebih sedikit daripada setter lakukan, dan setter sering digunakan.
Vlasec
0

Sebagai @MarkusPscheidt ide ini pada dasarnya adalah perpanjangan dari antipattern Interface Konstan . Pendekatan ini awalnya digunakan secara luas untuk memungkinkan satu set konstanta diwarisi oleh banyak kelas yang berbeda. Alasan mengapa ini dianggap sebagai antipattern adalah karena masalah yang ditemui menggunakan pendekatan ini dalam proyek nyata. Saya tidak melihat alasan untuk berpikir bahwa masalah ini hanya akan berhubungan dengan konstanta.

Mungkin yang lebih penting, benar-benar tidak perlu melakukan ini. Gunakan impor statis sebagai gantinya dan secara eksplisit tentang apa yang Anda impor dan dari mana.

JimmyJames
sumber
Ya, impor statis terdengar seperti ide yang lebih baik daripada pewarisan konstanta dan berfungsi yang bekerja dengan anggota statis dari kedua kelas dan antarmuka. Dan itu terlihat di dalam kelas / antarmuka saja.
Vlasec
@Vecec Benar tetapi tidak perlu melakukan ini pada antarmuka. Satu-satunya perbedaan antara melakukan ini dengan kelas dan antarmuka adalah bahwa Anda dapat mewarisi dari lebih dari satu antarmuka. Praktik standar untuk kelas utilitas seperti ini adalah menjadikan konstruktor sebagai pribadi untuk mencegah instantiasi atau ekstensi. Tidak ada manfaatnya melakukan ini dalam antarmuka di atas kelas. Itu hanya membuka pintu untuk penyalahgunaan.
JimmyJames
Saya bisa melihat nilai dalam konstanta pada antarmuka. Antarmuka cenderung mendefinisikan metode dan metode terkadang menerima konstanta sebagai parameter. Misalnya: interface ColorFilter {Image applyGradient(int colorSpace, int width, byte[] pixels); }. Saya bisa membayangkan ada menjadi beberapa konstanta yang berbeda RGB, RGBA, CMYK, GREYSCALE, INDEXEDdll
Stijn de Witt
@StijndeWitt Ini bukan yang terburuk tetapi Anda dapat menggunakan kelas enum atau bersarang di antarmuka untuk tujuan ini. Masalah sebenarnya adalah menggunakan ini untuk membawa statika ke dalam ruang lingkup banyak kelas melalui pewarisan. Pendekatan itu jelas lebih rendah daripada menggunakan impor statis.
JimmyJames
0

Saya akan mengatakan, jika Anda tertarik tentang cara menggunakan fitur baru dengan benar, lihat kode di JDK. Metode default pada antarmuka sangat banyak digunakan dan mudah ditemukan, tetapi metode statis pada antarmuka tampaknya sangat jarang. Beberapa contoh termasuk java.time.chrono.Chronology, java.util.Comparator, java.util.Map, dan beberapa interface dalam java.util.functiondan java.util.stream.

Sebagian besar contoh yang saya lihat melibatkan penggunaan API yang cenderung sangat umum: konversi antara berbagai jenis dalam API waktu, misalnya, untuk perbandingan yang cenderung sering dilakukan antara objek yang diteruskan ke fungsi atau objek yang terkandung dalam koleksi. Takeaway saya: jika ada bagian dari API Anda yang harus fleksibel, tetapi Anda dapat menutupi mengatakan 80% kasus penggunaan dengan beberapa standar, pertimbangkan untuk menggunakan metode statis pada antarmuka Anda. Mengingat betapa jarangnya fitur ini tampaknya digunakan di JDK, saya akan sangat konservatif ketika memanfaatkannya dalam kode saya sendiri.

Contoh Anda tidak terlihat seperti apa yang saya lihat di JDK. Untuk kasus-kasus tertentu, Anda hampir pasti akan membuat enumyang mengimplementasikan antarmuka yang diinginkan. Antarmuka menggambarkan seperangkat perilaku dan benar-benar tidak memiliki bisnis mempertahankan koleksi implementasinya.

ngreen
sumber
-1

Saya ingin tahu apakah ada alasan untuk tidak menggunakannya.

Bagaimana dengan, um, kompatibilitas dengan JVM lama?

Apakah Anda menganggap ini ide yang baik secara umum?

Tidak. Ini adalah perubahan yang tidak kompatibel ke belakang yang menambah zilch pada ekspresivitas bahasa. Dua jempol ke bawah.

Apakah ada beberapa model situasi di mana Anda sangat merekomendasikan kelas?

Saya sangat merekomendasikan menggunakan kelas untuk memuat detail implementasi. Antarmuka benar-benar hanya dimaksudkan untuk mengatakan "Objek ini mendukung operasi XYZ" dan itu tidak ada hubungannya dengan metode statis apa pun yang mungkin Anda pikirkan untuk dimasukkan ke dalam deklarasi antarmuka. Fitur ini seperti kursi malas dengan kulkas mini bawaan. Tentu itu memiliki beberapa kegunaan niche tetapi tidak menarik cukup berat untuk pantas menjadi arus utama.

UPDATE: Harap tidak ada lagi downvotes. Jelas beberapa orang berpikir metode statis di antarmuka adalah lutut lebah, dan saya menyerah. Saya akan segera menghapus jawaban ini tetapi komentar yang terlampir adalah diskusi yang menarik.

DepresiDaniel
sumber
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
maple_shaft
Jika saya menurunkan pertanyaan Anda, saya akan melakukannya karena dua jawaban pertama kami bukanlah jawaban yang nyata. Versi bahasa baru membawa alat baru untuk mempermudah, itulah tujuan dari versi baru ini. Jawaban ketiga agak hilang dalam puing-puing dua yang rusak pertama.
Vlasec