Terminologi: Saya akan merujuk pada konstruksi bahasa interface
sebagai antarmuka , dan antarmuka jenis atau objek sebagai permukaan (karena tidak ada istilah yang lebih baik).
Kopling longgar dapat dicapai dengan memiliki objek bergantung pada abstraksi bukan jenis beton.
Benar.
Hal ini memungkinkan untuk kopling longgar karena dua alasan utama: 1 - abstraksi lebih kecil kemungkinannya untuk berubah daripada jenis beton, yang berarti kode dependen lebih kecil kemungkinannya untuk dipatahkan. 2 - tipe beton yang berbeda dapat digunakan saat runtime, karena semuanya sesuai dengan abstraksi. Jenis beton baru juga dapat ditambahkan nanti tanpa perlu mengubah kode dependen yang ada.
Tidak sepenuhnya benar. Bahasa saat ini umumnya tidak mengantisipasi bahwa abstraksi akan berubah (walaupun ada beberapa pola desain untuk mengatasinya). Memisahkan spesifik dari hal - hal umum adalah abstraksi. Ini biasanya dilakukan oleh beberapa lapisan abstraksi . Lapisan ini dapat diubah ke beberapa spesifik lain tanpa melanggar kode yang dibangun di atas abstraksi ini - kopling longgar tercapai. Contoh Non-OOP: Suatu sort
rutin dapat diubah dari Quicksort di versi 1 ke Tim Sort di versi 2. Kode yang hanya tergantung pada hasil yang diurutkan (yaitu dibangun berdasarkan sort
abstraksi) karena itu dipisahkan dari implementasi penyortiran yang sebenarnya.
Apa yang saya sebut permukaan di atas adalah bagian umum dari abstraksi. Sekarang terjadi di OOP bahwa satu objek terkadang harus mendukung banyak abstraksi. Contoh yang tidak terlalu optimal: Java java.util.LinkedList
mendukung kedua List
antarmuka yaitu tentang abstraksi "teratur, dapat diindeks koleksi", dan mendukung Queue
antarmuka yang (secara kasar) adalah tentang abstraksi "FIFO".
Bagaimana suatu objek dapat mendukung banyak abstraksi?
C ++ tidak memiliki antarmuka, tetapi memiliki banyak pewarisan, metode virtual, dan kelas abstrak. Abstraksi kemudian dapat didefinisikan sebagai kelas abstrak (yaitu kelas yang tidak dapat segera dipakai) yang menyatakan, tetapi tidak mendefinisikan metode virtual. Kelas yang mengimplementasikan spesifik abstraksi kemudian dapat mewarisi dari kelas abstrak dan mengimplementasikan metode virtual yang diperlukan.
Masalahnya di sini adalah bahwa pewarisan berganda dapat menyebabkan masalah intan , di mana urutan di mana kelas dicari untuk implementasi metode (MRO: metode resolusi urutan) dapat menyebabkan "kontradiksi". Ada dua tanggapan untuk ini:
Tetapkan perintah yang waras dan tolak perintah yang tidak bisa diluruskan dengan bijaksana. The C3 MRO cukup masuk akal dan bekerja dengan baik. Itu diterbitkan tahun 1996.
Ambil rute yang mudah dan tolak beberapa warisan di seluruh.
Java mengambil opsi yang terakhir dan memilih warisan perilaku tunggal. Namun, kita masih membutuhkan kemampuan suatu objek untuk mendukung banyak abstraksi. Oleh karena itu, antarmuka harus digunakan yang tidak mendukung definisi metode, hanya deklarasi.
Hasilnya adalah bahwa MRO jelas (lihat saja setiap superclass secara berurutan), dan bahwa objek kita dapat memiliki beberapa permukaan untuk sejumlah abstraksi.
Ini ternyata agak tidak memuaskan, karena cukup sering sedikit perilaku adalah bagian dari permukaan. Pertimbangkan sebuah Comparable
antarmuka:
interface Comparable<T> {
public int cmp(T that);
public boolean lt(T that); // less than
public boolean le(T that); // less than or equal
public boolean eq(T that); // equal
public boolean ne(T that); // not equal
public boolean ge(T that); // greater than or equal
public boolean gt(T that); // greater than
}
Ini sangat user-friendly (API yang bagus dengan banyak metode yang mudah), tetapi membosankan untuk diimplementasikan. Kami ingin antarmuka hanya menyertakan cmp
, dan menerapkan metode lain secara otomatis dalam hal satu metode yang diperlukan. Mixin , tetapi yang lebih penting Ciri-ciri [ 1 ], [ 2 ] menyelesaikan masalah ini tanpa jatuh ke dalam perangkap pewarisan berganda.
Ini dilakukan dengan mendefinisikan komposisi sifat sehingga sifat-sifat tersebut tidak benar-benar berakhir mengambil bagian dalam MRO - sebagai gantinya metode yang didefinisikan disusun ke dalam kelas pelaksana.
The Comparable
antarmuka dapat dinyatakan dalam Scala sebagai
trait Comparable[T] {
def cmp(that: T): Int
def lt(that: T): Boolean = this.cmp(that) < 0
def le(that: T): Boolean = this.cmp(that) <= 0
...
}
Ketika sebuah kelas kemudian menggunakan sifat itu, metode lain ditambahkan ke definisi kelas:
// "extends" isn't different from Java's "implements" in this case
case class Inty(val x: Int) extends Comparable[Inty] {
override def cmp(that: Inty) = this.x - that.x
// lt etc. get added automatically
}
Begitu Inty(4) cmp Inty(6)
juga akan -2
dan Inty(4) lt Inty(6)
akan terjadi true
.
Banyak bahasa memiliki beberapa dukungan untuk sifat-sifat, dan bahasa apa pun yang memiliki "Metaobject Protocol (MOP)" dapat memiliki sifat ditambahkan ke dalamnya. Pembaruan Java 8 baru-baru ini menambahkan metode default yang mirip dengan ciri-ciri (metode dalam antarmuka dapat memiliki implementasi fallback sehingga opsional untuk menerapkan kelas untuk mengimplementasikan metode ini).
Sayangnya, sifat-sifat adalah penemuan yang cukup baru (2002), dan dengan demikian cukup langka dalam bahasa arus utama yang lebih besar.