Dimulai dengan C # 8.0 pada .NET Core 3.0, Anda dapat menentukan implementasi saat Anda mendeklarasikan anggota antarmuka. Skenario yang paling umum adalah menambahkan anggota dengan aman ke antarmuka yang sudah dirilis dan digunakan oleh klien yang tak terhitung jumlahnya.
Dalam tutorial ini, Anda akan belajar cara:
- Perluas antarmuka dengan aman dengan menambahkan metode dengan implementasi.
- Buat implementasi parameterisasi untuk memberikan fleksibilitas yang lebih besar.
- Aktifkan pelaksana untuk memberikan implementasi yang lebih spesifik dalam bentuk penggantian.

Prasyarat
Anda harus mengatur mesin Anda untuk menjalankan .NET Core, termasuk kompiler pratinjau C # 8.0. Kompiler pratinjau C # 8.0 tersedia mulai dengan Visual Studio 2019 , atau SDK pratinjau .NET Core 3.0 terbaru. Anggota antarmuka default tersedia mulai dengan pratinjau .NET Core 3.0 4.
Ikhtisar skenario
Tutorial ini dimulai dengan versi 1 dari perpustakaan hubungan pelanggan. Anda bisa mendapatkan aplikasi pemula pada repo sampel kami di GitHub . Perusahaan yang membangun perpustakaan ini ditujukan bagi pelanggan dengan aplikasi yang ada untuk mengadopsi perpustakaan mereka. Mereka memberikan definisi antarmuka minimal untuk pengguna perpustakaan mereka untuk diimplementasikan. Berikut adalah definisi antarmuka untuk pelanggan:
public interface ICustomer { IEnumerable<IOrder> PreviousOrders { get; } DateTime DateJoined { get; } DateTime? LastOrder { get; } string Name { get; } IDictionary<DateTime, string> Reminders { get; } }
Mereka mendefinisikan antarmuka kedua yang mewakili pesanan:
public interface IOrder { DateTime Purchased { get; } decimal Cost { get; } }
Dari antarmuka itu, tim dapat membangun perpustakaan untuk pengguna mereka untuk menciptakan pengalaman yang lebih baik bagi pelanggan mereka. Tujuan mereka adalah untuk menciptakan hubungan yang lebih dalam dengan pelanggan yang sudah ada dan meningkatkan hubungan mereka dengan pelanggan baru.
Sekarang, saatnya untuk meningkatkan perpustakaan untuk rilis berikutnya. Salah satu fitur yang diminta memungkinkan diskon loyalitas untuk pelanggan yang memiliki banyak pesanan. Diskon loyalitas baru ini diterapkan setiap kali pelanggan melakukan pemesanan. Diskon spesifik adalah properti dari masing-masing pelanggan individu. Setiap implementasi ICustomer dapat menetapkan aturan yang berbeda untuk diskon loyalitas.
Cara paling alami untuk menambahkan fungsionalitas ini adalah meningkatkan antarmuka ICustomer
dengan metode untuk menerapkan diskon loyalitas apa pun. Saran desain ini menimbulkan kekhawatiran di antara pengembang yang berpengalaman: "Antarmuka tidak berubah setelah dirilis! Ini adalah perubahan besar! ” C # 8.0 menambahkan implementasi antarmuka default untuk meningkatkan antarmuka. Penulis perpustakaan dapat menambahkan anggota baru ke antarmuka dan menyediakan implementasi default untuk anggota tersebut.
Implementasi antarmuka default memungkinkan pengembang untuk memutakhirkan antarmuka sementara masih memungkinkan implementor untuk menimpa implementasi itu. Pengguna perpustakaan dapat menerima implementasi default sebagai perubahan yang tidak melanggar. Jika aturan bisnis mereka berbeda, mereka dapat menimpanya.
Tingkatkan dengan anggota antarmuka default
Tim menyetujui penerapan standar yang paling mungkin: diskon loyalitas untuk pelanggan.
Pembaruan harus menyediakan fungsionalitas untuk menetapkan dua properti: jumlah pesanan yang diperlukan untuk memenuhi syarat untuk diskon, dan persentase diskon. Ini menjadikannya skenario yang sempurna untuk anggota antarmuka standar. Anda dapat menambahkan metode ke antarmuka ICustomer, dan memberikan implementasi yang paling mungkin. Semua yang ada, dan implementasi baru apa pun dapat menggunakan implementasi default, atau menyediakannya sendiri.
Pertama, tambahkan metode baru ke implementasi:
Penulis perpustakaan menulis tes pertama untuk memeriksa implementasi:
SampleCustomer c = new SampleCustomer("customer one", new DateTime(2010, 5, 31)) { Reminders = { { new DateTime(2010, 08, 12), "childs's birthday" }, { new DateTime(1012, 11, 15), "anniversary" } } }; SampleOrder o = new SampleOrder(new DateTime(2012, 6, 1), 5m); c.AddOrder(o); o = new SampleOrder(new DateTime(2103, 7, 4), 25m); c.AddOrder(o);
Perhatikan bagian tes berikut:
Itu diperlukan dari ICustomer
ke ICustomer
. Kelas SampleCustomer
tidak perlu menyediakan implementasi untuk ComputeLoyaltyDiscount
; yang disediakan oleh antarmuka ICustomer
. Namun, kelas SampleCustomer
tidak mewarisi anggota dari interface-nya. Aturan itu belum berubah. Untuk memanggil metode apa pun yang dideklarasikan dan diimplementasikan dalam antarmuka, variabel harus berupa jenis antarmuka, ICustomer
dalam contoh ini.
Berikan parameterisasi
Itu awal yang bagus. Tetapi, implementasi standar terlalu ketat. Banyak konsumen dari sistem ini dapat memilih ambang yang berbeda untuk jumlah pembelian, panjang keanggotaan yang berbeda, atau diskon persentase yang berbeda. Anda dapat memberikan pengalaman peningkatan yang lebih baik bagi lebih banyak pelanggan dengan menyediakan cara untuk mengatur parameter tersebut. Mari kita tambahkan metode statis yang menetapkan tiga parameter yang mengendalikan implementasi default:
Ada banyak kemampuan bahasa baru yang ditampilkan dalam fragmen kode kecil itu. Antarmuka sekarang dapat menyertakan anggota statis, termasuk bidang dan metode. Pengubah akses yang berbeda juga diaktifkan. Bidang tambahan bersifat pribadi, metode baru bersifat publik. Setiap pengubah diizinkan pada anggota antarmuka.
Aplikasi yang menggunakan rumus umum untuk menghitung diskon loyalitas, tetapi parameter yang berbeda, tidak perlu menyediakan implementasi kustom; mereka dapat mengatur argumen melalui metode statis. Misalnya, kode berikut menetapkan "apresiasi pelanggan" yang memberikan penghargaan kepada pelanggan mana pun dengan keanggotaan lebih dari satu bulan:
ICustomer.SetLoyaltyThresholds(new TimeSpan(30, 0, 0, 0), 1, 0.25m); Console.WriteLine($"Current discount: {theCustomer.ComputeLoyaltyDiscount()}");
Perpanjang implementasi standar
Kode yang Anda tambahkan sejauh ini telah memberikan implementasi yang nyaman untuk skenario di mana pengguna menginginkan sesuatu seperti implementasi default, atau untuk memberikan seperangkat aturan yang tidak terkait. Untuk fitur terakhir, mari kita sedikit memperbaiki kode untuk mengaktifkan skenario di mana pengguna mungkin ingin membangun implementasi default.
Pertimbangkan startup yang ingin menarik pelanggan baru. Mereka menawarkan diskon 50% dari pesanan pertama pelanggan baru. Jika tidak, pelanggan yang sudah ada mendapatkan diskon standar. Penulis pustaka perlu memindahkan implementasi default ke metode protected static
sehingga setiap kelas yang mengimplementasikan antarmuka ini dapat menggunakan kembali kode dalam implementasinya. Implementasi default dari anggota antarmuka memanggil metode bersama ini juga:
public decimal ComputeLoyaltyDiscount() => DefaultLoyaltyDiscount(this); protected static decimal DefaultLoyaltyDiscount(ICustomer c) { DateTime start = DateTime.Now - length; if ((c.DateJoined < start) && (c.PreviousOrders.Count() > orderCount)) { return discountPercent; } return 0; }
Dalam implementasi kelas yang mengimplementasikan antarmuka ini, override dapat memanggil metode static helper, dan memperluas logika itu untuk memberikan diskon "pelanggan baru":
public decimal ComputeLoyaltyDiscount() { if (PreviousOrders.Any() == false) return 0.50m; else return ICustomer.DefaultLoyaltyDiscount(this); }
Anda dapat melihat seluruh kode yang sudah jadi di [sampel repo kami di GitHub] (Anda bisa mendapatkan aplikasi pemula pada repo sampel kami di GitHub .
Fitur-fitur baru ini berarti bahwa antarmuka dapat diperbarui dengan aman ketika ada implementasi standar yang wajar untuk anggota baru tersebut. Dengan hati-hati mendesain antarmuka untuk mengekspresikan ide fungsional tunggal yang dapat diimplementasikan oleh beberapa kelas. Itu membuatnya lebih mudah untuk meningkatkan definisi antarmuka ketika persyaratan baru ditemukan untuk ide fungsional yang sama.