Ungkap rahasia arsitektur perangkat lunak dengan Menguasai Arsitektur Perangkat Lunak: 11 Pola Desain Utama Dijelaskan .
2. Pola Desain — Adaptor
3. Pola Desain — Pembangun
4. Cara Menggunakan Pola Rantai Tanggung Jawab
5. Pola Desain — Dekorator
6. Pola Desain — Metode Pabrik
7. Pola Desain — Iterator
8. Pola Desain — Mediator
9. Pola Desain — Pengamat
10. Pola Properti Lanjutan C# 8.0
11. Pola Desain — Singleton
Menurut Gang of Four, pola pabrik abstrak dapat diasumsikan sebagai pabrik untuk menciptakan pabrik.
Pola pabrik abstrak hanyalah perluasan metode pabrik; sebaiknya pelajari metode pabrik sebelum memahami desain pabrik abstrak.
Mari kita pertimbangkan contoh yang sama dari Bank mana pun dengan jenis akun seperti rekening Tabungan dan Giro. Sekarang, mari kita terapkan contoh di atas menggunakan pola desain pabrik abstrak.
Pertama, terapkan antarmuka ISavingAccount dan ICurrentAccount sebagai berikut:
public interface ISavingAccount{ } public interface ICurrentAccount{ }
Mewarisi antarmuka di kelas di bawah ini
public class CurrentAccount : ICurrentAccount { public CurrentAccount(string message) { Console.WriteLine(message); } } public class SavingsAccount : ISavingAccount { public SavingsAccount( string message) { Console.WriteLine(message); } }
Mari menulis kelas abstrak dengan metode abstrak untuk setiap jenis akun.
public abstract class AccountTypeFactory { public abstract ISavingAccount SavingAccountFactory(string message); public abstract ICurrentAccount CurrentAccountFactory(string message); }
Sekarang, mari kita buat implementasi pabrik bernama “Bank1Factory,” yang menyediakan implementasi metode abstrak.
public class Bank1Factory : AccountTypeFactory { public override ICurrentAccount CurrentAccountFactory(string message) { return new CurrentAccount(message); } public override ISavingAccount SavingAccountFactory(string message) { return new SavingsAccount(message); } }
Pola desain pabrik abstrak berbeda dari metode pabrik yang perlu menerapkan penyedia pabrik, yang mengembalikan pabrik sesuai definisi.
Sekarang setelah kita membuat semua abstraksi dan pabrik. Mari kita rancang penyedia pabrik. Berikut ini cuplikan kode untuk penyedia pabrik, di mana metode statis akan membuat pabrik berdasarkan nama akun.
public class AccountFactoryProvider { public static AccountTypeFactory GetAccountTypeFactory(string accountName) { if (accountName.Contains("B1")) { return new Bank1Factory(); } else return null; } }
Mari kita ambil contoh daftar nomor akun, jika nama akunnya secara harfiah terdiri dari “ B1 ”, maka ia akan menggunakan instance Bank1Factory yang dikembalikan melalui penyedia pabrik.
static void Main(string[] args) { List<string> accNames = new List<string> { "B1-456", "B1-987", "B2-222" }; for (int i = 0; i < accNames.Count; i++) { AccountTypeFactory anAbstractFactory = AccountFactoryProvider.GetAccountTypeFactory(accNames[i]); if (anAbstractFactory == null) { Console.WriteLine("Invalid " + (accNames[i])); } else { ISavingAccount savingAccount = anAbstractFactory.SavingAccountFactory("Hello saving"); ICurrentAccount currentAccount = anAbstractFactory.CurrentAccountFactory("Hello Current"); } } Console.ReadLine(); }
Jika nama akun tidak mengandung literal “B1”, maka program akan mengeluarkan {{accountName}} yang tidak valid
Berikut ini adalah output dari potongan kode di atas.
Hello saving B1-456 Hello Current B1-456 Hello saving B1-987 Hello Current B1-987
Menurut Gang of Four, Pola Adaptor mengubah antarmuka suatu kelas menjadi antarmuka yang dibutuhkan klien.
Dengan kata lain, pola desain adaptor membantu antarmuka yang tidak kompatibel bekerja secara kolektif.
Mari kita pertimbangkan contoh penggabungan dua organisasi; organisasi X mengambil alih Y, tetapi saat menggabungkan kode, antarmukanya tidak kompatibel. Asumsikan bahwa antarmuka yang menyediakan daftar transaksi organisasi Y tidak kompatibel dengan X.
Jadi, pola desain adaptor membantu memecahkan masalah ini dengan implementasi yang sangat mudah.
Mari buat daftar transaksi dari organisasi Y yang dikonversi ke pola yang dibutuhkan aplikasi klien organisasi X. Kelas di atas dikenal sebagai "Adaptee".
public class OrgYTransactions { public List<string> GetTransactionsList() { List<string> transactions = new List<string>(); transactions.Add("Debit 1"); transactions.Add("Debit 2"); transactions.Add("Debit 3"); return transactions; } }
Kedua, mari kita buat antarmuka target.
public interface ITransactions{ List<string> GetTransactions(); }
Sekarang akhirnya, mari kita implementasikan kelas adaptor sebagai berikut.
public class TransAdapter : OrgYTransactions, ITransactions { public List<string> GetTransactions() { return GetTransactionsList(); } }
Setelah semua implementasi di atas selesai, mari kita pahami cara menggunakan kelas adaptor dalam aplikasi konsol.
class Program { static void Main(string[] args) { ITransactions adapter = new TransAdapter(); foreach (var item in adapter.GetTransactions()) { Console.WriteLine(item); } } }
Jika Anda perhatikan dengan seksama penggunaan di bawah ini, kami telah menggunakan antarmuka target ITransactions dan kelas adaptor TransAdapter tanpa mempertimbangkan tampilan antarmuka kelas pihak ketiga OrgYTransactions. Itulah kekuatan pola desain adaptor yang mengubah antarmuka kelas menjadi antarmuka yang dibutuhkan klien.
Menurut Gang of Four, pola kreasi “Builder” memungkinkan seseorang untuk memisahkan dan menggunakan kembali metode tertentu untuk membangun sesuatu.
Mari kita ambil contoh Mobil, dan pengguna ingin membangun dua model, yaitu SUV dan Sedan.
Pola desain pembangun berguna dalam kasus penggunaan di atas, dan mari kita lihat demonstrasinya langkah demi langkah.
Kelas Mobil memiliki properti berikut.
public class Car{ public string Name { get; set; } public double TopSpeed { get; set; } public bool IsSUV { get; set; } }
Pertama, mari terapkan pembangun kelas abstrak yang diperluas oleh berbagai model mobil seperti SUV atau sedan sesuai kasus penggunaan.
public abstract class CarBuilder { protected readonly Car _car = new Car(); public abstract void SetName(); public abstract void SetSpeed(); public abstract void SetIsSUV(); public virtual Car GetCar() => _car; }
Kelas abstrak terdiri dari metode berikut
Sekarang, mari kita membuat pabrik yang memanfaatkan kelas CarBuilder untuk membuat berbagai model mobil dan mengembalikan contoh mobil yang dibuat.
public class CarFactory { public Car Build(CarBuilder builder) { builder.SetName(); builder.SetSpeed(); builder.SetIsSUV(); return builder.GetCar(); } }
Terakhir, terapkan model mobil yang berbeda.
public class ModelSuv : CarBuilder { public override void SetIsSUV() { _car.IsSUV = true; } public override void SetName() { _car.Name = "Maruti SUV"; } public override void SetSpeed() { _car.TopSpeed = 1000; } }
public class ModelSedan : CarBuilder { public override void SetIsSUV() { _car.IsSUV = false; } public override void SetName() { _car.Name = "Maruti Sedan"; } public override void SetSpeed() { _car.TopSpeed = 2000; } }
Terakhir, mari kita gunakan pola desain untuk membangun model mobil yang berbeda dengan bantuan metode factory.Build(<model>).
static void Main(string[] args) { var sedan = new ModelSedan(); var suv = new ModelSuv(); var factory = new CarFactory(); var builders = new List<CarBuilder> { suv, sedan }; foreach (var b in builders) { var c = factory.Build(b); Console.WriteLine($"The Car details" + $"\n--------------------------------------" + $"\nName: {c.Name}" + $"\nIs SUV: {c.IsSUV}" + $"\nTop Speed: {c.TopSpeed} mph\n"); } }
Penggunaan di atas menunjukkan betapa bagusnya kita dapat membangun berbagai model mobil menggunakan pola desain pembangun.
Pola kode sangat mudah dirawat & diperluas. Jika di masa mendatang kita perlu mengembangkan model baru, cukup model baru tersebut yang perlu memperluas kelas CarBuilder, dan selesai.
Menurut Gang of Four, hal ini mendefinisikan rantai tanggung jawab untuk memproses permintaan. Dengan kata lain, meneruskan permintaan dari satu objek ke objek lain hingga objek tersebut menerima tanggung jawabnya.
Mari kita pertimbangkan contoh sistem klaim di perusahaan korporat mana pun. Berikut adalah daftar kisaran harga yang dapat disetujui dan oleh siapa.
100–1000 Rs => Junior/Senior Engineers => Approved by Manager 1001–10000 Rs => Managers => Approved by Senior Manager
Jika jumlahnya di luar kisaran 10.000, diperlukan persetujuan luar biasa dari manajer senior.
Kasus penggunaan di atas dapat dengan mudah diimplementasikan menggunakan pola desain Rantai Tanggung Jawab. Jadi, kelas klaim memiliki properti berikut.
public class Claim{ public int Id{get;set;} public double amount{get;set;} }
Pertama, mari kita definisikan fungsi apa yang dapat dilakukan oleh pemberi persetujuan klaim dan tetapkan hierarki untuk karyawan di berbagai tingkatan. Terapkan kelas abstrak seperti yang ditunjukkan di bawah ini
public abstract class ClaimApprover { protected ClaimApprover claimApprover; public void SetHierarchy(ClaimApprover claimApprover) { this.claimApprover = claimApprover; } public abstract void ApproveRequest(Claim claim); }
Sesuai kasus penggunaan, mari kita kendalikan pemohon klaim kelas "junior/senior". Perhatikan bahwa kelas/jabatan karyawan ini tidak dapat menyetujui klaim apa pun.
public class Junior : ClaimApprover { public override void ApproveRequest(Claim claim) { System.Console.WriteLine("Cannot approve"); } }
Demikian pula, mari kita definisikan implementasi untuk peran Manajer dan Manajer Senior.
public class Manager : ClaimApprover { public override void ApproveRequest(Claim claim) { if (claim.amount >= 100 && claim.amount <= 1000) { System.Console.WriteLine($"Claim reference {claim.Id} with amount {claim.amount} is approved by Manager"); } else if (claimApprover != null) { claimApprover.ApproveRequest(claim); } } }
Perhatikan bahwa berdasarkan rentang jumlah, jika dalam rentang Manajer, klaim dapat disetujui oleh Manajer; jika tidak, permintaan akan diteruskan ke Manajer Senior.
public class SeniorManager : ClaimApprover { public override void ApproveRequest(Claim claim) { if (claim.amount > 1000 && claim.amount <= 10000) { System.Console.WriteLine($"Claim reference {claim.Id} with amount {claim.amount} is approved by Senior Manager"); } else { System.Console.WriteLine($"Exceptional approval for Claim reference {claim.Id} with amount {claim.amount} is approved by Senior Manager"); } } }
Demikian pula, jika rentang jumlahnya berada dalam rentang Manajer Senior, klaim dapat disetujui oleh Manajer; jika tidak, karena berada pada hierarki terakhir, persetujuan luar biasa dilakukan untuk jumlah di luar rentang tersebut.
ClaimApprover junior = new Manager(); ClaimApprover sukhpinder = new Manager(); ClaimApprover singh = new SeniorManager(); junior.SetHierarchy(sukhpinder); sukhpinder.SetHierarchy(singh); Claim c1 = new Claim() { amount = 999, Id = 1001 }; Claim c2 = new Claim() { amount = 10001, Id = 1002 }; junior.ApproveRequest(c1); sukhpinder.ApproveRequest(c2);
Claim reference 1001 with amount 999 is approved by Manager Exceptional approval for Claim reference 1002 with amount 10001 is approved by Senior Manager
Untuk keluaran baris 1, jumlahnya berada dalam kisaran, jadi manajer menyetujuinya.
Untuk keluaran lini 2, meskipun manajer senior menyetujuinya, jumlahnya berada di luar kisaran.
Menurut Gang of Four, pola menambahkan tanggung jawab ekstra pada objek kelas secara dinamis.
Mari kita pertimbangkan contoh pembelian mobil senilai sepuluh lakh; perusahaan menyediakan fitur tambahan berikut.
Dengan beberapa fitur tambahan, total harga mobil meningkat. Mari kita terapkan kasus penggunaan di atas menggunakan Pola Dekorator.
Mari kita terapkan kasus penggunaan yang dijelaskan di atas. Pertama, definisikan kelas abstrak Car dan metode dasarnya.
public abstract class Car{ public abstract int CarPrice(); public abstract string GetName(); }
Pertimbangkan sebuah mobil kecil yang berada di atas kelas abstrak Mobil.
public class SmallCar : Car{ public override int CarPrice() => 10000; public override string GetName() => "Alto Lxi"; }
Sekarang, implementasikan kelas CarDecorator menggunakan komponen Car.
public class CarDecorator : Car { protected Car _car; public CarDecorator(Car car) { _car = car; } public override int CarPrice() => _car.CarPrice(); public override string GetName() =>_car.GetName(); }
Sekarang, mari kita membuat kelas terpisah untuk setiap fitur tambahan yang tersedia untuk Mobil yang mewarisi kelas CarDecorator.
Sesuai dengan kasus penggunaan, fitur tambahannya adalah sunroof dan sistem musik canggih.
Ganti metode sebagai
Tambahkan biaya tambahan “sistem musik canggih” ke total harga mobil.
Perbarui nama mobil dengan nama fitur tambahan.
public class AdvanceMusic : CarDecorator { public AdvanceMusic(Car car) : base(car) { } public override int CarPrice() => _car.CarPrice() + 3000; public override string GetName()=> "Alto Lxi with advance music system"; }
Ganti metode sebagai
public class Sunroof : CarDecorator { public Sunroof(Car car) : base(car) { } public override int CarPrice() => _car.CarPrice() + 2000; public override string GetName() => "Alto Lxi with Sunroof"; }
Buat contoh SmallCar dan keluarkan nama dan harga mobil.
Car car = new SmallCar(); Console.WriteLine($"Price of car {car.GetName()} : " + car.CarPrice());
Sekarang, mari kita tambahkan fitur tambahan seperti yang ditunjukkan di bawah ini
var car1 = new Sunroof(car); var car2 = new AdvanceMusic(car);
static void Main(string[] args) { Car car = new SmallCar(); Console.WriteLine($"Price of car {car.GetName()} : " + car.CarPrice()); var car1 = new Sunroof(car); Console.WriteLine($"Price of car {car1.GetName()} : " + car1.CarPrice()); var car2 = new AdvanceMusic(car); Console.WriteLine($"Price of car {car2.GetName()} : " + car2.CarPrice()); }
Selamat..!! Anda telah berhasil menerapkan kasus penggunaan menggunakan pola dekorator.
Menurut Gang of Four, metode pabrik memungkinkan subkelas menentukan objek kelas mana yang harus dibuat.
Mari kita pertimbangkan contoh Bank mana pun dengan jenis akun Tabungan dan Giro. Sekarang, mari kita terapkan contoh di atas menggunakan pola desain pabrik
Pertama, buat kelas abstrak tipe akun.
public abstract class AccoutType { public string Balance { get; set; } }
Terapkan kelas akun giro dan tabungan dengan mewarisi kelas abstrak AccountType seperti yang ditunjukkan di bawah ini.
public class SavingsAccount : AccoutType { public SavingsAccount() { Balance = "10000 Rs"; } } public class CurrentAccount : AccoutType { public CurrentAccount() { Balance = "20000 Rs"; } }
Terakhir, mari kita terapkan antarmuka pabrik, yang akan menyediakan kontrak yang membantu membuat objek kelas. Antarmuka ini juga dikenal sebagai Creator.
public interface IAccountFactory { AccoutType GetAccoutType(string accountName); }
Terakhir, tulis implementasi metode antarmuka kreator seperti yang ditunjukkan di bawah ini. Kelas yang mengimplementasikan kreator dikenal sebagai Concrete Creator.
public class AccountFactory : IAccountFactory { public AccoutType GetAccoutType(string accountName) { if (accountName.Equals("SAVINGS", StringComparison.OrdinalIgnoreCase)) { return new SavingsAccount(); } else if (accountName.Equals("CURRENT", StringComparison.OrdinalIgnoreCase)) { return new CurrentAccount(); } else { throw new ArgumentException("Invalid account name"); } } }
Selesai. Anda telah berhasil menerapkan metode pabrik menggunakan contoh Bank.
Subkelas akan menentukan objek kelas “AccountType” mana yang akan dibuat berdasarkan nama akun.
class Program { static void Main(string[] args) { IAccountFactory accountFactory = new AccountFactory(); var savingAccount = accountFactory.GetAccoutType("SAVINGS"); Console.WriteLine("Saving account balance: " + savingAccount.Balance); var currentAccount = accountFactory.GetAccoutType("CURRENT"); Console.WriteLine("Current account balance: " + currentAccount.Balance); } }
Misalnya, jika nama akunnya adalah “TABUNGAN,” maka objek kelas “AkunTabungan” akan dibuat dan dikembalikan.
Demikian pula, jika nama akunnya adalah “CURRENT,” maka objek kelas “CurrentAccount” akan dibuat dan dikembalikan.
Saving account balance: 10000 Rs Current account balance: 20000 Rs
Menurut Gang of Four, pola iterator menyediakan proses untuk mendapatkan objek agregator tanpa mengetahui implementasinya.
Mari kita ambil contoh daftar koleksi mobil dan string[] array sepeda motor, kita perlu mendesain objek agregator sehingga seseorang dapat mengulang koleksi tersebut tanpa mengetahui apakah itu daftar atau array.
Pola desain iterator membantu memecahkan masalah ini di mana iterator standar akan melintasi berbagai jenis koleksi.
Dengan mempertimbangkan kasus penggunaan di atas, mari kita tentukan antarmuka iterator khusus yang berfungsi sebagai lapisan abstrak pada iterator daftar dan array.
public interface IVehicleIterator{ void First(); bool IsDone(); string Next(); string Current(); }
Sekarang, tulis iterator mobil dan sepeda motor yang mengimplementasikan antarmuka di atas sesuai dengan kasus penggunaan.
public class CarIterator : IVehicleIterator { private List<string> _cars; private int _current; public CarIterator(List<string> cars) { _cars = cars; _current = 0; } public string Current() { return _cars.ElementAt(_current); } public void First() { _current = 0; } public bool IsDone() { return _current >= _cars.Count; } public string Next() { return _cars.ElementAt(_current++); } }
Iterator mobil diimplementasikan pada koleksi List<string> dan menyediakan implementasi metode antarmuka.
Iterator sepeda motor diimplementasikan melalui koleksi string[] dan menyediakan implementasi metode antarmuka.
public class MotercycleIterator : IVehicleIterator { private string[] _motercylces; private int _current; public MotercycleIterator(string[] motercylces) { _motercylces = motercylces; _current = 0; } public string Current() { return _motercylces[_current]; } public void First() { _current = 0; } public bool IsDone() { return _current >= _motercylces.Length; } public string Next() { return _motercylces[_current++]; } }
Setelah semua iterator di atas didefinisikan, tentukan antarmuka objek agregator standar yang membuat iterator.
public interface IVehicleAggregate{ IVehicleIterator CreateIterator(); }
Terakhir, tuliskan kelas-kelas yang mengimplementasikan antarmuka agregator di atas. Berdasarkan kasus penggunaan, kelas Mobil dan Sepeda Motor akan mengimplementasikan antarmuka agregator.
Metode antarmuka agregator mengembalikan iterator yang relevan seperti yang ditunjukkan di bawah ini.
public class Car : IVehicleAggregate { private List<string> _cars; public Car() { _cars = new List<string> { "Car 1", "Car 2", "Car 3" }; } public IVehicleIterator CreateIterator() { return new CarIterator(_cars); } }
Metode antarmuka agregator mengembalikan iterator yang relevan seperti yang ditunjukkan di bawah ini.
public class Motercycle : IVehicleAggregate { private string[] _motercycles; public Motercycle() { _motercycles = new[] { "Bike 1", "Bike 2", "Bike 3" }; } public IVehicleIterator CreateIterator() { return new MotercycleIterator(_motercycles); } }
Metode PrintVehicles memeriksa apakah !iterator.isDone kemudian mengeluarkan elemen koleksi. Apa pun koleksi yang kita hadapi, terapkan metode seperti First, IsDone, dan Next.
static void Main(string[] args) { IVehicleAggregate car = new Vehicles.Car(); IVehicleAggregate motercycle = new Vehicles.Motercycle(); IVehicleIterator carIterator = car.CreateIterator(); IVehicleIterator motercycleIterator = motercycle.CreateIterator(); PrintVehicles(carIterator); PrintVehicles(motercycleIterator); } static void PrintVehicles(IVehicleIterator iterator) { iterator.First(); while (!iterator.IsDone()) { Console.WriteLine(iterator.Next()); } }
Kami tidak mengetahui jenis koleksi yang mendasarinya, tetapi masih diulang melalui Pola Desain Iterator. Jika Anda terus maju dan menjalankannya, output berikut akan ditampilkan.
Menurut Gang of Four, pola Mediator merangkum interaksi objek satu sama lain.
Pola desain mediator membantu kita merancang aplikasi yang saling terhubung secara longgar dengan merangkum interaksi objek.
Mari kita pertimbangkan contoh ruang obrolan tempat peserta mendaftar, dan cara berkomunikasi secara efisien.
Perlu mengimplementasikan percakapan ruang obrolan berikut menggunakan Pola Desain Mediator.
David to Scott: 'Hey' Scott to David: 'I am good how about you.' Jennifer to Ashley: 'Hey ashley... david is back in the group' Jennifer to David: 'Where have you been?' Ashley to David: 'How come you aren't active here anymore?'
Langkah utama adalah membuat daftar nama pengguna yang akan digunakan di dalam ruang obrolan. Enum publik untuk itu ditunjukkan di bawah ini.
public enum Username{ Ashley, David, Jennifer, Scott }
Sekarang, pertama dan terutama, terapkan lapisan abstrak ruang obrolan.
public abstract class AChatroom { public abstract void Register(User user); public abstract void Post(string fromUser, string toUser, string msg); }
Dan kelas yang mendefinisikan metode abstrak. Metode tersebut memvalidasi apakah pengguna tersebut ada dalam kamus. Misalnya, metode register memvalidasi apakah pengguna tersebut sudah ada atau belum. Jika tidak ada, maka pengguna tersebut hanya didaftarkan di ruang obrolan.
public class Chatroom : AChatroom { private Dictionary<string, User> _users = new Dictionary<string, User>(); public override void Post(string fromUser, string toUser, string msg) { User participant = _users[toUser]; if (participant != null) { participant.DM(fromUser, msg); } } public override void Register(User user) { if (!_users.ContainsValue(user)) { _users[user.Name] = user; } user.Chatroom = this; } }
Terakhir, mari terapkan tindakan yang dapat dilakukan pengguna, seperti mengeposkan pesan kepada pengguna di ruang obrolan atau menerima DM dari pengguna lain.
public class User { private Chatroom _chatroom; private string _name; public User(string name) => this._name = name; public string Name => _name; public Chatroom Chatroom { set { _chatroom = value; } get => _chatroom; } public void Post(string to, string message) => _chatroom.Post(_name, to, message); public virtual void DM(string from, string message) => Console.WriteLine("{0} to {1}: '{2}'", from, Name, message); }
static void Main(string[] args) { Chatroom chatroom = new Chatroom(); User Jennifer = new UserPersona(Username.Jennifer.ToString()); User Ashley = new UserPersona(Username.Ashley.ToString()); User David = new UserPersona(Username.David.ToString()); User Scott = new UserPersona(Username.Scott.ToString()); chatroom.Register(Jennifer); chatroom.Register(Ashley); chatroom.Register(David); chatroom.Register(Scott); David.Post(Username.Scott.ToString(), "Hey"); Scott.Post(Username.David.ToString(), "I am good how about you."); Jennifer.Post(Username.Ashley.ToString(), "Hey ashley... david is back in the group"); Jennifer.Post(Username.David.ToString(), "Where have you been?"); Ashley.Post(Username.David.ToString(), "How come you aren't active here anymore?"); Console.ReadKey(); }
Eksekusi program hanya menjelaskan metode Post dari kelas pengguna.
Output: Riwayat ruang obrolan dari eksekusi program di atas
David to Scott: 'Hey' Scott to David: 'I am good how about you.' Jennifer to Ashley: 'Hey ashley... david is back in the group' Jennifer to David: 'Where have you been?' Ashley to David: 'How come you aren't active here anymore?'
Menurut Gang of Four, pola pengamat mendefinisikan ketergantungan antara dua objek atau lebih. Jadi, ketika satu status objek berubah, maka semua dependennya akan diberitahu.
Dengan kata lain, perubahan pada satu objek memicu pemberitahuan pada objek lain.
Mari kita ambil contoh influencer selebriti Instagram yang memiliki jumlah pengikut " x ". Jadi, saat selebriti tersebut mengunggah postingan, maka semua pengikutnya akan diberi tahu.
Mari kita terapkan kasus penggunaan tersebut di atas menggunakan Pola Desain Observer.
Berdasarkan kasus penggunaan, yang pertama mengimplementasikan antarmuka yang berisi tindakan apa saja yang dapat dilakukan oleh seorang selebriti. Antarmuka ini dikenal sebagai “ Subjek .”
public interface ICelebrityInstagram{ string FullName { get; } string Post { get; set; } void Notify(string post); void AddFollower(IFollower fan); void RemoveFollower(IFollower fan); }
Beritahu: Untuk memberitahukan semua pengikut.
AddFollower: Tambahkan pengikut baru ke daftar selebriti.
RemoveFollower: Hapus pengikut dari daftar selebriti.
Sekarang, terapkan antarmuka pengamat “IFollower”, yang berisi fungsi anggota “Perbarui” untuk pemberitahuan.
public interface IFollower{ void Update(ICelebrityInstagram celebrityInstagram); }
Akhirnya, sudah saatnya untuk menerapkan “Implementasi Konkret” untuk “ Subjek ” dan “ Pengamat ”.
Ini menyediakan implementasi fungsi anggota Pembaruan, yang menampilkan nama selebriti & postingannya ke konsol.
public class Follower : IFollower { public void Update(ICelebrityInstagram celebrityInstagram) { Console.WriteLine($"Follower notified. Post of {celebrityInstagram.FullName}: " + $"{celebrityInstagram.Post}"); } }
public class Sukhpinder : ICelebrityInstagram { private readonly List<IFollower> _posts = new List<IFollower>(); private string _post; public string FullName => "Sukhpinder Singh"; public string Post { get { return _post; } set { Notify(value); } } public void AddFollower(IFollower follower) { _posts.Add(follower); } public void Notify(string post) { _post = post; foreach (var item in _posts) { item.Update(this); } } public void RemoveFollower(IFollower follower) { _posts.Remove(follower); } }
Kasus penggunaan berikut menunjukkan bahwa setiap kali pernyataan di bawah ini dijalankan sukhpinder.Post = “Saya suka pola desain.”; Metode pembaruan dipicu untuk setiap pengikut, yaitu, setiap objek pengikut diberitahu tentang posting baru dari “Sukhpinder.”
static void Main(string[] args) { var sukhpinder = new Sukhpinder(); var firstFan = new Follower(); var secondFan = new Follower(); sukhpinder.AddFollower(firstFan); sukhpinder.AddFollower(secondFan); sukhpinder.Post = "I love design patterns."; Console.Read(); }
Artikel ini menjelaskan bagaimana pencocokan pola menyediakan cara yang efektif untuk memanfaatkan dan memproses data dalam bentuk yang bukan bagian dari sistem utama.
Mari kita ambil contoh Kalkulator Tol dan lihat bagaimana pencocokan pola membantu menulis algoritma untuk itu.
public class Car { public int PassengerCount { get; set; } } public class DeliveryTruck { public int Weight { get; set; } } public class Taxi { public int Fare { get; set; } } public class Bus { public int Capacity { get; set; } public int RidersCount { get; set; } }
Contoh 1: Hitung tarif tol sesuai dengan kondisi berikut:
- Jika kendaraannya Mobil => 100 Rs
- Jika kendaraannya adalah DeliveryTruck => 200 Rs
- Jika kendaraannya Bus => 150 Rs
- Jika kendaraannya adalah Taksi => 120 Rs
Jika jenis kendaraan cocok dengan Mobil 100, maka akan dikembalikan dan seterusnya. Perhatikan bahwa null & {} adalah kasus default untuk jenis objek.
Selain itu, “_” dapat digunakan untuk memprogram skenario default. Lihat sintaks sakelar baru.
Ini adalah cara pengkodean yang jauh lebih bersih & efisien & juga merekomendasikan penggunaan nama variabel satu huruf di dalam sintaksis switch.
public static int TollFare(Object vehicleType) => vehicleType switch { Car c => 100, DeliveryTruck d => 200, Bus b => 150, Taxi t => 120, null => 0, { } => 0 };
Contoh pengujian dari sudut pandang aplikasi konsol. Kode di bawah ini menggambarkan cara memanggil fungsi pencocokan pola di atas dari metode utama.
var car = new Car(); var taxi = new Taxi(); var bus = new Bus(); var truck = new DeliveryTruck(); Console.WriteLine($"The toll for a car is {TollFare(car)}"); Console.WriteLine($"The toll for a taxi is {TollFare(taxi)}"); Console.WriteLine($"The toll for a bus is {TollFare(bus)}"); Console.WriteLine($"The toll for a truck is {TollFare(truck)}");
The toll for a car is 100 The toll for a taxi is 120 The toll for a bus is 150 The toll for a truck is 200
Contoh 2: Tambahkan harga hunian berdasarkan jenis kendaraan
- Mobil dan taksi dengan penumpang “TIDAK” membayar tambahan 10 Rs.
- Mobil dan taksi dengan dua penumpang mendapat diskon Rs 10.
- Mobil dan taksi dengan tiga penumpang atau lebih mendapat diskon Rs 20.
- Bus yang penumpangnya kurang dari 50% membayar tambahan 30 Rs.
- Bus yang menampung lebih dari 90% penumpang mendapat diskon Rs 40.
- Truk dengan berat lebih dari 5000 lbs dikenakan biaya tambahan sebesar 100 Rs.
- Truk ringan di bawah 3000 lbs, diberi diskon 20 Rs.
Lihat sintaks pencocokan pola dengan kelas properti tunggal & jamak. Tautan
Car { PassengerCount: 0 } => 100 + 10, Car { PassengerCount: 1 } => 100, Car { PassengerCount: 2 } => 100 - 10, Car c => 100 - 20,
Taxi {Fare:0 }=>100+10, Taxi { Fare: 1 } => 100, Taxi { Fare: 2 } => 100 - 10, Taxi t => 100 - 20,
Bus b when ((double)b.RidersCount / (double)b.Capacity) < 0.50 => 150 + 30, Bus b when ((double)b.RidersCount / (double)b.Capacity) > 0.90 => 150 - 40, Bus b => 150,
DeliveryTruck t when (t.Weight > 5000) => 200 + 100, DeliveryTruck t when (t.Weight < 3000) => 200 - 20, DeliveryTruck t => 200,
Contoh di bawah ini menyoroti keuntungan dari pencocokan pola: cabang pola dikompilasi secara berurutan. Kompiler juga memperingatkan tentang kode yang tidak dapat diakses.
public static int OccupancyTypeTollFare(Object vehicleType) => vehicleType switch { Car { PassengerCount: 0 } => 100 + 10, Car { PassengerCount: 1 } => 100, Car { PassengerCount: 2 } => 100 - 10, Car c => 100 - 20, Taxi { Fare: 0 } => 100 + 10, Taxi { Fare: 1 } => 100, Taxi { Fare: 2 } => 100 - 10, Taxi t => 100 - 20, Bus b when ((double)b.RidersCount / (double)b.Capacity) < 0.50 => 150 + 30, Bus b when ((double)b.RidersCount / (double)b.Capacity) > 0.90 => 150 - 40, Bus b => 150, DeliveryTruck t when (t.Weight > 5000) => 200 + 100, DeliveryTruck t when (t.Weight < 3000) => 200 - 20, DeliveryTruck t => 200, null => 0, { } => 0, };
Contoh pengujian dari sudut pandang aplikasi konsol. Kode di bawah ini menggambarkan cara memanggil fungsi pencocokan pola di atas dari metode utama.
var car1 = new Car{ PassengerCount=2}; var taxi1 = new Taxi { Fare = 0 }; var bus1 = new Bus { Capacity = 100, RidersCount = 30 }; var truck1 = new DeliveryTruck { Weight = 30000 }; Console.WriteLine($"The toll for a car is {OccupancyTypeTollFare(car1)}"); Console.WriteLine($"The toll for a taxi is {OccupancyTypeTollFare(taxi1)}"); Console.WriteLine($"The toll for a bus is {OccupancyTypeTollFare(bus1)}"); Console.WriteLine($"The toll for a truck is {OccupancyTypeTollFare(truck1)}");
The toll for a car is 90 The toll for a taxi is 110 The toll for a bus is 180 The toll for a truck is 300
“Pencocokan pola membuat kode lebih mudah dibaca dan menawarkan alternatif untuk teknik berorientasi objek saat Anda tidak dapat menambahkan kode ke kelas Anda.”
Gang of Four — Pola desain Singleton memastikan bahwa kelas tertentu hanya memiliki satu instans/objek dan titik akses global.
Kelas singleton digunakan untuk menghilangkan pembuatan lebih dari satu objek dari kelas tertentu.
public class SingletonExample { private string Name { get; set; } = "Hello from singleton"; private static SingletonExample _instance; public static SingletonExample Instance { get { if (_instance == null) { _instance = new SingletonExample(); } return _instance; } } public SingletonExample() { } public string GetName() => Name; }
Mari panggil kelas singleton dua kali dan tetapkan instance yang dikembalikan ke dua variabel yang berbeda. Terakhir, periksa apakah kedua objek tersebut sama menggunakan fungsi Object.Equals.
static void Main(string[] args) { var response = SingletonExample.Instance; Console.WriteLine(response); var response1 = SingletonExample.Instance; Console.WriteLine(response1); Console.WriteLine(Object.Equals(response1, response)); }
Output konsol mengembalikan true; selamat. Anda telah berhasil menerapkan Pola Singleton.
Kelas di atas dikenal sebagai kelas singleton, tetapi saat ini, kelas tersebut tidak aman untuk thread. Dalam lingkungan multi-thread, dua thread dapat menjalankan pernyataan if (_instance == null) secara bersamaan, dan pada akhirnya kita akan memiliki beberapa instance dari kelas singleton.
Salah satu cara untuk membuat thread lebih aman adalah dengan menggunakan mekanisme penguncian, dan cara lainnya adalah dengan membuat instance read-only untuk pendekatan yang lebih bersih dan efisien.
public class ThreadSafeSingleton { private static readonly ThreadSafeSingleton _instance = new ThreadSafeSingleton(); public static ThreadSafeSingleton Instance { get { return _instance; } } public ThreadSafeSingleton() { } }
https://github.com/ssukhpinder/DesignPatterns
Sponsor membantu saya terus memelihara dan membangun proyek baru seperti ini.
🙏 Jika Anda menggunakan Pay, Noticed, atau proyek saya lainnya, kontribusi kecil akan SANGAT BERARTI. Dengan sendirinya, open source tidak akan menguntungkan. Semoga, dengan bantuan Anda, melanjutkan pekerjaan saya dapat berkelanjutan, dan saya tidak perlu mencari pekerjaan tetap 😛.
Terima kasih telah menjadi bagian dari komunitas C#!