Бағдарламалық жасақтама архитектурасын меңгеру арқылы бағдарламалық жасақтама архитектурасының құпияларын ашыңыз: 11 негізгі дизайн үлгілері түсіндірілді .
2. Дизайн үлгісі — Адаптер
3. Дизайн үлгісі — Құрылысшы
4. Жауапкершілік үлгісін пайдалану жолы
5. Дизайн үлгісі — Декоратор
6. Дизайн үлгісі — зауыттық әдіс
7. Дизайн үлгісі — Итератор
8. Дизайн үлгісі — Медиатор
9. Дизайн үлгісі — бақылаушы
10. Advance Property Pattern C# 8.0
11. Дизайн үлгісі — Singleton
Төрттік банданың пікірінше, абстрактілі зауыттық үлгілерді фабрикалар құру зауыты ретінде қарастыруға болады.
Абстрактілі зауыт үлгісі таза кеңейту зауыттық әдісі болып табылады; дерексіз зауыт дизайнын түсінбес бұрын зауыттық әдіспен өту ұсынылады.
Жинақ және Ағымдағы шоттар сияқты шот түрлері бар кез келген Банктің бірдей мысалын қарастырайық. Енді абстрактілі зауыттық дизайн үлгісін пайдаланып жоғарыдағы мысалды жүзеге асырайық.
Біріншіден, ISavingAccount және ICurrentAccount интерфейстерін келесідей орындаңыз:
public interface ISavingAccount{ } public interface ICurrentAccount{ }
Төмендегі сыныптардағы интерфейсті мұраға алыңыз
public class CurrentAccount : ICurrentAccount { public CurrentAccount(string message) { Console.WriteLine(message); } } public class SavingsAccount : ISavingAccount { public SavingsAccount( string message) { Console.WriteLine(message); } }
Әрбір тіркелгі түрі үшін дерексіз әдістермен дерексіз класс жазайық.
public abstract class AccountTypeFactory { public abstract ISavingAccount SavingAccountFactory(string message); public abstract ICurrentAccount CurrentAccountFactory(string message); }
Енді дерексіз әдістерді енгізуді қамтамасыз ететін «Bank1Factory» атты зауыттық іске асыруды жасайық.
public class Bank1Factory : AccountTypeFactory { public override ICurrentAccount CurrentAccountFactory(string message) { return new CurrentAccount(message); } public override ISavingAccount SavingAccountFactory(string message) { return new SavingsAccount(message); } }
Дерексіз зауыттық дизайн үлгісі зауыттық әдіспен ерекшеленеді, ол зауыттық жеткізушіні жүзеге асыру қажет, ол зауыттарды анықтамаға сәйкес қайтарады.
Қазір бізде барлық абстракциялар мен зауыттар бар. Зауыт провайдерін жобалауға рұқсат етіңіз. Төменде зауыт провайдеріне арналған код үзіндісін табыңыз, мұнда статикалық әдіс тіркелгі атауына негізделген зауыт жасайды.
public class AccountFactoryProvider { public static AccountTypeFactory GetAccountTypeFactory(string accountName) { if (accountName.Contains("B1")) { return new Bank1Factory(); } else return null; } }
Есептік жазбаның атауы тура мағынасында “ B1 ” тұратын болса, ол зауыт провайдері арқылы қайтарылған Bank1Factory данасын пайдаланатын шот нөмірлерінің тізімін мысалға алайық.
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(); }
Егер тіркелгі атауында "B1" литералы болмаса, бағдарлама жарамсыз {{accountName}} мәнін шығарады.
Жоғарыдағы код үзіндісінің шығысын төменде табыңыз.
Hello saving B1-456 Hello Current B1-456 Hello saving B1-987 Hello Current B1-987
Gang of Four бойынша, адаптер үлгісі класс интерфейстерін клиент қажет ететін интерфейстерге түрлендіреді.
Басқаша айтқанда, адаптердің дизайн үлгісі үйлесімсіз интерфейстердің бірлесіп жұмыс істеуіне көмектеседі.
Екі ұйымның бірігуінің мысалын қарастырайық; X ұйымы Y-ді басып алады, бірақ кодты біріктіру кезінде интерфейстер үйлесімді емес. Y ұйымының транзакцияларының тізімін беретін интерфейс X-пен үйлесімді емес деп есептейік.
Осылайша, адаптер дизайнының үлгісі іске асырылуы өте қарапайым болатын бұл мәселені шешуге көмектеседі.
X ұйымының клиенттік қолданбасы талап ететін үлгілерге түрлендірілетін Y ұйымынан транзакциялар тізімін жасайық. Жоғарыда аталған сынып «Адаптеуші» ретінде белгілі.
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; } }
Екіншіден, мақсатты интерфейсті жасайық.
public interface ITransactions{ List<string> GetTransactions(); }
Енді, ең соңында, адаптер класын келесідей іске асырамыз.
public class TransAdapter : OrgYTransactions, ITransactions { public List<string> GetTransactions() { return GetTransactionsList(); } }
Жоғарыда көрсетілген барлық іске асырулар орындалғаннан кейін консольдық қосымшада адаптер класын қалай пайдалану керектігін түсінейік.
class Program { static void Main(string[] args) { ITransactions adapter = new TransAdapter(); foreach (var item in adapter.GetTransactions()) { Console.WriteLine(item); } } }
Төмендегі пайдалануды мұқият қарасаңыз, біз үшінші тарап класының OrgYTransactions интерфейстерінің қалай көрінетінін ескермей, ITransactions мақсатты интерфейсін және TransAdapter адаптер класын пайдаландық. Бұл адаптер дизайн үлгісінің күші, ол класс интерфейстерін клиент қажет ететін интерфейстерге түрлендіреді.
Bang of Four пікірінше, «Құрылысшы» жасампаздық үлгісі бір нәрсені құрудың белгілі бір әдісін бөліп, қайта пайдалануға мүмкіндік береді.
Автокөліктің мысалын алайық, ал пайдаланушы екі үлгіні, яғни жол талғамайтын көлік пен седан құрастырғысы келді.
Құрылысшы дизайны үлгісі жоғарыда көрсетілген пайдалану жағдайында ыңғайлы болады және қадамдық демонстрацияны көрейік.
Car класының келесі қасиеттері бар.
public class Car{ public string Name { get; set; } public double TopSpeed { get; set; } public bool IsSUV { get; set; } }
Біріншіден, пайдалану жағдайына сәйкес жол талғамайтын көліктер немесе седандар сияқты әртүрлі автомобиль үлгілері арқылы кеңейтілген дерексіз класс құрастырушысын іске асырайық.
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; }
Абстрактілі класс келесі әдістерден тұрады
Енді әртүрлі автомобиль үлгілерін жасау үшін CarBuilder сыныбын қолданатын және жасалған көлік данасын қайтаратын зауыт құрайық.
public class CarFactory { public Car Build(CarBuilder builder) { builder.SetName(); builder.SetSpeed(); builder.SetIsSUV(); return builder.GetCar(); } }
Соңында, автомобильдердің әртүрлі үлгілерін іске асырыңыз.
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; } }
Соңында, 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"); } }
Жоғарыдағы қолдану құрылысшы дизайн үлгісін пайдаланып әртүрлі көлік үлгілерін қаншалықты әдемі құрастыра алатынымызды көрсетеді.
Код үлгісі жоғары қолдауға және кеңейтуге болады. Болашақта бізге жаңа үлгіні әзірлеу қажет болса, тек жаңа модель CarBuilder класын кеңейту керек және ол аяқталды.
Bang of Four пікірінше, ол сұрауды өңдеу үшін жауапкершілік тізбегін анықтайды. Басқаша айтқанда, сұрауды бір нысаннан екіншісіне нысан өз жауапкершілігін қабылдағанға дейін жіберіңіз.
Кез келген корпоративтік компаниядағы шағымдар жүйесінің мысалын қарастырайық. Мұнда және кім бекітетін баға диапазонының тізімі берілген.
100–1000 Rs => Junior/Senior Engineers => Approved by Manager 1001–10000 Rs => Managers => Approved by Senior Manager
Егер сома 10 000 ауқымынан тыс болса, аға менеджерден ерекше мақұлдау қажет.
Жоғарыда келтірілген пайдалану жағдайын Жауапкершілік тізбегі дизайн үлгісі арқылы оңай жүзеге асыруға болады. Сонымен, шағым класы келесі қасиеттерге ие.
public class Claim{ public int Id{get;set;} public double amount{get;set;} }
Біріншіден, шағымды мақұлдаушы қандай функцияларды орындай алатынын анықтап алайық және әртүрлі деңгейдегі қызметкерлер үшін иерархияны орнатайық. Төменде көрсетілгендей дерексіз классты орындаңыз
public abstract class ClaimApprover { protected ClaimApprover claimApprover; public void SetHierarchy(ClaimApprover claimApprover) { this.claimApprover = claimApprover; } public abstract void ApproveRequest(Claim claim); }
Қолдану жағдайына сәйкес, «кіші/аға» сыныбын талап етушіге көшейік. Қызметкерлердің бұл сыныбы/тағайындауы ешқандай шағымдарды бекіте алмайтынын ескеріңіз.
public class Junior : ClaimApprover { public override void ApproveRequest(Claim claim) { System.Console.WriteLine("Cannot approve"); } }
Сол сияқты, менеджер және аға менеджер рөлдері үшін іске асыруды анықтайық.
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); } } }
Сома ауқымына негізделген, егер Менеджер ауқымында болса, шағымды Менеджер мақұлдауы мүмкін екенін ескеріңіз; әйтпесе, сұрау Аға менеджерге жіберіледі.
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"); } } }
Сол сияқты, егер сома ауқымы Аға менеджер ауқымында болса, шағымды Менеджер мақұлдай алады; әйтпесе, иерархияның соңғысы болғандықтан, ауқымнан тыс сома үшін ерекше бекіту орындалады.
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
1-жолдың шығысы үшін сома ауқымда болды, сондықтан менеджер оны бекітті.
2-жолдың шығысы үшін, аға менеджер оны мақұлдағанымен, сома ауқымнан тыс болды.
Gang of Four пікірінше, үлгі сынып нысанына динамикалық түрде қосымша жауапкершіліктер қосады.
Құны он миллион тұратын көлікті сатып алу мысалын қарастырайық; компания келесі қосымша мүмкіндіктерді ұсынады.
Кейбір қосымша мүмкіндіктермен автомобильдің жалпы бағасы артады. Жоғарыдағы пайдалану жағдайын Декоратор үлгісін қолданып жүзеге асырайық.
Жоғарыда анықталған пайдалану жағдайын жүзеге асырайық. Біріншіден, абстрактілі Car класын және оның негізгі әдістерін анықтаңыз.
public abstract class Car{ public abstract int CarPrice(); public abstract string GetName(); }
Автокөлік абстрактілі класынан жоғары орналасқан шағын көлікті қарастырайық.
public class SmallCar : Car{ public override int CarPrice() => 10000; public override string GetName() => "Alto Lxi"; }
Енді Car компонентін пайдаланып CarDecorator класын іске асырыңыз.
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(); }
Енді CarDecorator сыныбын иемденетін Автокөлік үшін қолжетімді әрбір қосымша мүмкіндік үшін бөлек класс жасайық.
Қолдану жағдайына сәйкес, қосымша мүмкіндіктер люк пен жетілдірілген музыка жүйесі болып табылады.
Әдістерді қайта белгілеңіз
Автокөліктің жалпы бағасына «Жетілдірілген музыкалық жүйенің» қосымша құнын қосыңыз.
Көлік атауын қосымша функция атауымен жаңартыңыз.
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"; }
Әдістерді қайта белгілеңіз
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"; }
SmallCar данасын жасаңыз және көліктің атауы мен бағасын шығарыңыз.
Car car = new SmallCar(); Console.WriteLine($"Price of car {car.GetName()} : " + car.CarPrice());
Енді төменде көрсетілгендей қосымша мүмкіндіктерді қосамыз
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()); }
Құттықтаймын..!! Декоратор үлгісін пайдаланып пайдалану жағдайын сәтті орындадыңыз.
«Төрттік тобына» сәйкес, зауыттық әдіс ішкі сыныпқа қандай сынып объектісін жасау керектігін анықтауға мүмкіндік береді.
Жинақ және Ағымдағы шоттар сияқты шот түрлері бар кез келген банктің мысалын қарастырайық. Енді жоғарыдағы мысалды зауыттық дизайн үлгісін қолдана отырып жүзеге асырайық
Біріншіден, тіркелгі түріндегі дерексіз класс жасаңыз.
public abstract class AccoutType { public string Balance { get; set; } }
Төменде көрсетілгендей AccountType дерексіз сыныбын иеленетін ағымдағы және сақтау шотының сыныптарын енгізіңіз.
public class SavingsAccount : AccoutType { public SavingsAccount() { Balance = "10000 Rs"; } } public class CurrentAccount : AccoutType { public CurrentAccount() { Balance = "20000 Rs"; } }
Соңында, класс нысанын жасауға көмектесетін келісім-шартты қамтамасыз ететін зауыттық интерфейсті іске асырамыз. Бұл интерфейс Жаратушы ретінде де белгілі.
public interface IAccountFactory { AccoutType GetAccoutType(string accountName); }
Соңында, төменде көрсетілгендей жасаушы интерфейсі әдісінің орындалуын жазыңыз. Жасаушыны іске асыратын класс 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"); } } }
Міне бітті. Банк үлгісін пайдаланып зауыттық әдісті сәтті енгіздіңіз.
Қосалқы сынып тіркелгі атауына негізделген қандай «AccountType» сынып нысаны жасалатынын шешеді.
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); } }
Мысалы, егер тіркелгі атауы «SAVINGS» болса, «SavingAccount» сыныбының нысаны жасалады және қайтарылады.
Сол сияқты, егер тіркелгі атауы «CURRENT» болса, «CurrentAccount» сыныбының нысаны жасалады және қайтарылады.
Saving account balance: 10000 Rs Current account balance: 20000 Rs
Gang of Four пікірінше, итератор үлгісі агрегатор нысанын оның орындалуын білмей алу процесін қамтамасыз етеді.
Автокөліктердің коллекциялық тізімін және string[] мотоциклдер массивінің мысалын алайық, біз жиынтықты оның тізім немесе массив екенін білмей қайталай алатындай етіп құрастыруымыз керек.
Итератор дизайн үлгісі стандартты итератор әртүрлі жинақ түрлерін аралайтын осы мәселені шешуге көмектеседі.
Жоғарыдағы пайдалану жағдайын қарастыра отырып, тізім мен массив итераторы үстінде дерексіз қабат ретінде әрекет ететін пайдаланушы итератор интерфейсін анықтайық.
public interface IVehicleIterator{ void First(); bool IsDone(); string Next(); string Current(); }
Енді пайдалану жағдайына сәйкес жоғарыдағы интерфейсті жүзеге асыратын автомобиль және мотоцикл итераторларын жазыңыз.
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++); } }
Автокөлік итераторы List<string> жиыны арқылы жүзеге асырылады және интерфейс әдістерін жүзеге асыруды қамтамасыз етеді.
Мотоцикл итераторы string[] жинағы арқылы жүзеге асырылады және интерфейс әдістерін іске асыруды қамтамасыз етеді.
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++]; } }
Жоғарыда аталған барлық иераторлар анықталғаннан кейін итераторларды жасайтын стандартты агрегатор нысан интерфейсін анықтаңыз.
public interface IVehicleAggregate{ IVehicleIterator CreateIterator(); }
Соңында, жоғарыдағы агрегатор интерфейсін жүзеге асыратын сыныптарды жазыңыз. Қолдану жағдайына сәйкес, автомобиль және мотоцикл сыныптары агрегатор интерфейсін жүзеге асырады.
Агрегатор интерфейсінің әдісі төменде көрсетілгендей сәйкес итераторды қайтарады.
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); } }
Агрегатор интерфейсінің әдісі төменде көрсетілгендей сәйкес итераторды қайтарады.
public class Motercycle : IVehicleAggregate { private string[] _motercycles; public Motercycle() { _motercycles = new[] { "Bike 1", "Bike 2", "Bike 3" }; } public IVehicleIterator CreateIterator() { return new MotercycleIterator(_motercycles); } }
PrintVehicles әдістері !iterator.isDone, содан кейін жинақ элементін шығаратынын тексереді. Қандай жинақпен айналысатынымызға қарамастан, First, IsDone және 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()); } }
Біз жинақтың негізгі түрін білмейміз, бірақ ол әлі де Итератор дизайн үлгісі арқылы қайталанады. Жалғастырып, іске қоссаңыз, ол келесі нәтижені көрсетеді.
Gang of Four пікірінше, Медиатор үлгісі объектінің бір-бірімен әрекеттесуін инкапсуляциялайды.
Медиаторды жобалау үлгісі бізге объектінің өзара әрекеттесуін инкапсуляциялау арқылы еркін байланысқан қолданбаларды жобалауға көмектеседі.
Қатысушылар тіркелетін сөйлесу бөлмесінің мысалын және қалай тиімді байланысуға болатынын қарастырайық.
Медиатор дизайн үлгісін пайдаланып келесі сөйлесу бөлмесін енгізу қажет.
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?'
Негізгі қадам - сөйлесу бөлмесінде пайдаланылатын пайдаланушы аттары тізімін жасау. Бұл үшін жалпы тізім төменде көрсетілген.
public enum Username{ Ashley, David, Jennifer, Scott }
Енді, ең алдымен, сөйлесу бөлмесінің абстрактілі қабатын іске асырыңыз.
public abstract class AChatroom { public abstract void Register(User user); public abstract void Post(string fromUser, string toUser, string msg); }
Және абстрактілі әдістерді анықтайтын класс. Әдістер пайдаланушының сөздікте бар-жоғын растайды. Мысалы, тіркеу әдісі пайдаланушының бұрыннан бар-жоғын тексереді. Егер жоқ болса, пайдаланушыны сөйлесу бөлмесінде ғана тіркеңіз.
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; } }
Соңында, сөйлесу бөлмесінде пайдаланушыға хабарлама жіберу немесе басқа пайдаланушыдан хабарлама алу сияқты пайдаланушы орындай алатын әрекеттерді орындайық.
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(); }
Бағдарламаның орындалуы тек пайдаланушы класының Post әдісін сипаттайды.
Шығару: Жоғарыдағы бағдарламаның орындалуының чат бөлмесінің тарихы
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?'
Gang of Four сәйкес, бақылаушы үлгісі екі немесе одан да көп нысандарға тәуелділікті анықтайды. Сонымен, бір нысан күйі өзгерген кезде оның барлық тәуелділері хабарланады.
Басқаша айтқанда, бір нысандағы өзгеріс басқа нысандағы хабарландыруды бастайды.
Жазушыларының « x » саны бар Instagram атақты ықпал етушісінің мысалын алайық. Осылайша, атақты адам жазба қосқан сәтте, барлық жазылушыларға хабарланады.
Жоғарыда аталған қолдану жағдайын Observer Design Pattern көмегімен жүзеге асырайық.
Пайдалану жағдайына сәйкес, бірінші атақты адам орындай алатын әрекеттерді қамтитын интерфейсті жүзеге асырады. Ол « Тақырып » ретінде белгілі.
public interface ICelebrityInstagram{ string FullName { get; } string Post { get; set; } void Notify(string post); void AddFollower(IFollower fan); void RemoveFollower(IFollower fan); }
Хабарландыру: барлық жазылушыларды хабардар ету.
AddFollower: атақтылар тізіміне жаңа жазылушы қосыңыз.
RemoveFollower: атақтылар тізімінен жазылушыны алып тастаңыз.
Енді хабарландыру үшін «Жаңарту» мүше функциясын қамтитын бақылаушы «IFollower» интерфейсін іске қосыңыз.
public interface IFollower{ void Update(ICelebrityInstagram celebrityInstagram); }
Ақырында, « Тақырып » және « Байқаушы » үшін де «Нақты іске асыруды» жүзеге асыру уақыты келді.
Ол консольге атақты адамның атын және жазбасын шығаратын Жаңарту мүшесі функциясының орындалуын қамтамасыз етеді.
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); } }
Келесі пайдалану жағдайы төменде көрсетілген мәлімдеме орындалған сайын sukhpinder.Post = “Мен дизайн үлгілерін жақсы көремін.”; Жаңарту әдісі әрбір жазылушы үшін іске қосылады, яғни әрбір ізбасар нысаны «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(); }
Мақалада үлгіні сәйкестендіру бастапқы жүйенің бөлігі болып табылмайтын пішіндердегі деректерді пайдалану және өңдеудің тиімді жолын қалай қамтамасыз ететінін сипаттайды.
Төлем калькуляторының мысалын алайық және үлгіні сәйкестендіру алгоритмді жазуға қалай көмектесетінін көрейік.
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; } }
1-мысал: Төмендегі шарттарға сәйкес жол ақысын есептеңіз:
- Егер көлік автомобиль болса => 100 Rs
- Егер көлік DeliveryTruck болса => 200 Rs
- Көлік автобус болса => 150 Rs
- Көлік такси болса => 120 руб
Көлік түрі Car 100-ге сәйкес келсе, қайтарылады және т.б. Нөл және {} нысан түрі үшін әдепкі жағдайлар екенін ескеріңіз.
Сондай-ақ, «_» әдепкі сценарийді бағдарламалау үшін пайдаланылуы мүмкін. Жаңа қосқыш синтаксисін қараңыз.
Бұл кодтаудың әлдеқайда таза және тиімді әдісі және коммутатор синтаксисінде бір әріпті айнымалы атауларды пайдалану ұсынылады.
public static int TollFare(Object vehicleType) => vehicleType switch { Car c => 100, DeliveryTruck d => 200, Bus b => 150, Taxi t => 120, null => 0, { } => 0 };
Консоль қолданбасы тұрғысынан сынақ мысалдары. Төмендегі код жоғарыдағы үлгіні сәйкестендіру функциясын негізгі әдістен қалай шақыру керектігін көрсетеді.
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
2-мысал: Көлік түріне қарай тұру бағасын қосыңыз
- «ЖОҚ» жолаушылары бар автомобильдер мен таксилер қосымша 10 рупий төлейді.
- Екі жолаушы бар көліктер мен таксилерге 10 рупий жеңілдік беріледі.
- Үш немесе одан да көп жолаушысы бар автомобильдер мен таксилерге 20 рупийлік жеңілдік беріледі.
- Жолаушылар саны 50%-дан аз автобустар қосымша 30 рупий төлейді.
- Жолаушыларының 90%-дан астамы бар автобустар 40 рупий жеңілдік алады.
- 5000 фунттан асатын жүк көліктерінен қосымша 100 рупий алынады.
- 3000 фунттан төмен жеңіл жүк көліктеріне 20 рупийлік жеңілдік берілген.
Жалғыз және бірнеше сипат кластары бар үлгі сәйкестік синтаксисін қараңыз. Сілтеме
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,
Төмендегі мысал үлгіні сәйкестендірудің артықшылықтарын көрсетеді: үлгі тармақтары ретімен құрастырылған. Сондай-ақ компилятор қол жетімсіз код туралы ескертеді.
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, };
Консоль қолданбасы тұрғысынан сынақ мысалдары. Төмендегі код жоғарыдағы үлгіні сәйкестендіру функциясын негізгі әдістен қалай шақыру керектігін көрсетеді.
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
«Үлгілерді сәйкестендіру кодты оқуға ыңғайлы етеді және сыныптарыңызға код қосу мүмкін болмаған кезде объектіге бағытталған әдістерге балама ұсынады.»
Gang of Four — Singleton дизайн үлгісі белгілі бір сыныптың тек бір данасы/нысаны және жаһандық кіру нүктесі болуын қамтамасыз етеді.
Синглтондық сыныптар белгілі бір сыныптың бірнеше нысанының данасын жою үшін қолданылады.
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; }
Синглтон класын екі рет шақырайық және қайтарылған дананы екі түрлі айнымалыға тағайындайық. Соңында, theObject.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)); }
Консоль шығысы шын мәнін қайтарады; құттықтаймын. Сіз Singleton үлгісін сәтті енгіздіңіз.
Жоғарыда аталған класс синглондық класс ретінде белгілі, бірақ қазіргі уақытта ол ағынмен қауіпсіз емес. Көп ағынды ортада екі ағын if (_instance == null) операторын бір уақытта тигізуі мүмкін және бізде синглондық сыныптың бірнеше даналары болады.
Қауіпсіз ағынның бір жолы - құлыптау механизмін пайдалану, ал екінші жолы - таза және тиімді тәсіл үшін тек оқуға арналған дананы жасау.
public class ThreadSafeSingleton { private static readonly ThreadSafeSingleton _instance = new ThreadSafeSingleton(); public static ThreadSafeSingleton Instance { get { return _instance; } } public ThreadSafeSingleton() { } }
https://github.com/ssukhpinder/DesignPatterns
Демеушілік маған осындай жаңа жобаларды жалғастыруға және құруға көмектеседі.
🙏 Егер сіз Pay, Noticed немесе менің басқа жобаларымның бірін пайдалансаңыз, аздаған үлесіңіз ТҮЛ КӨП дегенді білдіреді. Өздігінен ашық бастапқы код шоттарды төлемейді. Сіздің көмегіңізбен жұмысымды жалғастыру тұрақты болады және мен нақты жұмысқа барудың қажеті жоқ деп үміттенемін 😛.
C# қауымдастығының бөлігі болғаныңыз үшін рахмет!