Ada cara yang cukup "standar" untuk menyandikan tipe penjumlahan ke bahasa berorientasi objek.
Inilah dua contoh:
type Either<'a, 'b> = Left of 'a | Right of 'b
Dalam C #, kita dapat menguraikan ini sebagai:
interface Either<A, B> {
C Match<C>(Func<A, C> left, Func<B, C> right);
}
class Left<A, B> : Either<A, B> {
private readonly A a;
public Left(A a) { this.a = a; }
public C Match<C>(Func<A, C> left, Func<B, C> right) {
return left(a);
}
}
class Right<A, B> : Either<A, B> {
private readonly B b;
public Right(B b) { this.b = b; }
public C Match<C>(Func<A, C> left, Func<B, C> right) {
return right(b);
}
}
F # lagi:
type List<'a> = Nil | Cons of 'a * List<'a>
C # lagi:
interface List<A> {
B Match<B>(B nil, Func<A, List<A>, B> cons);
}
class Nil<A> : List<A> {
public Nil() {}
public B Match<B>(B nil, Func<A, List<A>, B> cons) {
return nil;
}
}
class Cons<A> : List<A> {
private readonly A head;
private readonly List<A> tail;
public Cons(A head, List<A> tail) {
this.head = head;
this.tail = tail;
}
public B Match<B>(B nil, Func<A, List<A>, B> cons) {
return cons(head, tail);
}
}
Pengkodean sepenuhnya mekanis. Pengkodean ini menghasilkan hasil yang memiliki sebagian besar kelebihan dan kekurangan yang sama dari tipe data aljabar. Anda juga dapat mengenali ini sebagai variasi dari Pola Pengunjung. Kami bisa mengumpulkan parameter untuk Match
bersama-sama menjadi antarmuka yang bisa kami sebut Pengunjung.
Di sisi keuntungan, ini memberi Anda pengkodean jumlah jenis berprinsip. (Ini adalah pengkodean Scott .) Ini memberi Anda "pencocokan pola" yang lengkap meskipun hanya satu "lapisan" yang cocok pada suatu waktu. Match
dalam beberapa hal merupakan antarmuka "lengkap" untuk tipe-tipe ini dan operasi tambahan apa pun yang kita inginkan dapat didefinisikan dalam hal itu. Ini menyajikan perspektif yang berbeda pada banyak pola OO seperti Pola Objek Null dan Pola Negara seperti yang saya tunjukkan pada jawaban Ryathal, serta Pola Pengunjung dan Pola Komposit. The Option
/ Maybe
tipe seperti Null Object Pola generik. Pola Komposit mirip dengan pengkodean type Tree<'a> = Leaf of 'a | Children of List<Tree<'a>>
. Pola Negara pada dasarnya adalah penyandian enumerasi.
Di sisi kerugiannya, ketika saya menulisnya, Match
metode ini menempatkan beberapa batasan pada subclass apa yang dapat ditambahkan secara bermakna, terutama jika kita ingin mempertahankan Properti Pengganti Liskov. Misalnya, menerapkan pengodean ini ke tipe enumerasi tidak akan memungkinkan Anda untuk memperpanjang enumerasi. Jika Anda memang ingin memperpanjang enumerasi, Anda harus mengubah semua penelepon dan pelaksana di mana-mana sama seperti jika Anda menggunakan enum
dan switch
. Yang mengatakan, pengkodean ini agak lebih fleksibel daripada yang asli. Sebagai contoh, kita dapat menambahkan Append
implementor List
yang hanya menampung dua daftar yang memberi kita waktu tambahan. Ini akan berperilaku seperti daftar ditambahkan bersama tetapi akan diwakili dengan cara yang berbeda.
Tentu saja, banyak dari masalah ini berkaitan dengan fakta yang Match
agak (secara konseptual tetapi sengaja) terkait dengan subkelas. Jika kami menggunakan metode yang tidak terlalu spesifik, kami mendapatkan desain OO yang lebih tradisional dan kami mendapatkan kembali ekstensibilitas, tetapi kami kehilangan "kelengkapan" antarmuka dan karenanya kami kehilangan kemampuan untuk mendefinisikan operasi apa pun pada jenis ini dalam hal antarmuka. Seperti yang disebutkan di tempat lain, ini adalah manifestasi dari Masalah Ekspresi .
Dapat diperdebatkan, desain seperti di atas dapat digunakan secara sistematis untuk sepenuhnya menghilangkan kebutuhan percabangan yang pernah mencapai ideal OO. Smalltalk, misalnya, menggunakan pola ini sering termasuk untuk Boolean sendiri. Tetapi seperti diskusi sebelumnya menyarankan, "penghapusan percabangan" ini cukup ilusi. Kami baru saja mengimplementasikan percabangan dengan cara yang berbeda, dan masih memiliki banyak properti yang sama.