├── Patterns ├── Proxy │ ├── IUser.cs │ ├── IAuthenticationController.cs │ ├── IEntries.cs │ ├── IProductInfo.cs │ ├── Proxy.csproj │ ├── AuthenticationController.cs │ ├── Entries.cs │ └── EntriesProxy.cs ├── FactoryMethod │ ├── IPerks.cs │ ├── FactoryMethod.csproj │ ├── BasicPerks.cs │ ├── GoldPerks.cs │ ├── SilverPerks.cs │ ├── EarningBonusCalculatorRefactored.cs │ ├── EarningBonusCalculatorLegacy.cs │ └── PerksFactory.cs ├── Observer │ ├── IObserver.cs │ ├── Observer.csproj │ ├── Customer.cs │ ├── ISubject.cs │ └── SpecialsSubject.cs ├── Iterator │ ├── MatrixDirection.cs │ ├── Iterator.csproj │ ├── AggregateObject.cs │ ├── IMatrix.cs │ ├── GenericIterator.cs │ ├── NumberMatrix.cs │ └── MatrixIterator.cs ├── Flyweight │ ├── TextBlob.cs │ ├── TextBlobFactory.cs │ ├── Flyweight.csproj │ ├── TextDownloader.cs │ ├── XmlTextBlob.cs │ └── XmlTextBlobFactory.cs ├── Command │ ├── ICommand.cs │ ├── IProduct.cs │ ├── IInvoker.cs │ ├── Command.csproj │ ├── IProductList.cs │ ├── ClearCommand.cs │ ├── RemoveCommand.cs │ ├── AddCommand.cs │ └── ProductCommandInvoker.cs ├── Mediator │ ├── IAlertScreen.cs │ ├── Mediator.csproj │ ├── Product.cs │ ├── IMediator.cs │ ├── IPurchaser.cs │ ├── PurchaseMediator.cs │ └── Purchaser.cs ├── SingletonDependencyInjection │ ├── ILogger.cs │ ├── SingletonDI.csproj │ ├── Logger.cs │ └── LazyLogger.cs ├── State │ ├── IProduct.cs │ ├── State.csproj │ ├── ICouponState.cs │ ├── ICoupon.cs │ ├── InvalidCouponState.cs │ ├── ValidCouponState.cs │ └── Coupon.cs ├── TemplateMethod │ ├── ITaxBracket.cs │ ├── TemplateMethod.csproj │ ├── IBook.cs │ ├── BookPriceCalculator.cs │ ├── LowIncomeBookPriceCalculator.cs │ └── HighIncomeBookPriceCalculator.cs ├── Strategy │ ├── IDiscountScheme.cs │ ├── ICoupon.cs │ ├── IProduct.cs │ ├── Strategy.csproj │ ├── NonMemberDiscountScheme.cs │ ├── business_logic.txt │ └── MemberDiscountScheme.cs ├── Bridge │ ├── IPayment.cs │ ├── Bridge.csproj │ ├── IPaymentGateway.cs │ ├── Order.cs │ ├── PurchaseOrder.cs │ ├── PaypalPayment.cs │ └── CreditCardPayment.cs ├── Decorator │ ├── IHtmlElement.cs │ ├── Decorator.csproj │ ├── HtmlElement.cs │ ├── BoldenHtmlElement.cs │ ├── ItalicizeHtmlElement.cs │ ├── HtmlElementDecorator.cs │ └── HyperLinkifyHtmlElement.cs ├── Visitor │ ├── IVisitor.cs │ ├── Visitor.csproj │ ├── GameElement.cs │ ├── Instruction.cs │ ├── TextElement.cs │ ├── Revealer.cs │ ├── Concealer.cs │ └── Sprite.cs ├── Facade │ ├── IPaymentProcessor.cs │ ├── Facade.csproj │ ├── IMerchantAuthenticationType.cs │ ├── ITransactionRequest.cs │ ├── IBillingAddress.cs │ ├── IEnvironment.cs │ ├── ITransactionController.cs │ ├── ICreditCard.cs │ ├── business_logic.txt │ └── PaymentProcessor.cs ├── Memento │ ├── IProductOriginator.cs │ ├── IProductCaretaker.cs │ ├── Memento.csproj │ ├── Product.cs │ ├── ProductOriginator.cs │ └── ProductCaretaker.cs ├── Adapter │ ├── Adapter.csproj │ ├── IGoodReadsProfile.cs │ ├── ISocialMediaProfile.cs │ └── SocialMediaProfileAdapter.cs ├── Builder │ ├── Builder.csproj │ ├── Product.cs │ ├── IDIscountStrategy.cs │ ├── SkuCodeStartDiscountStrategyBuilder.cs │ ├── SkuCodeStartDiscountStrategy.cs │ └── DiscountStrategyBuilder.cs ├── Composite │ ├── Composite.csproj │ ├── IBook.cs │ ├── Book.cs │ └── BookComposite.cs ├── Prototype │ ├── Prototype.csproj │ ├── Address.cs │ ├── BasicCustomer.cs │ └── Customer.cs ├── AbstractFactory │ ├── AbstractFactory.csproj │ ├── IProcessor.cs │ ├── IStorage.cs │ ├── ILaptopPartsFactory.cs │ ├── AMDProcessor.cs │ ├── HardDrive.cs │ ├── IntelProcessor.cs │ ├── SolidStateDrive.cs │ ├── LenovoPartsFactory.cs │ └── DellPartsFactory.cs └── ChainOfResponsibility │ ├── IPaymentGateway.cs │ ├── ChainOfResponsibility.csproj │ ├── ICreditCardHandler.cs │ ├── ICreditCard.cs │ ├── CreditCardHandlerBase.cs │ ├── AmexCardHandler.cs │ ├── VisaCardHandler.cs │ └── MastercardHandler.cs ├── .gitignore ├── LICENSE ├── Tests ├── TemplateMethodTest.cs ├── FlyweightTest.cs ├── StateTest.cs ├── AdapterTest.cs ├── SingletonDITest.cs ├── BridgeTest.cs ├── MementoTest.cs ├── ObserverTest.cs ├── VisitorTest.cs ├── BuilderTest.cs ├── CompositeTest.cs ├── PrototypeTest.cs ├── Tests.csproj ├── AbstractFactory │ └── AbstractFactoryTest.cs ├── ProxyTest.cs ├── FactoryMethodTest.cs ├── FacadeTestData.cs ├── IteratorTest.cs ├── FacadeTest.cs ├── MediatorTest.cs ├── ChainOfResponsibilityTest.cs ├── StrategyTest.cs ├── DecoratorTest.cs └── CommandTest.cs ├── .github └── workflows │ └── dotnet.yml ├── README.md └── DesignPatterns.sln /Patterns/Proxy/IUser.cs: -------------------------------------------------------------------------------- 1 | namespace Proxy 2 | { 3 | public interface IUser 4 | { 5 | bool IsAdmin {get;set;} 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Patterns/FactoryMethod/IPerks.cs: -------------------------------------------------------------------------------- 1 | namespace FactoryMethod 2 | { 3 | public interface IPerks 4 | { 5 | decimal EarningBonus(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Patterns/Observer/IObserver.cs: -------------------------------------------------------------------------------- 1 | namespace Observer 2 | { 3 | public interface IObserver 4 | { 5 | string Update(ISubject subject); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Patterns/Iterator/MatrixDirection.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Iterator 3 | { 4 | public enum MatrixDirection 5 | { 6 | ByRow, 7 | ByColumn 8 | } 9 | } -------------------------------------------------------------------------------- /Patterns/Flyweight/TextBlob.cs: -------------------------------------------------------------------------------- 1 | namespace Flyweight 2 | { 3 | public interface TextBlob 4 | { 5 | void Download(int id, string blobContent); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Patterns/Proxy/IAuthenticationController.cs: -------------------------------------------------------------------------------- 1 | namespace Proxy 2 | { 3 | public interface IAuthenticationController 4 | { 5 | bool IsAdmin(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Patterns/Command/ICommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Command 4 | { 5 | public interface ICommand 6 | { 7 | void Execute(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Patterns/Mediator/IAlertScreen.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator 2 | { 3 | public interface IAlertScreen 4 | { 5 | void ShowMessage(string item, string location); 6 | } 7 | } -------------------------------------------------------------------------------- /Patterns/SingletonDependencyInjection/ILogger.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SingletonDI 3 | { 4 | public interface ILogger 5 | { 6 | void Log(string msg); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Patterns/State/IProduct.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace State 4 | { 5 | public interface IProduct 6 | { 7 | decimal Price {get;} 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Patterns/Command/IProduct.cs: -------------------------------------------------------------------------------- 1 | namespace Command 2 | { 3 | public interface IProduct 4 | { 5 | string Name { get; set; } 6 | decimal Price { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /Patterns/TemplateMethod/ITaxBracket.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | namespace TemplateMethod 5 | { 6 | public interface ITaxBracket 7 | { 8 | int TaxPercent(); 9 | } 10 | } -------------------------------------------------------------------------------- /Patterns/Strategy/IDiscountScheme.cs: -------------------------------------------------------------------------------- 1 | namespace Strategy 2 | { 3 | public interface IDiscountScheme 4 | { 5 | decimal ComputePrice(IProduct product, ICoupon coupon); 6 | } 7 | } -------------------------------------------------------------------------------- /Patterns/Bridge/IPayment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Bridge 4 | { 5 | public interface IPayment 6 | { 7 | void SubmitPayment(decimal amount); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Patterns/Decorator/IHtmlElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Decorator 4 | { 5 | public interface IHtmlElement 6 | { 7 | string GetHtmlElement(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Patterns/Proxy/IEntries.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Proxy 3 | { 4 | public interface IEntries 5 | { 6 | bool Delete(int id); 7 | IProductInfo? Get(int id); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Patterns/Visitor/IVisitor.cs: -------------------------------------------------------------------------------- 1 | namespace Visitor 2 | { 3 | public interface IVisitor 4 | { 5 | void Visit(GameElement gameObject); 6 | void Visit(TextElement textObject); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Patterns/Flyweight/TextBlobFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Flyweight 4 | { 5 | public interface TextBlobFactory 6 | { 7 | TextBlob GetTextBlob(int id); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Patterns/Proxy/IProductInfo.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Proxy 3 | { 4 | public interface IProductInfo 5 | { 6 | string Name { get; set; } 7 | int Id { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Patterns/Command/IInvoker.cs: -------------------------------------------------------------------------------- 1 | namespace Command 2 | { 3 | public interface IInvoker 4 | { 5 | bool AddCommand(string key, ICommand command); 6 | void InvokeCommand(string key); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Patterns/Facade/IPaymentProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Facade 2 | { 3 | public interface IPaymentProcessor 4 | { 5 | void InitializePaymentGatewayInterface(); 6 | bool SubmitPayment(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Patterns/Memento/IProductOriginator.cs: -------------------------------------------------------------------------------- 1 | namespace Memento 2 | { 3 | public interface IProductOriginator 4 | { 5 | void SetMemento(Product product); 6 | Product GetMemento(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Patterns/Strategy/ICoupon.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Strategy 4 | { 5 | public interface ICoupon 6 | { 7 | int DiscountPercentage(); 8 | bool IsExpired(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Patterns/Adapter/Adapter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Bridge/Bridge.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Builder/Builder.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Command/Command.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Facade/Facade.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Memento/IProductCaretaker.cs: -------------------------------------------------------------------------------- 1 | namespace Memento 2 | { 3 | public interface IProductCaretaker 4 | { 5 | void AddProductMemento(Product product); 6 | Product GetLastMemento(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Patterns/Memento/Memento.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Proxy/Proxy.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/State/State.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Visitor/Visitor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Bridge/IPaymentGateway.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Bridge 4 | { 5 | public interface IPaymentGateway 6 | { 7 | void ProcessPayment(decimal amount, IPayment payment); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Patterns/Composite/Composite.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Decorator/Decorator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Flyweight/Flyweight.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Flyweight/TextDownloader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Flyweight 4 | { 5 | public interface TextDownloader 6 | { 7 | void DownloadFile(string url, string blobContent); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Patterns/Iterator/Iterator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Mediator/Mediator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Observer/Observer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Prototype/Prototype.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Strategy/IProduct.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | 4 | namespace Strategy 5 | { 6 | public interface IProduct 7 | { 8 | decimal SellingPrice(); 9 | 10 | bool IsOnSale(); 11 | } 12 | } -------------------------------------------------------------------------------- /Patterns/Strategy/Strategy.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Facade/IMerchantAuthenticationType.cs: -------------------------------------------------------------------------------- 1 | namespace Facade 2 | { 3 | public interface IMerchantAuthenticationType 4 | { 5 | string LoginID {get;set;} 6 | string TransactionKey {get;set;} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Patterns/FactoryMethod/FactoryMethod.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Mediator/Product.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator 2 | { 3 | public sealed class Product 4 | { 5 | public string Item { get; set; } = string.Empty; 6 | public string Location { get; set; } = string.Empty; 7 | } 8 | } -------------------------------------------------------------------------------- /Patterns/TemplateMethod/TemplateMethod.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/AbstractFactory/AbstractFactory.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/AbstractFactory/IProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | //TODO: Use Generic Constraints 4 | public interface IProcessor 5 | { 6 | string BrandName(); 7 | double SpeedInGigaHertz(); 8 | } 9 | } -------------------------------------------------------------------------------- /Patterns/ChainOfResponsibility/IPaymentGateway.cs: -------------------------------------------------------------------------------- 1 | namespace ChainOfResponsibility 2 | { 3 | public interface IPaymentGateway 4 | { 5 | bool SubmitVerification(ICreditCardHandler creditCardHandler, ICreditCard card); 6 | } 7 | } -------------------------------------------------------------------------------- /Patterns/FactoryMethod/BasicPerks.cs: -------------------------------------------------------------------------------- 1 | namespace FactoryMethod 2 | { 3 | public class BasicPerks : IPerks 4 | { 5 | public decimal EarningBonus() 6 | { 7 | return 0m; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Patterns/FactoryMethod/GoldPerks.cs: -------------------------------------------------------------------------------- 1 | namespace FactoryMethod 2 | { 3 | public class GoldPerks : IPerks 4 | { 5 | public decimal EarningBonus() 6 | { 7 | return 1.0m; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Patterns/SingletonDependencyInjection/SingletonDI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/Adapter/IGoodReadsProfile.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Mail; 2 | 3 | namespace Adapter 4 | { 5 | public interface IGoodReadsProfile 6 | { 7 | string Name { get; } 8 | MailAddress Email { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Patterns/ChainOfResponsibility/ChainOfResponsibility.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Patterns/FactoryMethod/SilverPerks.cs: -------------------------------------------------------------------------------- 1 | namespace FactoryMethod 2 | { 3 | public class SilverPerks : IPerks 4 | { 5 | public decimal EarningBonus() 6 | { 7 | return 0.5m; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Patterns/Iterator/AggregateObject.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | 3 | namespace Iterator 4 | { 5 | public abstract class AggregateObject : IEnumerable 6 | { 7 | public abstract IEnumerator GetEnumerator(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Patterns/Command/IProductList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Command 4 | { 5 | public interface IProductList 6 | { 7 | string Name { get; set; } 8 | List Products { get; set;} 9 | } 10 | } -------------------------------------------------------------------------------- /Patterns/Iterator/IMatrix.cs: -------------------------------------------------------------------------------- 1 | namespace Iterator 2 | { 3 | public interface IMatrix 4 | { 5 | T this[int row, int column] {get;set;} 6 | 7 | int TotalColumns(); 8 | int TotalRows(); 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Patterns/AbstractFactory/IStorage.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | //TODO: Use Generic Constraints 4 | public interface IStorage 5 | { 6 | string HardwareType(); 7 | int ReadSpeedInMBytesPerSec(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Patterns/Composite/IBook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Composite 4 | { 5 | public interface IBook 6 | { 7 | decimal Price { get; } 8 | string Name { get; } 9 | 10 | int Discount { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Patterns/State/ICouponState.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | 4 | namespace State 5 | { 6 | public interface ICouponState 7 | { 8 | void ChangeExpiryDate(DateTime date, ICoupon coupon); 9 | bool UseDiscount(); 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /Patterns/TemplateMethod/IBook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TemplateMethod 4 | { 5 | public interface IBook 6 | { 7 | decimal Price { get; } 8 | string Name { get; } 9 | int Discount { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Patterns/Visitor/GameElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Visitor 4 | { 5 | public abstract class GameElement 6 | { 7 | public bool Active { get; set; } = false; 8 | public abstract void Accept(IVisitor visitor); 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Patterns/Visitor/Instruction.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Visitor 3 | { 4 | public class Instruction : TextElement 5 | { 6 | public override void Accept(IVisitor visitor) 7 | { 8 | visitor.Visit(this); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Patterns/Mediator/IMediator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mediator 4 | { 5 | public interface IMediator 6 | { 7 | bool BroadcastPurchaseCompletion(IPurchaser purchaser); 8 | bool AddPurchaser(IPurchaser purchaser); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Patterns/Mediator/IPurchaser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mediator 4 | { 5 | public interface IPurchaser 6 | { 7 | void Receive(IPurchaser purchaser); 8 | void Complete(Product product); 9 | Product? GetProduct(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Patterns/AbstractFactory/ILaptopPartsFactory.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace AbstractFactory 3 | { 4 | // TODO: Use Generic Constraints 5 | public interface ILaptopPartsFactory 6 | { 7 | IStorage CreateStorage(); 8 | IProcessor CreateProcessor(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Patterns/ChainOfResponsibility/ICreditCardHandler.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace ChainOfResponsibility 3 | { 4 | public interface ICreditCardHandler 5 | { 6 | ICreditCardHandler SetNext(ICreditCardHandler creditCardHandler); 7 | bool IsCreditCardValid(ICreditCard card); 8 | } 9 | } -------------------------------------------------------------------------------- /Patterns/Facade/ITransactionRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Facade 2 | { 3 | public interface ITransactionRequest 4 | { 5 | decimal Amount{get;set;} 6 | IBillingAddress BillingAddress{get;set;} 7 | 8 | ICreditCard CreditCard {get;set;} 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Patterns/Visitor/TextElement.cs: -------------------------------------------------------------------------------- 1 | namespace Visitor 2 | { 3 | public abstract class TextElement 4 | { 5 | public abstract void Accept(IVisitor visitor); 6 | public bool Active { get; set; } = false; 7 | public string Message { get; set; } = string.Empty; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Patterns/Builder/Product.cs: -------------------------------------------------------------------------------- 1 | namespace Builder 2 | { 3 | public class Product 4 | { 5 | public required string StockKeepingUnit { get; set; } 6 | public decimal RegularRetailPrice { get; set; } 7 | public required string Name { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Patterns/SingletonDependencyInjection/Logger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SingletonDI 4 | { 5 | public class Logger : ILogger 6 | { 7 | public void Log(string msg) 8 | { 9 | Console.WriteLine($"LOG: {msg}"); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Patterns/Adapter/ISocialMediaProfile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Mail; 3 | 4 | namespace Adapter 5 | { 6 | public interface ISocialMediaProfile 7 | { 8 | string Name { get; } 9 | string UserName { get; } 10 | MailAddress Email { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Patterns/Observer/Customer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Observer 4 | { 5 | public class Customer : IObserver 6 | { 7 | public string Update(ISubject subject) 8 | { 9 | return $"Customer received {subject.SubjectState}"; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Patterns/State/ICoupon.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace State 4 | { 5 | public interface ICoupon 6 | { 7 | void UpdateExpiryDate(DateTime dateTime); 8 | decimal ApplyDiscountTo(IProduct product); 9 | 10 | void SetCouponState(ICouponState state); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Patterns/Facade/IBillingAddress.cs: -------------------------------------------------------------------------------- 1 | namespace Facade 2 | { 3 | public interface IBillingAddress 4 | { 5 | string FirstName {get;set;} 6 | string LastName {get; set;} 7 | string Address {get;set;} 8 | string City {get;set;} 9 | string ZipCode{get;set;} 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Patterns/Builder/IDIscountStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Builder 4 | { 5 | public interface IDiscountStrategy 6 | { 7 | int DiscountInPercentage { get; set; } 8 | string SkuCode {get; set;} 9 | decimal CalculateDiscountedRetailPrice(Product product); 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Patterns/Observer/ISubject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Observer 4 | { 5 | public interface ISubject 6 | { 7 | void Attach(IObserver observer); 8 | 9 | void Detach(IObserver observer); 10 | 11 | void Notify(); 12 | 13 | string SubjectState {get; set;} 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Patterns/Builder/SkuCodeStartDiscountStrategyBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Builder 2 | { 3 | public class SkuCodeStartDiscountStrategyBuilder : DiscountStrategyBuilder 4 | { 5 | public SkuCodeStartDiscountStrategyBuilder() 6 | : base(new SkuCodeStartDiscountStrategy()) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Patterns/Visitor/Revealer.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Visitor 3 | { 4 | public class Revealer : IVisitor 5 | { 6 | public void Visit(GameElement gameObject) 7 | { 8 | gameObject.Active = true; 9 | } 10 | 11 | public void Visit(TextElement textElement) 12 | { 13 | textElement.Active = true; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Patterns/Visitor/Concealer.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Visitor 3 | { 4 | public class Concealer : IVisitor 5 | { 6 | public void Visit(GameElement gameObject) 7 | { 8 | gameObject.Active = false; 9 | } 10 | 11 | public void Visit(TextElement textElement) 12 | { 13 | textElement.Active = false; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Patterns/AbstractFactory/AMDProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public class AmdProcessor : IProcessor 4 | { 5 | public string BrandName() 6 | { 7 | return "AMD"; 8 | } 9 | public double SpeedInGigaHertz() 10 | { 11 | return 2.1; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Patterns/AbstractFactory/HardDrive.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | 4 | public class HardDrive : IStorage 5 | { 6 | public string HardwareType() 7 | { 8 | return "hdd"; 9 | } 10 | public int ReadSpeedInMBytesPerSec() 11 | { 12 | return 50; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Patterns/Facade/IEnvironment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Facade 4 | { 5 | public enum EnvironmentTarget 6 | { 7 | UNINITIALIZED, 8 | SANDBOX, 9 | PRODUCTiON 10 | } 11 | public interface IEnvironment 12 | { 13 | EnvironmentTarget environmentVariableTarget {get;set;} 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Patterns/Visitor/Sprite.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Visitor 3 | { 4 | public class Sprite : GameElement 5 | { 6 | public string Name { get; } 7 | public Sprite(string name) { Name = name; } 8 | public override void Accept(IVisitor visitor) 9 | { 10 | visitor.Visit(this); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Patterns/AbstractFactory/IntelProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public class IntelProcessor : IProcessor 4 | { 5 | public string BrandName() 6 | { 7 | return "Intel"; 8 | } 9 | public double SpeedInGigaHertz() 10 | { 11 | return 1.8; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Patterns/Bridge/Order.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Bridge 4 | { 5 | public abstract class Order 6 | { 7 | protected IPayment Payment { get; } 8 | public Order(IPayment payment) 9 | { 10 | Payment = payment; 11 | } 12 | public abstract void Checkout(decimal amount); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Patterns/Memento/Product.cs: -------------------------------------------------------------------------------- 1 | namespace Memento 2 | { 3 | public class Product 4 | { 5 | public string Name { get; set; } = string.Empty; 6 | public decimal Price { get; set; } 7 | 8 | public Product ShallowCopy() 9 | { 10 | return (Product)this.MemberwiseClone(); 11 | } 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Patterns/AbstractFactory/SolidStateDrive.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | 4 | public class SolidStateDrive : IStorage 5 | { 6 | public string HardwareType() 7 | { 8 | return "ssd"; 9 | } 10 | public int ReadSpeedInMBytesPerSec() 11 | { 12 | return 250; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Patterns/Bridge/PurchaseOrder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Bridge 4 | { 5 | public class PurchaseOrder : Order 6 | { 7 | public PurchaseOrder(IPayment payment) : base(payment) 8 | { 9 | } 10 | public override void Checkout(decimal amount) 11 | { 12 | Payment.SubmitPayment(amount); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Patterns/AbstractFactory/LenovoPartsFactory.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public class LenovoPartsFactory : ILaptopPartsFactory 4 | { 5 | public IStorage CreateStorage() 6 | { 7 | return new HardDrive(); 8 | } 9 | public IProcessor CreateProcessor() 10 | { 11 | return new AmdProcessor(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Patterns/Facade/ITransactionController.cs: -------------------------------------------------------------------------------- 1 | namespace Facade 2 | { 3 | 4 | public enum TransactionResponseType 5 | { 6 | OK, 7 | DECLINED 8 | } 9 | public interface ITransactionController 10 | { 11 | ITransactionRequest TransactionRequest {get;set;} 12 | void Execute(); 13 | 14 | TransactionResponseType GetApiResponse(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Patterns/AbstractFactory/DellPartsFactory.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public class DellPartsFactory : ILaptopPartsFactory 4 | { 5 | public IStorage CreateStorage() 6 | { 7 | return new SolidStateDrive(); 8 | } 9 | public IProcessor CreateProcessor() 10 | { 11 | return new IntelProcessor(); 12 | } 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /Patterns/Command/ClearCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Command 2 | { 3 | public class ClearCommand : ICommand 4 | { 5 | private IProductList _productList; 6 | public ClearCommand(IProductList productList) 7 | { 8 | _productList = productList; 9 | } 10 | public void Execute() 11 | { 12 | _productList.Products.Clear(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Patterns/Proxy/AuthenticationController.cs: -------------------------------------------------------------------------------- 1 | namespace Proxy 2 | { 3 | public class AuthenticationController : IAuthenticationController 4 | { 5 | private IUser _user; 6 | public AuthenticationController(IUser user) 7 | { 8 | _user = user; 9 | } 10 | public bool IsAdmin() 11 | { 12 | return _user.IsAdmin; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Patterns/Decorator/HtmlElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | 4 | namespace Decorator 5 | { 6 | public class HtmlElement : IHtmlElement 7 | { 8 | private readonly string _value; 9 | HtmlElement(string value) 10 | { 11 | _value = value; 12 | } 13 | 14 | public string GetHtmlElement() 15 | { 16 | return _value; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Patterns/ChainOfResponsibility/ICreditCard.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ChainOfResponsibility 4 | { 5 | // Credit card numbers are created in a consistent way. 6 | // American Express cards start with either 34 or 37. 7 | // Mastercard numbers begin with 51–55. 8 | // Visa cards start with 4 9 | public interface ICreditCard 10 | { 11 | string Number { get; set; } 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Patterns/Facade/ICreditCard.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Facade 4 | { 5 | public enum CreditCard 6 | { 7 | VISA, 8 | MASTERCARD, 9 | AMEX 10 | } 11 | public interface ICreditCard 12 | { 13 | CreditCard Type {get;set;} 14 | string AccountNumber {get;set;} 15 | 16 | string CVC {get;set;} 17 | 18 | DateTime ExpiryDate {get;set;} 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Patterns/Decorator/BoldenHtmlElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Decorator 4 | { 5 | public class BoldenHtmlElement : HtmlElementDecorator 6 | { 7 | public BoldenHtmlElement(IHtmlElement htmlElement) : base(htmlElement) 8 | { 9 | } 10 | 11 | public override string GetHtmlElement() 12 | { 13 | return $"{base.GetWrappedHtmlElement()}"; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Patterns/Iterator/GenericIterator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | 3 | namespace Iterator 4 | { 5 | public abstract class GenericIterator : IEnumerator 6 | { 7 | 8 | object IEnumerator.Current => Current(); 9 | 10 | public abstract object Current(); 11 | 12 | public abstract bool MoveNext(); 13 | 14 | public abstract void Reset(); 15 | 16 | public abstract bool HasNext(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Patterns/Prototype/Address.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Prototype 4 | { 5 | public class Address 6 | { 7 | public string StreetAddress { get; set; } = string.Empty; 8 | public string City { get; set; } = string.Empty; 9 | public string State { get; set; } = string.Empty; 10 | public string Country { get; set; } = string.Empty; 11 | public string PostCode { get; set; } = string.Empty; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Patterns/Decorator/ItalicizeHtmlElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Decorator 4 | { 5 | public class ItalicizeHtmlElement : HtmlElementDecorator 6 | { 7 | public ItalicizeHtmlElement(IHtmlElement htmlElement) : base(htmlElement) 8 | { 9 | 10 | } 11 | 12 | public override string GetHtmlElement() 13 | { 14 | return $"{base.GetWrappedHtmlElement()}"; 15 | } 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.*~ 3 | project.lock.json 4 | .DS_Store 5 | *.pyc 6 | 7 | # Visual Studio Code 8 | .vscode 9 | 10 | # User-specific files 11 | *.suo 12 | *.user 13 | *.userosscache 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | build/ 24 | bld/ 25 | [Bb]in/ 26 | [Oo]bj/ 27 | msbuild.log 28 | msbuild.err 29 | msbuild.wrn 30 | 31 | # Visual Studio 2015 32 | .vs/ -------------------------------------------------------------------------------- /Patterns/Bridge/PaypalPayment.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | namespace Bridge 4 | { 5 | public class PaypalPayment : IPayment 6 | { 7 | private IPaymentGateway _mPaymentGateway; 8 | 9 | public PaypalPayment(IPaymentGateway paymentGateway) 10 | { 11 | _mPaymentGateway = paymentGateway; 12 | } 13 | public void SubmitPayment(decimal amount) 14 | { 15 | _mPaymentGateway.ProcessPayment(amount, this); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Patterns/Bridge/CreditCardPayment.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | namespace Bridge 4 | { 5 | public class CreditCardPayment : IPayment 6 | { 7 | private IPaymentGateway _mPaymentGateway; 8 | 9 | public CreditCardPayment(IPaymentGateway paymentGateway) 10 | { 11 | _mPaymentGateway = paymentGateway; 12 | } 13 | public void SubmitPayment(decimal amount) 14 | { 15 | _mPaymentGateway.ProcessPayment(amount, this); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Patterns/Composite/Book.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Composite 4 | { 5 | public class Book : IBook 6 | { 7 | public decimal Price { get; private set; } 8 | public string Name { get; private set; } 9 | 10 | public int Discount {get; private set;} 11 | 12 | public Book(string name, decimal price, int discount = 0) 13 | { 14 | Price = price; 15 | Name = name; 16 | Discount = discount; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Patterns/Prototype/BasicCustomer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Prototype 4 | { 5 | public abstract class BasicCustomer : ICloneable 6 | { 7 | public string FirstName { get; set; } = string.Empty; 8 | public string LastName { get; set; } = string.Empty; 9 | public Address? HomeAddress { get; set; } 10 | public Address? BillingAddress { get; set; } 11 | 12 | public abstract object Clone(); 13 | public abstract Customer DeepClone(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Patterns/Command/RemoveCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Command 2 | { 3 | public class RemoveCommand : ICommand 4 | { 5 | private IProductList _productList; 6 | private IProduct _product; 7 | public RemoveCommand(IProductList productList, IProduct product) 8 | { 9 | _productList = productList; 10 | _product = product; 11 | } 12 | public void Execute() 13 | { 14 | _productList.Products.Remove(_product); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Patterns/Decorator/HtmlElementDecorator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Decorator 4 | { 5 | public abstract class HtmlElementDecorator : IHtmlElement 6 | { 7 | IHtmlElement _htmlElement; 8 | public HtmlElementDecorator(IHtmlElement htmlElement) => _htmlElement = htmlElement; 9 | 10 | public abstract string GetHtmlElement(); 11 | 12 | protected string GetWrappedHtmlElement() 13 | { 14 | return _htmlElement.GetHtmlElement(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Patterns/Command/AddCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Command 2 | { 3 | public class AddCommand : ICommand 4 | { 5 | private IProduct _product; 6 | private IProductList _productList; 7 | 8 | public AddCommand(IProductList productList, IProduct product) 9 | { 10 | _productList = productList; 11 | _product = product; 12 | } 13 | 14 | public void Execute() 15 | { 16 | _productList.Products.Add(_product); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Patterns/State/InvalidCouponState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace State 4 | { 5 | public class InvalidCouponState : ICouponState 6 | { 7 | public void ChangeExpiryDate(DateTime dateTime, ICoupon coupon) 8 | { 9 | if (dateTime >= DateTime.Today) 10 | { 11 | coupon.SetCouponState(new ValidCouponState()); 12 | } 13 | } 14 | 15 | public bool UseDiscount() 16 | { 17 | return false; 18 | } 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /Patterns/TemplateMethod/BookPriceCalculator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TemplateMethod 4 | { 5 | public abstract class BookPriceCalculator 6 | { 7 | protected abstract decimal ComputeTotalPriceBeforeTax(IBook books); 8 | protected abstract decimal ApplyTax(decimal priceBeforeTax); 9 | 10 | public decimal CalculatePrice(IBook books) 11 | { 12 | var priceBeforeTax = ComputeTotalPriceBeforeTax(books); 13 | return ApplyTax(priceBeforeTax); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Patterns/State/ValidCouponState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace State 4 | { 5 | public class ValidCouponState : ICouponState 6 | { 7 | public void ChangeExpiryDate(DateTime dateTime, ICoupon coupon) 8 | { 9 | if (dateTime < DateTime.Today) 10 | { 11 | coupon.SetCouponState(new InvalidCouponState()); 12 | } 13 | 14 | } 15 | 16 | public bool UseDiscount() 17 | { 18 | return true; 19 | } 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /Patterns/Flyweight/XmlTextBlob.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Flyweight 4 | { 5 | public class XmlTextBlob : TextBlob 6 | { 7 | private TextDownloader textDownloader; 8 | 9 | public XmlTextBlob(TextDownloader textDownloader) 10 | { 11 | this.textDownloader = textDownloader; 12 | } 13 | public void Download(int id, string blobContent) 14 | { 15 | textDownloader.DownloadFile($"https://localhost:5001/api/getitem/{id}", blobContent); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Patterns/Decorator/HyperLinkifyHtmlElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Decorator 4 | { 5 | public class HyperLinkifyHtmlElement : HtmlElementDecorator 6 | { 7 | private string _link; 8 | public HyperLinkifyHtmlElement(string link, IHtmlElement htmlElement) : base(htmlElement) 9 | { 10 | _link = link; 11 | } 12 | 13 | public override string GetHtmlElement() 14 | { 15 | return $"{base.GetWrappedHtmlElement()}"; 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Patterns/Strategy/NonMemberDiscountScheme.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | 4 | namespace Strategy 5 | { 6 | public class NonMemberDiscountScheme : IDiscountScheme 7 | { 8 | public decimal ComputePrice(IProduct product, ICoupon coupon) 9 | { 10 | if (coupon.IsExpired()) 11 | { 12 | return product.SellingPrice(); 13 | } 14 | var discount = product.IsOnSale() ? 0M : (product.SellingPrice() * (coupon.DiscountPercentage()/ 100M)); 15 | return product.SellingPrice() - discount; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Patterns/TemplateMethod/LowIncomeBookPriceCalculator.cs: -------------------------------------------------------------------------------- 1 | namespace TemplateMethod 2 | { 3 | public class LowIncomeBookPriceCalculator : BookPriceCalculator 4 | { 5 | private readonly int _discount; 6 | public LowIncomeBookPriceCalculator(int discount) 7 | { 8 | _discount = discount; 9 | } 10 | protected override decimal ComputeTotalPriceBeforeTax(IBook books) 11 | { 12 | return books.Price - (books.Price * (_discount / 100.0M)); 13 | } 14 | protected override decimal ApplyTax(decimal priceBeforeTax) 15 | { 16 | return priceBeforeTax; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Patterns/Builder/SkuCodeStartDiscountStrategy.cs: -------------------------------------------------------------------------------- 1 | namespace Builder 2 | { 3 | public class SkuCodeStartDiscountStrategy : IDiscountStrategy 4 | { 5 | public int DiscountInPercentage { get; set; } 6 | public string SkuCode { get; set; } = string.Empty; 7 | public decimal CalculateDiscountedRetailPrice(Product product) 8 | { 9 | if (!product.StockKeepingUnit.StartsWith(SkuCode)) 10 | { 11 | return product.RegularRetailPrice; 12 | } 13 | 14 | return product.RegularRetailPrice - (DiscountInPercentage / 100m * product.RegularRetailPrice); 15 | } 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Patterns/SingletonDependencyInjection/LazyLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SingletonDI 4 | { 5 | public sealed class LazyLogger 6 | { 7 | // Reference: CSharpInDepth using .Net4 Lazy 8 | // Implicitly uses LazyThreadSafetyMode.ExecutionAndPublication as the 9 | // thread safety mode for the Lazy 10 | private static readonly Lazy _lazyLogger = new Lazy( 11 | () => new Logger() 12 | ); 13 | public static Logger Instance 14 | { 15 | get 16 | { 17 | return _lazyLogger.Value; 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Patterns/Strategy/business_logic.txt: -------------------------------------------------------------------------------- 1 | 2 | User Story: 3 | As a store owner, I want to be able to compute for the correct 4 | discount per customer so that the dogmandu members get extra discounts. 5 | 6 | 7 | Business Logic: 8 | At the DogMandu Anniversary Sale shop, a discount coupon is provided 9 | to each customer (discount can be arbitrary). The discount has an added 10 | 5% if the customer is a dogmandu member. 11 | However, if a coupon is expired, despite 12 | being a member, no discount is given. 13 | Also, items already on sale are excluded 14 | from discounts for non members. Members however still get the 15 | discount on these items plus the base member discount of 5%. 16 | 17 | -------------------------------------------------------------------------------- /Patterns/Adapter/SocialMediaProfileAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Mail; 3 | 4 | namespace Adapter 5 | { 6 | public class SocialMediaProfileAdapter : IGoodReadsProfile 7 | { 8 | private readonly ISocialMediaProfile _socialMediaProfile; 9 | public SocialMediaProfileAdapter(ISocialMediaProfile? socialMediaProfile) 10 | { 11 | ArgumentNullException.ThrowIfNull(socialMediaProfile); 12 | _socialMediaProfile = socialMediaProfile; 13 | } 14 | 15 | public string Name { get { return _socialMediaProfile.Name; } } 16 | 17 | public MailAddress Email { get { return _socialMediaProfile.Email; } } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Patterns/Memento/ProductOriginator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Memento 4 | { 5 | public class ProductOriginator : IProductOriginator 6 | { 7 | private Product? _product; 8 | 9 | public ProductOriginator(Product product) 10 | { 11 | ArgumentNullException.ThrowIfNull(product); 12 | SetMemento(product); 13 | } 14 | 15 | public void SetMemento(Product product) 16 | { 17 | _product = product; 18 | } 19 | 20 | public Product GetMemento() 21 | { 22 | ArgumentNullException.ThrowIfNull(_product); 23 | return _product; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Patterns/FactoryMethod/EarningBonusCalculatorRefactored.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FactoryMethod 4 | { 5 | public class EarningBonusCalculator 6 | { 7 | private readonly PerksFactory 8 | perksFactory; 9 | 10 | public EarningBonusCalculator(PerksFactory perksFactory) 11 | { 12 | this.perksFactory = perksFactory; 13 | } 14 | public int UpdatedMiles(int currentTotalMiles, int newMilesEarned) 15 | { 16 | var perks = perksFactory.GetPerks(currentTotalMiles); 17 | return currentTotalMiles + newMilesEarned + Convert.ToInt32(Math.Floor(newMilesEarned * perks?.EarningBonus() ?? 0)); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Patterns/Memento/ProductCaretaker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Memento 6 | { 7 | public class ProductCaretaker : IProductCaretaker 8 | { 9 | private IList productMementos = new List(); 10 | public void AddProductMemento(Product product) 11 | { 12 | productMementos.Add(product.ShallowCopy()); 13 | } 14 | 15 | public Product GetLastMemento() 16 | { 17 | var product = productMementos.DefaultIfEmpty(null).Last(); 18 | 19 | ArgumentNullException.ThrowIfNull(product); 20 | 21 | return product; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Patterns/Proxy/Entries.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Proxy 4 | { 5 | public class Entries : IEntries 6 | { 7 | private Dictionary _products; 8 | public Entries(Dictionary products) 9 | { 10 | _products = products; 11 | } 12 | 13 | public bool Delete(int id) 14 | { 15 | return _products.Remove(id); 16 | } 17 | public IProductInfo? Get(int id) 18 | { 19 | if (!_products.ContainsKey(id)) 20 | { 21 | return null; 22 | } 23 | 24 | return _products[id]; 25 | } 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Patterns/Composite/BookComposite.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Composite 6 | { 7 | public class BookComposite : IBook 8 | { 9 | private List _books; 10 | public decimal Price => _books.Select(book => book.Price).Sum(); 11 | public string Name { get; private set; } 12 | public int Discount {get; private set;} 13 | public BookComposite(int discount, string name) 14 | { 15 | Discount = discount; 16 | Name = name; 17 | _books = new List(); 18 | } 19 | 20 | public void Add(IBook book) 21 | { 22 | _books.Add(book); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Patterns/ChainOfResponsibility/CreditCardHandlerBase.cs: -------------------------------------------------------------------------------- 1 | namespace ChainOfResponsibility 2 | { 3 | public abstract class CreditCardHandlerBase : ICreditCardHandler 4 | { 5 | private ICreditCardHandler? nextCreditCardHandler; 6 | public ICreditCardHandler SetNext(ICreditCardHandler creditCardHandler) 7 | { 8 | nextCreditCardHandler = creditCardHandler; 9 | return creditCardHandler; 10 | } 11 | 12 | public virtual bool IsCreditCardValid(ICreditCard card) 13 | { 14 | if (nextCreditCardHandler != null) 15 | { 16 | return nextCreditCardHandler.IsCreditCardValid(card); 17 | } 18 | 19 | return false; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Patterns/Observer/SpecialsSubject.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Observer 4 | { 5 | public class SpecialsSubject : ISubject 6 | { 7 | public delegate void Callback(string s); 8 | public required string SubjectState { get; set; } 9 | 10 | private readonly List _observers = []; 11 | 12 | public void Attach(IObserver observer) 13 | { 14 | _observers.Add(observer); 15 | } 16 | 17 | public void Detach(IObserver observer) 18 | { 19 | _observers.Remove(observer); 20 | } 21 | 22 | public void Notify() 23 | { 24 | _observers.ForEach(observer => observer.Update(this)); 25 | } 26 | 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Patterns/ChainOfResponsibility/AmexCardHandler.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace ChainOfResponsibility 3 | { 4 | public class AmexCardHandler : CreditCardHandlerBase 5 | { 6 | private IPaymentGateway paymentGateway; 7 | private const string AmexCardStartingNumber = "3"; 8 | public AmexCardHandler(IPaymentGateway paymentGateway) 9 | { 10 | this.paymentGateway = paymentGateway; 11 | } 12 | 13 | public override bool IsCreditCardValid(ICreditCard card) 14 | { 15 | if (card.Number.StartsWith(AmexCardStartingNumber)) 16 | { 17 | return paymentGateway.SubmitVerification(this, card); 18 | } 19 | 20 | return base.IsCreditCardValid(card); 21 | } 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /Patterns/ChainOfResponsibility/VisaCardHandler.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace ChainOfResponsibility 3 | { 4 | public class VisaCardHandler : CreditCardHandlerBase 5 | { 6 | private IPaymentGateway paymentGateway; 7 | private const string VisaCardStartingNumber = "4"; 8 | public VisaCardHandler(IPaymentGateway paymentGateway) 9 | { 10 | this.paymentGateway = paymentGateway; 11 | } 12 | 13 | public override bool IsCreditCardValid(ICreditCard card) 14 | { 15 | if (card.Number.StartsWith(VisaCardStartingNumber)) 16 | { 17 | return paymentGateway.SubmitVerification(this, card); 18 | } 19 | 20 | return base.IsCreditCardValid(card); 21 | } 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /Patterns/ChainOfResponsibility/MastercardHandler.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace ChainOfResponsibility 3 | { 4 | public class MastercardHandler : CreditCardHandlerBase 5 | { 6 | private IPaymentGateway paymentGateway; 7 | private const string MasterCardStartingNumber = "5"; 8 | public MastercardHandler(IPaymentGateway paymentGateway) 9 | { 10 | this.paymentGateway = paymentGateway; 11 | } 12 | 13 | public override bool IsCreditCardValid(ICreditCard card) 14 | { 15 | if (card.Number.StartsWith(MasterCardStartingNumber)) 16 | { 17 | return paymentGateway.SubmitVerification(this, card); 18 | } 19 | 20 | return base.IsCreditCardValid(card); 21 | } 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /Patterns/TemplateMethod/HighIncomeBookPriceCalculator.cs: -------------------------------------------------------------------------------- 1 | namespace TemplateMethod 2 | { 3 | public class HighIncomeBookPriceCalculator : BookPriceCalculator 4 | { 5 | private static readonly int MTaxPercentage = 12; 6 | private readonly int _discount; 7 | public HighIncomeBookPriceCalculator(int discountPercentage) 8 | { 9 | _discount = discountPercentage; 10 | } 11 | protected override decimal ComputeTotalPriceBeforeTax(IBook books) 12 | { 13 | return books.Price - (books.Price * (_discount / 100.0M)); 14 | } 15 | protected override decimal ApplyTax(decimal priceBeforeTax) 16 | { 17 | return priceBeforeTax + (priceBeforeTax * (MTaxPercentage / 100.0M)); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Patterns/Command/ProductCommandInvoker.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Command 4 | { 5 | public class ProductCommandInvoker : IInvoker 6 | { 7 | private Dictionary _commands; 8 | 9 | public ProductCommandInvoker() 10 | { 11 | _commands = new Dictionary(); 12 | } 13 | 14 | public bool AddCommand(string key, ICommand command) 15 | { 16 | if (_commands.ContainsKey(key)) 17 | { 18 | return false; 19 | } 20 | _commands[key] = command; 21 | return true; 22 | } 23 | 24 | public void InvokeCommand(string key) 25 | { 26 | _commands[key].Execute(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Patterns/FactoryMethod/EarningBonusCalculatorLegacy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FactoryMethod 4 | { 5 | public class EarningBonusCalculatorLegacy 6 | { 7 | public int UpdatedMiles(int currentTotalMiles, int newMilesEarned) 8 | { 9 | if (currentTotalMiles >= 20000) 10 | { 11 | return currentTotalMiles + newMilesEarned + Convert.ToInt32(Math.Floor(newMilesEarned * 1.0)); 12 | } 13 | else if (currentTotalMiles >= 10000) 14 | { 15 | return currentTotalMiles + newMilesEarned + Convert.ToInt32(Math.Floor(newMilesEarned * 0.5)); 16 | } 17 | else 18 | { 19 | return currentTotalMiles + newMilesEarned; 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Patterns/Proxy/EntriesProxy.cs: -------------------------------------------------------------------------------- 1 | namespace Proxy 2 | { 3 | public class EntriesProxy : IEntries 4 | { 5 | private IAuthenticationController _authCtrl; 6 | private IEntries _realEntries; 7 | public EntriesProxy(IAuthenticationController authCtrl, IEntries realEntries) 8 | { 9 | _authCtrl = authCtrl; 10 | _realEntries = realEntries; 11 | } 12 | 13 | public bool Delete(int id) 14 | { 15 | var result = false; 16 | if (_authCtrl.IsAdmin()) 17 | { 18 | result = _realEntries.Delete(id); 19 | } 20 | return result; 21 | } 22 | public IProductInfo? Get(int id) 23 | { 24 | return _realEntries?.Get(id) ?? null; 25 | } 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Patterns/Strategy/MemberDiscountScheme.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | 4 | namespace Strategy 5 | { 6 | public class MemberDiscountScheme : IDiscountScheme 7 | { 8 | struct Discount 9 | { 10 | public const int BaseMemberDiscount = 5; 11 | } 12 | 13 | public decimal ComputePrice(IProduct product, ICoupon coupon) 14 | { 15 | var memberBaseDiscount = product.SellingPrice() * (Discount.BaseMemberDiscount / 100M); 16 | var memberDiscountedPrice = product.SellingPrice() - memberBaseDiscount; 17 | if (coupon.IsExpired()) 18 | { 19 | return memberDiscountedPrice; 20 | } 21 | 22 | return memberDiscountedPrice - (product.SellingPrice() * (coupon.DiscountPercentage()/ 100M)); 23 | } 24 | 25 | } 26 | } -------------------------------------------------------------------------------- /Patterns/Flyweight/XmlTextBlobFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Flyweight 5 | { 6 | public class XmlTextBlobFactory : TextBlobFactory 7 | { 8 | private Dictionary textBlobDictionary = new Dictionary(); 9 | private TextDownloader textDownloader; 10 | public XmlTextBlobFactory(TextDownloader textDownloader) 11 | { 12 | this.textDownloader = textDownloader; 13 | } 14 | 15 | public TextBlob GetTextBlob(int id) 16 | { 17 | if (textBlobDictionary.ContainsKey(id)) 18 | { 19 | return textBlobDictionary[id]; 20 | } 21 | 22 | var textBlob = new XmlTextBlob(textDownloader); 23 | textBlobDictionary[id] = textBlob; 24 | return textBlob; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Patterns/Builder/DiscountStrategyBuilder.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Builder 3 | { 4 | public abstract class DiscountStrategyBuilder 5 | { 6 | private IDiscountStrategy _discountStrategy; 7 | 8 | public DiscountStrategyBuilder(IDiscountStrategy discountStrategy) 9 | { 10 | _discountStrategy = discountStrategy; 11 | } 12 | 13 | public DiscountStrategyBuilder ApplicableToSKUCode(string skuCode) 14 | { 15 | _discountStrategy.SkuCode = skuCode; 16 | return this; 17 | } 18 | 19 | public DiscountStrategyBuilder WithDiscountInPercentage(int discountInPercentage) 20 | { 21 | _discountStrategy.DiscountInPercentage = discountInPercentage; 22 | return this; 23 | } 24 | public IDiscountStrategy Build() 25 | { 26 | return _discountStrategy; 27 | } 28 | 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Patterns/Iterator/NumberMatrix.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | 3 | namespace Iterator 4 | { 5 | public class NumberMatrix : AggregateObject, IMatrix 6 | { 7 | private int[,] _data; 8 | public MatrixDirection IteratorDirection { get; set; } 9 | 10 | public int TotalRows() => _data.GetLength(0); 11 | public int TotalColumns() => _data.GetLength(1); 12 | 13 | public NumberMatrix(MatrixDirection iteratorDirection, int width, int height) 14 | { 15 | _data = new int[width, height]; 16 | IteratorDirection = iteratorDirection; 17 | } 18 | 19 | public int this[int row, int column] 20 | { 21 | get { return _data[row, column]; } 22 | set { _data[row, column] = value; } 23 | } 24 | 25 | public override IEnumerator GetEnumerator() 26 | { 27 | return new MatrixIterator(this, IteratorDirection); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Patterns/Mediator/PurchaseMediator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | 5 | namespace Mediator 6 | { 7 | public class PurchaseMediator : IMediator 8 | { 9 | private List _activePurchasers; 10 | 11 | public PurchaseMediator() 12 | { 13 | _activePurchasers = new List(); 14 | } 15 | public bool BroadcastPurchaseCompletion(IPurchaser purchaser) 16 | { 17 | var isPurchaserActive = _activePurchasers.Remove(purchaser); 18 | 19 | if (isPurchaserActive) 20 | { 21 | _activePurchasers.ForEach(p => p.Receive(purchaser)); 22 | } 23 | 24 | return isPurchaserActive; 25 | } 26 | public bool AddPurchaser(IPurchaser purchaser) 27 | { 28 | if (_activePurchasers.Contains(purchaser) == false) 29 | { 30 | _activePurchasers.Add(purchaser); 31 | return true; 32 | } 33 | 34 | return false; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Dean Agan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Tests/TemplateMethodTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using Moq; 3 | using FluentAssertions; 4 | 5 | namespace TemplateMethod.Test 6 | { 7 | public class TemplateMethodShould 8 | { 9 | [Fact] 10 | public void ApplyTaxes_WhenBuyerIsHighIncomeEarner() 11 | { 12 | // Arrange 13 | var highIncomeBuyer = new HighIncomeBookPriceCalculator(5); 14 | var book = Mock.Of(book => book.Price == 100.0M); 15 | 16 | // Act 17 | var priceAfterTax = highIncomeBuyer.CalculatePrice(book); 18 | 19 | // Assert 20 | priceAfterTax.Should().Be(106.4M); 21 | } 22 | 23 | [Fact] 24 | public void NotApplyTaxes_WhenBuyerIsLowIncomeEarner() 25 | { 26 | // Arrange 27 | var lowIncomeBuyer = new LowIncomeBookPriceCalculator(5); 28 | var book = Mock.Of(book => book.Price == 100.0M); 29 | 30 | // Act 31 | var priceAfterTax = lowIncomeBuyer.CalculatePrice(book); 32 | 33 | // Assert 34 | priceAfterTax.Should().Be(95.0M); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a .NET project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net 3 | 4 | name: .NET 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Setup .NET 20 | uses: actions/setup-dotnet@v4 21 | with: 22 | dotnet-version: 8.0.x 23 | - name: Restore dependencies 24 | run: dotnet restore 25 | - name: Build 26 | run: dotnet build --no-restore 27 | - name: Test 28 | run: dotnet test --no-build --verbosity normal 29 | - name: Generate coverage report 30 | run: | 31 | cd ./Tests 32 | dotnet test /p:CollectCoverage=true /p:CoverletOutput=TestResults/ /p:CoverletOutputFormat=lcov 33 | - name: Publish coverage report to coveralls.io 34 | uses: coverallsapp/github-action@master 35 | with: 36 | github-token: ${{ secrets.GITHUB_TOKEN }} 37 | path-to-lcov: ./Tests/TestResults/coverage.info 38 | -------------------------------------------------------------------------------- /Patterns/FactoryMethod/PerksFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace FactoryMethod 6 | { 7 | public class PerksFactory 8 | { 9 | private readonly List<(int, string)> thresholds; 10 | private const string DEFAULT_PERKS_TYPE = "BasicPerks"; 11 | 12 | public PerksFactory(List<(int, string)> thresholds) 13 | { 14 | this.thresholds = thresholds; 15 | } 16 | public IPerks? GetPerks(int totalMiles) 17 | { 18 | var perks = thresholds.Where(th => totalMiles >= th.Item1) 19 | .Select(p => p.Item2) 20 | .DefaultIfEmpty("BasicPerks") 21 | .First(); 22 | 23 | var fullyQualifiedPerksName = $"FactoryMethod.{perks}"; 24 | 25 | var perksType = Type.GetType(fullyQualifiedPerksName); 26 | 27 | if (perksType == null) 28 | { 29 | return null; 30 | } 31 | 32 | return (IPerks?)Activator.CreateInstance(perksType); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Patterns/Facade/business_logic.txt: -------------------------------------------------------------------------------- 1 | User Story: As a customer, I want to be able to pay for my purchases in a secure way. 2 | 3 | We are integrating a payment gateway into our site. The task for this is to implement 4 | a way to pay securely using an SDK. 5 | 6 | In using the payment gateway SDK, these are the following steps: 7 | 8 | 1. IEnvironment - Set the environment. Assume step to either be SANDBOX or PRODUCTION. 9 | 2. IMerchantAuthenticationType - Assume interface requires login id and transaction key. 10 | 3. IBillingAddress - A billing address type. Require first name, last name, address, city and zip code. 11 | 3. ICreditCard - Can be a visa, mastercard or amex credit card. Must implement typename, account number, cvc and expiry date. 12 | 4. ITransactionRequest - Requires amount in decimal, IBillingAddress and ICreditCardType supplied. 13 | 5. ITransactionController - Requires an ITransactionRequest, an Execute function and a GetApiResponse function. 14 | The GetApiResponse function returns either OK or Declined. 15 | - Decline if 16 | + Environment not Set 17 | + Merchant authentication not set. 18 | + ICreditCardType has expired date. 19 | - Otherwise, returns OK. -------------------------------------------------------------------------------- /Patterns/Facade/PaymentProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Facade 4 | { 5 | public class PaymentProcessor : IPaymentProcessor 6 | { 7 | private readonly IEnvironment _environment; 8 | private readonly IMerchantAuthenticationType _merchAuthType; 9 | private readonly ITransactionController _txnCtrl; 10 | public PaymentProcessor( 11 | IEnvironment environment, 12 | IMerchantAuthenticationType merchAuthType, 13 | ITransactionController txnCtrl) 14 | { 15 | // TODO: Add data invariance. Use contracts? 16 | _environment = environment; 17 | _merchAuthType = merchAuthType; 18 | _txnCtrl = txnCtrl; 19 | } 20 | public void InitializePaymentGatewayInterface() 21 | { 22 | _environment.environmentVariableTarget = EnvironmentTarget.SANDBOX; 23 | _merchAuthType.TransactionKey = "transaction_key"; 24 | _merchAuthType.LoginID = "login_id"; 25 | } 26 | public bool SubmitPayment() 27 | { 28 | _txnCtrl.Execute(); 29 | var t = _txnCtrl.GetApiResponse() == TransactionResponseType.OK; 30 | return t; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Patterns/Mediator/Purchaser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mediator 4 | { 5 | public class Purchaser : IPurchaser 6 | { 7 | public string ItemBought { get; private set; } = string.Empty; 8 | public string Location { get; private set; } = string.Empty; 9 | private Product? _product; 10 | private IAlertScreen _alertScreen; 11 | private IMediator _mediator; 12 | 13 | public Purchaser(IAlertScreen? alertScreen, IMediator? mediator) 14 | { 15 | ArgumentNullException.ThrowIfNull(alertScreen); 16 | ArgumentNullException.ThrowIfNull(mediator); 17 | 18 | _alertScreen = alertScreen; 19 | _mediator = mediator; 20 | } 21 | 22 | public void Receive(IPurchaser purchaser) 23 | { 24 | var product = purchaser.GetProduct(); 25 | if (product != null) 26 | { 27 | _alertScreen.ShowMessage(product.Item, product.Location); 28 | } 29 | } 30 | 31 | public void Complete(Product product) 32 | { 33 | _product = product; 34 | _mediator.BroadcastPurchaseCompletion(this); 35 | } 36 | 37 | public Product? GetProduct() 38 | { 39 | return _product; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Tests/FlyweightTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using Moq; 4 | using FluentAssertions; 5 | 6 | namespace Flyweight.Test 7 | { 8 | public class FlyweightShould 9 | { 10 | private TextDownloader _textDownloader; 11 | public FlyweightShould() 12 | { 13 | this._textDownloader = Mock.Of(); 14 | } 15 | [Fact] 16 | public void NotRecreateObject_WhenObjectIsSame() 17 | { 18 | // Arrange 19 | var xmlTextBlob = new XmlTextBlobFactory(this._textDownloader); 20 | 21 | // Act 22 | var blob1 = xmlTextBlob.GetTextBlob(1); 23 | var blob2 = xmlTextBlob.GetTextBlob(1); 24 | 25 | // Assert 26 | blob1.GetHashCode().Should().Be(blob2.GetHashCode()); 27 | } 28 | 29 | [Fact] 30 | public void InvokeMatchingId_WhenXmlTextBlobDownloadInvoked() 31 | { 32 | // Arrange 33 | var xmlTextBlob = new XmlTextBlobFactory(this._textDownloader); 34 | var blob = xmlTextBlob.GetTextBlob(1); 35 | // Act 36 | blob.Download(1, "Hello"); 37 | // Assert 38 | Mock.Get(_textDownloader).Verify(td => td.DownloadFile($"https://localhost:5001/api/getitem/1", It.IsAny())); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Patterns/State/Coupon.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace State 5 | { 6 | public class Coupon : ICoupon 7 | { 8 | private int _discount; 9 | private ICouponState _currentState; 10 | private DateTime _expiry; 11 | 12 | public Coupon(int discount, DateTime expiry) 13 | { 14 | if (expiry < DateTime.Today) 15 | { 16 | throw new ArgumentException("Coupon must be constructed with valid expiry date"); 17 | } 18 | _expiry = expiry; 19 | _discount = discount; 20 | // Because coupon states are internal, we will 21 | // couple them with the specific states so they 22 | // are highly cohesive. For this implementation, 23 | // we will construct on state change. 24 | _currentState = new ValidCouponState(); 25 | } 26 | 27 | public void SetCouponState(ICouponState state) 28 | { 29 | _currentState = state; 30 | } 31 | 32 | public void UpdateExpiryDate(DateTime dateTime) 33 | { 34 | _currentState.ChangeExpiryDate(dateTime, this); 35 | } 36 | 37 | public decimal ApplyDiscountTo(IProduct product) 38 | { 39 | var price = product.Price; 40 | return _currentState.UseDiscount() ? price - (price * (_discount/100.0M)) : price; 41 | } 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /Tests/StateTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using Moq; 4 | using FluentAssertions; 5 | 6 | namespace State.Test 7 | { 8 | public class StateShould 9 | { 10 | private readonly IProduct _product = Mock.Of ( p => p.Price == 100.0M); 11 | [Fact] 12 | public void ReturnDiscountedProductPrice_WhenCoupon_IsValid() 13 | { 14 | // Arrange 15 | var expiry = DateTime.Today.AddDays(2); 16 | var coupon = new Coupon(5, expiry); 17 | // Act 18 | var price = coupon.ApplyDiscountTo(_product); 19 | // Assert 20 | price.Should().Be(95.0M); 21 | } 22 | 23 | [Fact] 24 | public void ReturnSameProductPrice_WhenCouponIsExpired() 25 | { 26 | // Arrange 27 | var expiry = DateTime.Today.AddDays(2); 28 | var coupon = new Coupon(5, expiry); 29 | coupon.UpdateExpiryDate(expiry.AddDays(-4)); 30 | // Act 31 | var price = coupon.ApplyDiscountTo(_product); 32 | // Assert 33 | price.Should().Be(100.0M); 34 | } 35 | 36 | [Fact] 37 | public void ThrowArgumentException_WhenCouponConstructedAsExpired() 38 | { 39 | // Arrange 40 | var expiry = DateTime.Today.AddDays(-2); 41 | // Act 42 | Action act = () => new Coupon(5, expiry); 43 | // Assert 44 | act.Should().ThrowExactly(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Tests/AdapterTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using Moq; 4 | using FluentAssertions; 5 | using AutoFixture; 6 | using System.Net.Mail; 7 | 8 | namespace Adapter.Tests 9 | { 10 | public class AdapterShould 11 | { 12 | private readonly Fixture _fixture = new(); 13 | 14 | [Fact] 15 | public void Return_SocialMediaProfileContent_WhenAccessingViaAdapter() 16 | { 17 | // Arrange 18 | var mockSocialMediaProfile = Mock.Of(msmp => msmp.Name == _fixture.Create() && 19 | msmp.UserName == _fixture.Create() && 20 | msmp.Email == _fixture.Create()); 21 | 22 | // Act 23 | var goodReadsProfile = new SocialMediaProfileAdapter(mockSocialMediaProfile); 24 | 25 | // Assert 26 | using (new FluentAssertions.Execution.AssertionScope("profile")) 27 | { 28 | goodReadsProfile.Name.Should().Be(mockSocialMediaProfile.Name); 29 | goodReadsProfile.Email.Should().Be(mockSocialMediaProfile.Email); 30 | } 31 | 32 | } 33 | 34 | [Fact] 35 | public void ThrowException_WhenSocialMediaAdapteeIsNull() 36 | { 37 | // Act 38 | Action act = () => new SocialMediaProfileAdapter(null); 39 | 40 | // Assert 41 | act.Should().ThrowExactly(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/SingletonDITest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using Moq; 3 | using FluentAssertions; 4 | using Ninject; 5 | 6 | namespace SingletonDI.Test 7 | { 8 | public class SingletonDIShould 9 | { 10 | 11 | [Fact] 12 | public void ReturnSameInstance_WhenComparingLazyLoggerSingleton() 13 | { 14 | // Arrange 15 | var logger1 = LazyLogger.Instance; 16 | // Act 17 | var logger2 = LazyLogger.Instance; 18 | // Assert 19 | logger1.Should().BeSameAs(logger2); 20 | } 21 | 22 | [Fact] 23 | public void ReturnDifferentInstance_WhenComparingInstanceCreatedByNinjectInTransientScope() 24 | { 25 | // Arrange 26 | var container = new StandardKernel(); 27 | container.Bind().To(); 28 | // Act 29 | var logger1 = container.Get(); 30 | var logger2 = container.Get(); 31 | // Assert 32 | logger1.Should().NotBeSameAs(logger2); 33 | } 34 | 35 | [Fact] 36 | public void ReturnSameInstance_WhenComparingInstanceCreatedByNinjectInSingletonScope() 37 | { 38 | // Arrange 39 | var container = new StandardKernel(); 40 | container.Bind().To().InSingletonScope(); 41 | // Act 42 | var logger1 = container.Get(); 43 | var logger2 = container.Get(); 44 | // Assert 45 | logger1.Should().BeSameAs(logger2); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Tests/BridgeTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Xunit; 3 | using Moq; 4 | using FluentAssertions; 5 | 6 | namespace Bridge.Test 7 | { 8 | public class BridgeShould 9 | { 10 | private readonly IPaymentGateway _mPaymentGateway; 11 | 12 | public BridgeShould() 13 | { 14 | _mPaymentGateway = Mock.Of(); 15 | } 16 | 17 | public delegate IPayment PaymentMethodCreator(IPaymentGateway gateway); 18 | 19 | public static IEnumerable PaymentMethodCreators 20 | { 21 | get 22 | { 23 | yield return new object[] { new PaymentMethodCreator((gateway) => new CreditCardPayment(gateway)) }; 24 | yield return new object[] { new PaymentMethodCreator((gateway) => new PaypalPayment(gateway)) }; 25 | } 26 | } 27 | 28 | [Theory] 29 | [MemberData(nameof(PaymentMethodCreators))] 30 | public void UseCorrectPaymentMethod_WhenCheckingOut(PaymentMethodCreator paymentMethodCreator) 31 | { 32 | // Arrange 33 | var paymentMethod = paymentMethodCreator(_mPaymentGateway); 34 | var order = new PurchaseOrder(paymentMethod); 35 | 36 | // Act 37 | order.Checkout(20.0M); 38 | 39 | // Assert 40 | using (new FluentAssertions.Execution.AssertionScope("payments")) 41 | { 42 | Mock.Get(_mPaymentGateway).Verify( 43 | pp => pp.ProcessPayment(20.0M, 44 | It.Is(p => p.GetType() == paymentMethod.GetType())), Times.Once); 45 | 46 | paymentMethod.GetType().Should().Implement(); 47 | } 48 | } 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Tests/MementoTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using Moq; 3 | using FluentAssertions; 4 | 5 | namespace Memento.Test 6 | { 7 | public class MementoShould 8 | { 9 | private ProductCaretaker _productCaretaker = new ProductCaretaker(); 10 | private Product _product = Mock.Of(product => product.Name == "Rice" && product.Price == 4.99M); 11 | private IProductOriginator _productOriginator; 12 | private Product _memento; 13 | 14 | public MementoShould() 15 | { 16 | _productOriginator = new ProductOriginator(_product); 17 | _memento = _productOriginator.GetMemento(); 18 | } 19 | [Fact] 20 | public void RetrieveCorrectMemento_WhenRetrievingFromCaretaker() 21 | { 22 | // Arrange 23 | _productCaretaker.AddProductMemento(_memento); 24 | 25 | // Act 26 | _memento.Price = 2.99M; 27 | _productOriginator.SetMemento(_productCaretaker.GetLastMemento()); 28 | _memento = _productOriginator.GetMemento(); 29 | 30 | // Assert 31 | _memento.Price.Should().Be(4.99M); 32 | } 33 | 34 | [Fact] 35 | public void RetrieveCorrectMemento_WhenRetrievingFromCaretakerAfterMultipleSet() 36 | { 37 | // Arrange 38 | _productCaretaker.AddProductMemento(_memento); 39 | 40 | // Act 41 | _memento.Price = 2.99M; 42 | _productCaretaker.AddProductMemento(_memento); 43 | _memento.Price = 7.99M; 44 | _productOriginator.SetMemento(_productCaretaker.GetLastMemento()); 45 | _memento = _productOriginator.GetMemento(); 46 | 47 | // Assert 48 | _memento.Price.Should().Be(2.99M); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Tests/ObserverTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Xunit; 3 | using Moq; 4 | using FluentAssertions; 5 | 6 | namespace Observer.Test 7 | { 8 | public class ObserverShould 9 | { 10 | [Fact] 11 | public void UpdateObserverOnce_WhenSubjectHasSpecials() 12 | { 13 | // Arrange 14 | var subject = new SpecialsSubject 15 | { 16 | SubjectState = "Footwear Sale" 17 | }; 18 | var mockObserver = new Mock(); 19 | // Act 20 | subject.Attach(mockObserver.Object); 21 | subject.Notify(); 22 | // Assert 23 | mockObserver.Verify(observer => observer.Update(subject), Times.Once()); 24 | } 25 | 26 | [Fact] 27 | public void NotCallUpdate_WhenObserverNotAttachedToSubject() 28 | { 29 | // Arrange 30 | var subject = new SpecialsSubject 31 | { 32 | SubjectState = "Footwear Sale" 33 | }; 34 | var mockObserver = new Mock(); 35 | // Act 36 | subject.SubjectState = "Footwear Sale"; 37 | subject.Notify(); 38 | // Assert 39 | mockObserver.Verify(observer => observer.Update(subject), Times.Never()); 40 | } 41 | 42 | [Fact] 43 | public void EchoMessage_WhenReceivingSubjectNotification() 44 | { 45 | // Arrange 46 | var mockSubject = new Mock(); 47 | var customer = new Customer(); 48 | // Act 49 | mockSubject.SetupGet(subj => subj.SubjectState).Returns("Footwear Sale"); 50 | mockSubject.Object.Attach(customer); 51 | // Assert 52 | customer.Update(mockSubject.Object).Should().Be("Customer received Footwear Sale"); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Tests/VisitorTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using Moq; 3 | using System; 4 | using FluentAssertions; 5 | 6 | namespace Visitor 7 | { 8 | public class VisitorShould 9 | { 10 | private GameElement _mario = new Sprite("Mario"); 11 | private GameElement _luigi = new Sprite("Luigi"); 12 | private TextElement _instruction = new Instruction(); 13 | 14 | [Fact] 15 | public void EnableMarioOnly_WhenRevealerAppliedToMario() 16 | { 17 | // Arrange 18 | var revealMario = new Revealer(); 19 | // Act 20 | _mario.Accept(revealMario); 21 | // Assert 22 | using (new FluentAssertions.Execution.AssertionScope("sprite states")) 23 | { 24 | _mario.Active.Should().BeTrue(); 25 | _luigi.Active.Should().BeFalse(); 26 | _instruction.Active.Should().BeFalse(); 27 | } 28 | } 29 | 30 | [Fact] 31 | public void EnableInstructionOnly_WhenRevealerAppliedToInstruction() 32 | { 33 | // Arrange 34 | var revealInstruction = new Revealer(); 35 | // Act 36 | _instruction.Accept(revealInstruction); 37 | using (new FluentAssertions.Execution.AssertionScope("sprite states")) 38 | { 39 | _mario.Active.Should().BeFalse(); 40 | _luigi.Active.Should().BeFalse(); 41 | _instruction.Active.Should().BeTrue(); 42 | } 43 | } 44 | 45 | [Fact] 46 | public void BeVisitedByMockObjectOnce_WhenAcceptionMockVisitor() 47 | { 48 | // Arrange 49 | var mockVisitor = new Mock(); 50 | // Act 51 | _mario.Accept(mockVisitor.Object); 52 | // Assert 53 | mockVisitor.Verify(dummyVisitor => dummyVisitor.Visit(_mario), Times.Once); 54 | } 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Tests/BuilderTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using FluentAssertions; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using AutoFixture; 6 | 7 | namespace Builder.Test 8 | { 9 | public class BuilderShould 10 | { 11 | private List _groceryShoppingCart; 12 | private Fixture _fixture = new Fixture(); 13 | public BuilderShould() 14 | { 15 | 16 | _groceryShoppingCart = new List 17 | { 18 | new Product { 19 | Name = _fixture.Create(), 20 | StockKeepingUnit = "Food-" + _fixture.Create(), 21 | RegularRetailPrice = 4.00M 22 | }, 23 | new Product { 24 | Name = _fixture.Create(), 25 | StockKeepingUnit = "Hygiene-" + _fixture.Create(), 26 | RegularRetailPrice = 1.50M 27 | }, 28 | new Product { 29 | Name = _fixture.Create(), 30 | StockKeepingUnit = "Food-" + _fixture.Create(), 31 | RegularRetailPrice = 6.00M 32 | } 33 | 34 | }; 35 | } 36 | 37 | [Fact] 38 | public void TotalPriceIsCorrect_WhenUsingBuiltStrategy() 39 | { 40 | // Arrange 41 | var strategy = new SkuCodeStartDiscountStrategyBuilder() 42 | .WithDiscountInPercentage(5) 43 | .ApplicableToSKUCode("Food") 44 | .Build(); 45 | // Act 46 | var totalAmount = _groceryShoppingCart 47 | .Select(product => strategy.CalculateDiscountedRetailPrice(product)) 48 | .Sum(); 49 | 50 | // Assert 51 | totalAmount.Should().BeApproximately(11.00M, 0.001M); 52 | 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Patterns/Prototype/Customer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Prototype 4 | { 5 | public class Customer : BasicCustomer 6 | { 7 | public override object Clone() 8 | { 9 | // Below is with pattern matching. If unfamiliar, this is how it reads: 10 | /* 11 | var clonedCustomer = this.MemberwiseClone() as BasicCustomer; 12 | 13 | if (clonedCustomer == null) 14 | { 15 | throw new InvalidOperationException("memberwise clone returned null"); 16 | } 17 | 18 | return clonedCustomer; 19 | */ 20 | if (this.MemberwiseClone() is not BasicCustomer clonedCustomer) 21 | { 22 | throw new InvalidOperationException("memberwise clone returned null"); 23 | } 24 | 25 | return clonedCustomer; 26 | } 27 | 28 | public override Customer DeepClone() 29 | { 30 | var customer = this.MemberwiseClone() as Customer ?? throw new InvalidOperationException("memberwise clone returned null"); 31 | 32 | customer.BillingAddress = this.BillingAddress != null ? new Address 33 | { 34 | StreetAddress = this.BillingAddress.StreetAddress, 35 | City = this.BillingAddress.City, 36 | State = this.BillingAddress.State, 37 | Country = this.BillingAddress.Country, 38 | PostCode = this.BillingAddress.PostCode 39 | } : null; 40 | 41 | customer.HomeAddress = this.HomeAddress != null ? new Address 42 | { 43 | StreetAddress = this.HomeAddress.StreetAddress, 44 | City = this.HomeAddress.City, 45 | State = this.HomeAddress.State, 46 | Country = this.HomeAddress.Country, 47 | PostCode = this.HomeAddress.PostCode 48 | } : null; 49 | 50 | return customer; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Patterns/Iterator/MatrixIterator.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Iterator 3 | { 4 | public class MatrixIterator : GenericIterator 5 | { 6 | private IMatrix _matrix; 7 | 8 | private int _rowNumber; 9 | private int _colNumber; 10 | private readonly int _totalRows; 11 | private readonly int _totalColumns; 12 | private readonly MatrixDirection _matrixDirection; 13 | 14 | 15 | public MatrixIterator(IMatrix matrix, MatrixDirection matrixDirection) 16 | { 17 | _matrix = matrix; 18 | _totalColumns = _matrix.TotalColumns(); 19 | _totalRows = _matrix.TotalRows(); 20 | _matrixDirection = matrixDirection; 21 | Reset(); 22 | } 23 | 24 | private void Increment(ref int axis1, ref int axis2, int axis1Length, int axis2Length) 25 | { 26 | if (axis1 < axis1Length - 1) 27 | { 28 | axis1++; 29 | } 30 | else 31 | { 32 | axis1 = 0; 33 | if (axis2 < axis2Length - 1) 34 | { 35 | axis2++; 36 | } 37 | } 38 | } 39 | 40 | public override bool HasNext() 41 | { 42 | return _colNumber < _matrix.TotalColumns() - 1 || _rowNumber < _matrix.TotalRows() - 1; 43 | } 44 | 45 | public override void Reset() 46 | { 47 | _rowNumber = _matrixDirection == MatrixDirection.ByColumn ? -1 : 0; 48 | _colNumber = _matrixDirection == MatrixDirection.ByRow ? -1 : 0; 49 | } 50 | 51 | public override bool MoveNext() 52 | { 53 | if (_matrixDirection == MatrixDirection.ByColumn) 54 | { 55 | Increment(ref _rowNumber, ref _colNumber, _totalRows, _totalColumns); 56 | } 57 | else 58 | { 59 | Increment(ref _colNumber, ref _rowNumber, _totalColumns, _totalRows); 60 | } 61 | 62 | return HasNext(); 63 | } 64 | 65 | public override object Current() 66 | { 67 | if (_rowNumber == -1 || _colNumber == -1) 68 | { 69 | return null; 70 | } 71 | return _matrix[_rowNumber, _colNumber]; 72 | } 73 | 74 | } 75 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C# Design Patterns 2 | 3 | [![.NET](https://github.com/deanagan/csharp-design-patterns/actions/workflows/dotnet.yml/badge.svg)](https://github.com/deanagan/csharp-design-patterns/actions/workflows/dotnet.yml) 4 | [![Coverage Status](https://coveralls.io/repos/github/deanagan/csharp-design-patterns/badge.svg?branch=master)](https://coveralls.io/github/deanagan/csharp-design-patterns?branch=master) 5 | 6 | ## csharp-design-patterns 7 | 8 | A bunch of demo codes for: 9 | 10 | 1. GoF Design patterns written in C# 11 | 2. Unit testing with Xunit 12 | 3. Mocking with Moq 13 | 4. Using FluentAssertions. 14 | 5. Ninject is used for testing the demo on DI based singletons. 15 | 16 | ## What Design Patterns can I find in this repository? 17 | 18 | - Patterns\AbstractFactory\AbstractFactory 19 | - Patterns\Adapter\Adapter 20 | - Patterns\Bridge\Bridge 21 | - Patterns\Builder\Builder 22 | - Patterns\ChainOfResponsibility\ChainOfResponsibility 23 | - Patterns\Command\Command 24 | - Patterns\Composite\Composite 25 | - Patterns\Decorator\Decorator 26 | - Patterns\Facade\Facade 27 | - Patterns\FactoryMethod\FactoryMethod 28 | - Patterns\Flyweight\Flyweight 29 | - Patterns\Mediator\Mediator 30 | - Patterns\Memento\Memento 31 | - Patterns\Observer\Observer 32 | - Patterns\Prototype\Prototype 33 | - Patterns\Proxy\Proxy 34 | - Patterns\SingletonDependencyInjection\SingletonDI 35 | - Patterns\State\State 36 | - Patterns\Strategy\Strategy 37 | - Patterns\TemplateMethod\TemplateMethod 38 | - Patterns\Visitor\Visitor 39 | - Patterns\Iterator\Iterator 40 | 41 | ## How To Run the Demos 42 | 43 | 1. Demos are divided into folders with the pattern. 44 | 2. To build all, enter `dotnet build` 45 | 3. To execute all the tests, enter `dotnet test` or `dotnet test -l:"console;verbosity=detailed"` to be more verbose. 46 | 47 | ## TODOs 48 | 49 | 1. Interpreter Pattern 50 | 2. Unit of Work 51 | 3. Repository Pattern 52 | 4. CQRS with MediatR 53 | 54 | ## Adding a Pattern 55 | 56 | 1. Under Patterns folder, create a new directory with the new pattern name. Example: `mkdir RepositoryPattern` 57 | 2. Change directory to new folder. 58 | 3. Create a new project. Example: `dotnet new classlib -n RepositoryPattern -o .` or just `dotnet new webapi` if you like having the same project name as the current folder. 59 | 60 | ## Adding a package 61 | 62 | 1. Change directory to the pattern you want to add a package for or to the test folder (if you want to add a test package). 63 | 2. Add the package: Example: `dotnet add package FluentAssertions` 64 | -------------------------------------------------------------------------------- /Tests/CompositeTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using Moq; 3 | using FluentAssertions; 4 | 5 | namespace Composite.Test 6 | { 7 | public class CompositeShould 8 | { 9 | 10 | private decimal ComputeDiscount(int discount, decimal price) 11 | { 12 | return price - (price * (discount / 100M)); 13 | } 14 | 15 | [Fact] 16 | public void HaveCorrectBookPrice_WhenBookIsPurchasedByItself() 17 | { 18 | // Arrange 19 | var book = new Book("Python Cookbook", 50.0M); 20 | 21 | // Act 22 | var price = ComputeDiscount(book.Discount, book.Price); 23 | 24 | // Assert 25 | price.Should().Be(50.0M); 26 | } 27 | 28 | [Fact] 29 | public void DiscountBookPriceAsSet_WhenBooksArePurchasedUnderBookComposite() 30 | { 31 | // Arrange 32 | var book1 = new Book("Effective C++", 65.0M); 33 | var book2 = new Book("More Effective C++", 35.0M); 34 | var bookComposite = new BookComposite(5, "Effective C++ Series"); 35 | bookComposite.Add(book1); 36 | bookComposite.Add(book2); 37 | 38 | // Act 39 | var compositePrice = ComputeDiscount(bookComposite.Discount, bookComposite.Price); 40 | 41 | // Assert 42 | compositePrice.Should().Be(95.0M); 43 | } 44 | 45 | [Fact] 46 | public void DiscountBookPricesWithTopSet_WhenBooksArePurchasedUnderBookCompositeWithAnotherComposite() 47 | { 48 | // Arrange 49 | var book1 = new Book("Effective C++", 65.0M); 50 | var book2 = new Book("More Effective C++", 35.0M); 51 | var bookCompositeCpp = new BookComposite(5, "Effective C++ Series"); 52 | bookCompositeCpp.Add(book1); 53 | bookCompositeCpp.Add(book2); 54 | 55 | var bookCompositeCSharp = new BookComposite(10, "Effective C# Series"); 56 | var book3 = new Book("Effective C#", 70.0M); 57 | var book4 = new Book("Pro C# Coding", 30.0M); 58 | 59 | bookCompositeCSharp.Add(book3); 60 | bookCompositeCSharp.Add(book4); 61 | 62 | var effectiveSeries = new BookComposite(15, "Effective Coding Series"); 63 | effectiveSeries.Add(bookCompositeCpp); 64 | effectiveSeries.Add(bookCompositeCSharp); 65 | 66 | // Act 67 | var effectiveSeriesPrice = ComputeDiscount(effectiveSeries.Discount, effectiveSeries.Price); 68 | 69 | // Assert 70 | effectiveSeriesPrice.Should().Be(170.0M); 71 | 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Tests/PrototypeTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using FluentAssertions; 3 | 4 | namespace Prototype.Test 5 | { 6 | public class PrototypeShould 7 | { 8 | private BasicCustomer _basicCustomer; 9 | 10 | public PrototypeShould() 11 | { 12 | _basicCustomer = new Customer(); 13 | _basicCustomer.FirstName = "John"; 14 | _basicCustomer.LastName = "Smith"; 15 | _basicCustomer.HomeAddress = new Address 16 | { 17 | StreetAddress = "1 Kent Street", 18 | City = "Sydney", 19 | State = "New South Wales", 20 | Country = "Australia", 21 | PostCode = "2000" 22 | }; 23 | _basicCustomer.BillingAddress = new Address 24 | { 25 | StreetAddress = _basicCustomer.HomeAddress.StreetAddress, 26 | City = _basicCustomer.HomeAddress.City, 27 | State = _basicCustomer.HomeAddress.State, 28 | Country = _basicCustomer.HomeAddress.Country, 29 | PostCode = _basicCustomer.HomeAddress.PostCode 30 | }; 31 | } 32 | 33 | [Fact] 34 | public void ShallowCloneCustomerWithSameAddress_WhenAddressObjectsAreShared() 35 | { 36 | // Arrange 37 | var customer = (Customer)_basicCustomer.Clone(); 38 | 39 | // Act 40 | var newStreetAddress = "26 York Street"; 41 | var relatedCustomer = (Customer)customer.Clone(); 42 | relatedCustomer.FirstName = "Jane"; 43 | relatedCustomer.HomeAddress!.StreetAddress = newStreetAddress; 44 | 45 | // Assert 46 | customer.HomeAddress.Should().Be(relatedCustomer.HomeAddress); 47 | } 48 | 49 | [Fact] 50 | public void DeepCloneCustomerWithDifferentAddressButWithSameFirstName_WhenAddressObjectsAreDifferent() 51 | { 52 | // Arrange 53 | var customer = (Customer)_basicCustomer.Clone(); 54 | 55 | // Act 56 | var diffCustomer = customer.DeepClone(); 57 | diffCustomer.FirstName = "Peter"; 58 | diffCustomer.LastName = "Wick"; 59 | diffCustomer.HomeAddress!.StreetAddress = "57 Hassall Ridge"; 60 | diffCustomer.HomeAddress.City = "Lexington"; 61 | diffCustomer.HomeAddress.State = "Kentucky"; 62 | diffCustomer.HomeAddress.Country = "United States Of America"; 63 | diffCustomer.HomeAddress.PostCode = "40511"; 64 | 65 | // Assert 66 | customer.HomeAddress.Should().NotBeEquivalentTo(diffCustomer.HomeAddress); 67 | customer.FirstName.Should().BeEquivalentTo("John"); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Tests/Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | false 6 | enable 7 | false 8 | true 9 | 10 | 11 | 12 | 13 | 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | all 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Tests/AbstractFactory/AbstractFactoryTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Xunit; 4 | using FluentAssertions; 5 | 6 | namespace AbstractFactory.Test 7 | { 8 | public class AbstractFactoryShould 9 | { 10 | public static IEnumerable FactoriesAndProcessorExpectations 11 | { 12 | get 13 | { 14 | yield return new object[] { (new LenovoPartsFactory()), "AMD", 2.1 }; 15 | yield return new object[] { (new DellPartsFactory()), "Intel", 1.8 }; 16 | } 17 | } 18 | 19 | [Theory] 20 | [MemberData(nameof(FactoriesAndProcessorExpectations))] 21 | public void HaveCorrectProcessor_WhenUsingFactory(ILaptopPartsFactory factory, string name, double speed) 22 | { 23 | // Act 24 | var processor = factory.CreateProcessor(); 25 | 26 | // Assert 27 | using (new FluentAssertions.Execution.AssertionScope("processor")) 28 | { 29 | processor.BrandName().Should().Be(name); 30 | processor.SpeedInGigaHertz().Should().Be(speed); 31 | } 32 | } 33 | 34 | public static IEnumerable FactoriesAndStorageExpectations 35 | { 36 | get 37 | { 38 | yield return new object[] { (new LenovoPartsFactory()), "hdd", 50 }; 39 | yield return new object[] { (new DellPartsFactory()), "ssd", 250 }; 40 | } 41 | } 42 | 43 | [Theory] 44 | [MemberData(nameof(FactoriesAndStorageExpectations))] 45 | public void HaveCorrectStorage_WhenUsingFactory(ILaptopPartsFactory factory, string hwtype, int speed) 46 | { 47 | // Act 48 | var storage = factory.CreateStorage(); 49 | 50 | // Assert 51 | using (new FluentAssertions.Execution.AssertionScope("storage")) 52 | { 53 | storage.HardwareType().Should().Be(hwtype); 54 | storage.ReadSpeedInMBytesPerSec().Should().Be(speed); 55 | } 56 | } 57 | 58 | public static IEnumerable FactoriesType 59 | { 60 | get 61 | { 62 | yield return new object[] { (new LenovoPartsFactory()) }; 63 | yield return new object[] { (new DellPartsFactory()) }; 64 | } 65 | } 66 | 67 | [Theory] 68 | [MemberData(nameof(FactoriesType))] 69 | public void ImplementIStorage_WhenUsingFactory(ILaptopPartsFactory factory) 70 | { 71 | // Act 72 | var storage = factory.CreateStorage(); 73 | 74 | // Assert 75 | storage.GetType().Should().Implement(); 76 | } 77 | 78 | [Theory] 79 | [MemberData(nameof(FactoriesType))] 80 | public void ImplementIProcessor_WhenUsingFactory(ILaptopPartsFactory factory) 81 | { 82 | // Act 83 | var processor = factory.CreateProcessor(); 84 | 85 | // Assert 86 | processor.GetType().Should().Implement(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Tests/ProxyTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Xunit; 4 | using Moq; 5 | using FluentAssertions; 6 | 7 | namespace Proxy.Test 8 | { 9 | public class ProxyShould 10 | { 11 | Dictionary _products; 12 | IEntries _entries; 13 | IUser _adminUser; 14 | IUser _regularUser; 15 | delegate IAuthenticationController CreateAuthController(IUser user); 16 | CreateAuthController _createAuthController; 17 | 18 | public ProxyShould() 19 | { 20 | _products = new Dictionary 21 | { 22 | {1, Mock.Of( 23 | productInfo => 24 | productInfo.Name == "Xbox360" && 25 | productInfo.Id == 1) 26 | } 27 | }; 28 | _adminUser = Mock.Of (user => user.IsAdmin == true); 29 | _regularUser = Mock.Of (user => user.IsAdmin == false); 30 | 31 | _createAuthController = (user) => new AuthenticationController(user); 32 | _entries = new Entries(_products); 33 | } 34 | 35 | [Fact] 36 | public void GetProductInfoSuccessfully_WhenReadingAsRegularUser() 37 | { 38 | // Arrange 39 | var authCtrl = _createAuthController(_regularUser); 40 | var entries = new EntriesProxy(authCtrl, _entries); 41 | 42 | // Act 43 | var productInfo = entries.Get(1); 44 | 45 | //Assert 46 | productInfo.Id.Should().Be(1); 47 | productInfo.Name.Should().Be("Xbox360"); 48 | } 49 | 50 | [Fact] 51 | public void GetProductInfoSuccessfully_WhenReadingAsAdminUser() 52 | { 53 | // Arrange 54 | var authCtrl = _createAuthController(_adminUser); 55 | var entries = new EntriesProxy(authCtrl, _entries); 56 | 57 | // Act 58 | var productInfo = entries.Get(1); 59 | 60 | //Assert 61 | productInfo.Id.Should().Be(1); 62 | productInfo.Name.Should().Be("Xbox360"); 63 | } 64 | 65 | [Fact] 66 | public void DeleteProductInfoSuccessfully_WhenDeletingAsAdminUser() 67 | { 68 | // Arrange 69 | var authCtrl = _createAuthController(_adminUser); 70 | var entries = new EntriesProxy(authCtrl, _entries); 71 | 72 | // Act 73 | var isDeleted = entries.Delete(1); 74 | 75 | //Assert 76 | isDeleted.Should().Be(true); 77 | entries.Get(1).Should().Be(null); 78 | } 79 | 80 | [Fact] 81 | public void BeUnableToDeleteProductInfo_WhenDeletingAsRegularUser() 82 | { 83 | // Arrange 84 | var authCtrl = _createAuthController(_regularUser); 85 | var entries = new EntriesProxy(authCtrl, _entries); 86 | 87 | // Act 88 | var isDeleted = entries.Delete(1); 89 | 90 | //Assert 91 | isDeleted.Should().Be(false); 92 | entries.Get(1).Should().NotBe(null); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Tests/FactoryMethodTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using System.Collections.Generic; 4 | using Moq; 5 | using FluentAssertions; 6 | 7 | namespace FactoryMethod.Test 8 | { 9 | public class FactoryMethodShould 10 | { 11 | private const int GoldPerksThreshold = 20000; 12 | private const int SilverPerksThreshold = 10000; 13 | private List<(int, string)> _thresholds = new List<(int, string)> { 14 | ( GoldPerksThreshold, "GoldPerks"), 15 | ( SilverPerksThreshold, "SilverPerks") 16 | }; 17 | 18 | [Fact] 19 | public void ReturnBasicPerksType_WhenMilesEarnedIsLessThanSilver() 20 | { 21 | // Arrange 22 | var perksFactory = new PerksFactory(_thresholds); 23 | 24 | // Act 25 | var perks = perksFactory.GetPerks(8000); 26 | 27 | // Assert 28 | perks.Should().BeOfType(); 29 | } 30 | 31 | [Fact] 32 | public void HaveCorrectTotalMiles_WhenUsingBasicPerks() 33 | { 34 | // Arrange 35 | var perksFactory = new PerksFactory(_thresholds); 36 | var calc = new EarningBonusCalculator(perksFactory); 37 | 38 | // Act 39 | var totalMiles = calc.UpdatedMiles(8000, 100); 40 | 41 | // Assert 42 | totalMiles.Should().Be(8100); 43 | } 44 | 45 | [Fact] 46 | public void GetSilverPerks_WhenMilesEarnedIsAtSilverThreshold() 47 | { 48 | // Arrange 49 | var perksFactory = new PerksFactory(_thresholds); 50 | 51 | // Act 52 | var perks = perksFactory.GetPerks(10000); 53 | 54 | // Assert 55 | perks.Should().BeOfType(); 56 | } 57 | 58 | [Fact] 59 | public void AddingMilesAtSilverPerks_AddsSilverPerkMilesEarned() 60 | { 61 | // Arrange 62 | var perksFactory = new PerksFactory(_thresholds); 63 | var calc = new EarningBonusCalculator(perksFactory); 64 | 65 | // Act 66 | var totalMiles = calc.UpdatedMiles(10000, 2000); 67 | 68 | // Assert 69 | totalMiles.Should().Be(13000); 70 | } 71 | 72 | [Fact] 73 | public void GetGoldPerks_WhenMilesEarnedIsAtGoldThreshold() 74 | { 75 | // Arrange 76 | var perksFactory = new PerksFactory(_thresholds); 77 | 78 | // Act 79 | var perks = perksFactory.GetPerks(20001); 80 | 81 | // Assert 82 | perks.Should().BeOfType(); 83 | } 84 | 85 | [Fact] 86 | public void AddingMilesAtGoldPerks_AddsGoldPerkMilesEarned() 87 | { 88 | // Arrange 89 | var perksFactory = new PerksFactory(_thresholds); 90 | var calc = new EarningBonusCalculator(perksFactory); 91 | 92 | // Act 93 | var totalMiles = calc.UpdatedMiles(20000, 2000); 94 | 95 | // Assert 96 | totalMiles.Should().Be(24000); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Tests/FacadeTestData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using Moq; 5 | using FluentAssertions; 6 | 7 | namespace Facade.Test 8 | { 9 | public class FacadeTestData 10 | { 11 | 12 | public static IBillingAddress CreateMockBillingAddress() 13 | { 14 | return Mock.Of 15 | ( 16 | billingAddress => 17 | billingAddress.FirstName == "John" && 18 | billingAddress.LastName == "Wick" && 19 | billingAddress.Address == "W 43rd Street" && 20 | billingAddress.City == "New York" && 21 | billingAddress.ZipCode == "10036" 22 | ); 23 | } 24 | 25 | public static ICreditCard CreateMockCreditCard(DateTime expiryDate) 26 | { 27 | return Mock.Of 28 | ( 29 | creditCard => 30 | creditCard.Type == CreditCard.VISA && 31 | creditCard.AccountNumber == "123456789" && 32 | creditCard.CVC == "987" && 33 | creditCard.ExpiryDate == expiryDate 34 | ); 35 | } 36 | 37 | public static IEnvironment CreateMockEnvironment() 38 | { 39 | return Mock.Of 40 | ( 41 | environment => environment.environmentVariableTarget == EnvironmentTarget.UNINITIALIZED 42 | ); 43 | } 44 | 45 | public static IMerchantAuthenticationType CreateMockMerchantAuthenticationType() 46 | { 47 | return Mock.Of 48 | ( 49 | merchAuthType => merchAuthType.LoginID == null && 50 | merchAuthType.TransactionKey == null 51 | ); 52 | } 53 | 54 | public static ITransactionRequest CreateMockTransactionRequest( 55 | IBillingAddress billingAddress, 56 | ICreditCard creditCard) 57 | { 58 | return Mock.Of 59 | ( 60 | txnReq => txnReq.Amount == 123.45M && 61 | txnReq.BillingAddress == billingAddress && 62 | txnReq.CreditCard == creditCard 63 | ); 64 | } 65 | 66 | public static ITransactionController CreateMockTransactionController( 67 | ITransactionRequest txnReq, 68 | ICreditCard creditCard) 69 | { 70 | var response = TransactionResponseType.DECLINED; 71 | 72 | if (creditCard.ExpiryDate.CompareTo(DateTime.Today) > 0 && 73 | txnReq.Amount > 0.0M) 74 | { 75 | response = TransactionResponseType.OK; 76 | } 77 | // We use a mock here rather than linq to moq because we need to setup execute for a strict mock behavior. 78 | var mockTxnCtrl = new Mock(MockBehavior.Strict); 79 | var seq = new MockSequence(); 80 | 81 | mockTxnCtrl.InSequence(seq).Setup(ex => ex.Execute()); 82 | mockTxnCtrl.InSequence(seq).Setup(ar => ar.GetApiResponse()).Returns(response); 83 | mockTxnCtrl.InSequence(seq).SetupGet(r => r.TransactionRequest).Returns(txnReq); 84 | 85 | return mockTxnCtrl.Object; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Tests/IteratorTest.cs: -------------------------------------------------------------------------------- 1 | 2 | using Xunit; 3 | using FluentAssertions; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace Iterator.Test 8 | { 9 | 10 | public class Matrix2 11 | { 12 | private double[,] storage = new double[3, 3]; 13 | 14 | public double this[int row, int column] 15 | { 16 | // The embedded array will throw out of range exceptions as appropriate. 17 | get { return storage[row, column]; } 18 | set { storage[row, column] = value; } 19 | } 20 | } 21 | 22 | public class IteratorShould 23 | { 24 | const int ROWS = 2; 25 | const int COLUMNS = 3; 26 | 27 | private struct MatrixValues 28 | { 29 | public int value; 30 | public int row; 31 | public int col; 32 | } 33 | 34 | public IteratorShould() 35 | { 36 | 37 | } 38 | 39 | private NumberMatrix SetupIterable(MatrixDirection direction) 40 | { 41 | var iterableObject = new NumberMatrix(direction, ROWS, COLUMNS); 42 | foreach(var element in GetMatrixValues(ROWS, COLUMNS)) 43 | { 44 | iterableObject[element.row, element.col] = element.value; 45 | } 46 | // Iterable object data initialized to: 47 | // [ 1, 2, 3 ] 48 | // [ 4, 5, 6 ] 49 | 50 | return iterableObject; 51 | } 52 | 53 | private IEnumerable GetMatrixValues(int rows, int columns) 54 | { 55 | return Enumerable.Range(1, rows*columns).Select((v, i) => new MatrixValues{ value = v, row = i / columns, col = i % columns}); 56 | } 57 | 58 | [Fact] 59 | public void ReturnCorrectNumbers_WhenInvokedByRow() 60 | { 61 | var iterableObject = SetupIterable(MatrixDirection.ByRow); 62 | var expected = 1; 63 | foreach(var num in iterableObject) 64 | { 65 | num.Should().Be(expected++); 66 | } 67 | 68 | expected = 1; 69 | foreach(var num in iterableObject) 70 | { 71 | num.Should().Be(expected++); 72 | } 73 | } 74 | 75 | [Fact] 76 | public void ReturnCorrectNumbers_WhenInvokedByColumn() 77 | { 78 | var iterableObject = SetupIterable(MatrixDirection.ByColumn); 79 | var expected = new int[] { 1,4,2,5,3,6 }; 80 | var index = 0; 81 | 82 | foreach(var num in iterableObject) 83 | { 84 | num.Should().Be(expected[index++]); 85 | } 86 | 87 | index = 0; 88 | foreach(var num in iterableObject) 89 | { 90 | num.Should().Be(expected[index++]); 91 | } 92 | } 93 | 94 | [Fact] 95 | public void ReturnCorrectNumber_WhenInvokingManually() 96 | { 97 | var iterableObject = SetupIterable(MatrixDirection.ByColumn); 98 | var enumerator = iterableObject.GetEnumerator(); 99 | enumerator.Current.Should().BeNull(); 100 | enumerator.MoveNext().Should().BeTrue(); 101 | enumerator.MoveNext().Should().BeTrue(); 102 | enumerator.MoveNext().Should().BeTrue(); 103 | enumerator.Current.Should().Be(2); 104 | 105 | } 106 | 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Tests/FacadeTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Moq; 3 | using FluentAssertions; 4 | using Xunit; 5 | 6 | namespace Facade.Test 7 | { 8 | public class FacadeShould 9 | { 10 | private readonly IEnvironment _environment = FacadeTestData.CreateMockEnvironment(); 11 | private readonly IMerchantAuthenticationType _merchantAuthenticationType = FacadeTestData.CreateMockMerchantAuthenticationType(); 12 | private readonly IBillingAddress _billingAddress = FacadeTestData.CreateMockBillingAddress(); 13 | private ITransactionController? _transactionController; 14 | 15 | private IPaymentProcessor CreatePaymentProcessor(DateTime creditCardExpiryDate) 16 | { 17 | var creditCard = FacadeTestData.CreateMockCreditCard(creditCardExpiryDate); 18 | var txnReq = FacadeTestData.CreateMockTransactionRequest(_billingAddress, creditCard); 19 | _transactionController = FacadeTestData.CreateMockTransactionController(txnReq, creditCard); 20 | return new PaymentProcessor(_environment, _merchantAuthenticationType, _transactionController); 21 | } 22 | [Fact] 23 | public void SetEnvironmentCorrectly_WhenFacadeInitializesPaymentGatewayInterface() 24 | { 25 | // Arrange 26 | var paymentProcessor = CreatePaymentProcessor(DateTime.Today.AddDays(1)); 27 | // Act 28 | paymentProcessor.InitializePaymentGatewayInterface(); 29 | // Assert 30 | _environment.environmentVariableTarget.Should().NotBe(EnvironmentTarget.UNINITIALIZED); 31 | } 32 | 33 | [Fact] 34 | public void MerchantAuthenticated_WhenFacadeInitializesPaymentGatewayInterface() 35 | { 36 | // Arrange 37 | var paymentProcessor = CreatePaymentProcessor(DateTime.Today.AddDays(1)); 38 | // Act 39 | paymentProcessor.InitializePaymentGatewayInterface(); 40 | // Assert 41 | using (new FluentAssertions.Execution.AssertionScope("merchant")) 42 | { 43 | _merchantAuthenticationType.LoginID.Should().NotBe(null); 44 | _merchantAuthenticationType.TransactionKey.Should().NotBe(null); 45 | } 46 | } 47 | 48 | [Fact] 49 | public void PaymentSubmittedSuccesfully_WhenFacadeInitializesCorrectlyAndChecksCreditCardExpiry() 50 | { 51 | // Arrange 52 | var paymentProcessor = CreatePaymentProcessor(DateTime.Today.AddDays(1)); 53 | // Act 54 | paymentProcessor.InitializePaymentGatewayInterface(); 55 | // Assert 56 | paymentProcessor.SubmitPayment().Should().BeTrue(); 57 | using (new FluentAssertions.Execution.AssertionScope("transaction controller")) 58 | { 59 | if (_transactionController != null) 60 | { 61 | var txnCtrl = Mock.Get(_transactionController); 62 | txnCtrl.Verify(tc => tc.Execute(), Times.Once); 63 | txnCtrl.Verify(ar => ar.GetApiResponse(), Times.Once); 64 | } 65 | } 66 | } 67 | 68 | [Fact] 69 | public void PaymentSubmissionFails_WhenCreditCardIsExpired() 70 | { 71 | // Arrange 72 | var paymentProcessor = CreatePaymentProcessor(DateTime.Today.AddDays(-1)); 73 | // Act 74 | paymentProcessor.InitializePaymentGatewayInterface(); 75 | // Assert 76 | paymentProcessor.SubmitPayment().Should().BeFalse(); 77 | } 78 | 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Tests/MediatorTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using Moq; 4 | using FluentAssertions; 5 | 6 | namespace Mediator.Test 7 | { 8 | public class MediatorShould 9 | { 10 | private Product _product; 11 | private IMediator _mediator; 12 | 13 | public MediatorShould() 14 | { 15 | _product = new Product { Item = "Ball", Location = "Sydney" }; 16 | _mediator = new PurchaseMediator(); 17 | } 18 | 19 | [Fact] 20 | public void AlertScreen_WhenOnePurchaserCompletesTransaction() 21 | { 22 | // Arrange 23 | var alertScreenForCompletedPurchaser = new Mock(); 24 | var alertScreenForActivePurchaser = new Mock(); 25 | var completedPurchaser = new Purchaser(alertScreenForCompletedPurchaser.Object, _mediator); 26 | var activePurchaser = new Purchaser(alertScreenForActivePurchaser.Object, _mediator); 27 | 28 | // Act 29 | _mediator.AddPurchaser(completedPurchaser); 30 | _mediator.AddPurchaser(activePurchaser); 31 | completedPurchaser.Complete(_product); 32 | 33 | // Assert 34 | using (new FluentAssertions.Execution.AssertionScope("purchaser")) 35 | { 36 | alertScreenForActivePurchaser.Verify(sc => sc.ShowMessage(_product.Item, _product.Location)); 37 | alertScreenForCompletedPurchaser.Verify(sc => sc.ShowMessage(It.IsAny(), It.IsAny()), Times.Never); 38 | } 39 | } 40 | 41 | [Fact] 42 | public void ExpectExceptionThrownByPurchaser_WhenAlertScreenIsNull() 43 | { 44 | // Act 45 | Action act = () => new Purchaser(null, _mediator); 46 | // Assert 47 | act.Should().ThrowExactly(); 48 | } 49 | 50 | [Fact] 51 | public void ExpectExceptionThrownByPurchaser_WhenMediatorIsNull() 52 | { 53 | // Arrange 54 | var alertScreen = Mock.Of(); 55 | // Act 56 | Action act = () => new Purchaser(alertScreen, null); 57 | // Assert 58 | act.Should().ThrowExactly(); 59 | } 60 | 61 | [Fact] 62 | public void ReturnFalse_WhenPurchaserIsNotRegistered() 63 | { 64 | // Arrange 65 | var purchaser = Mock.Of(); 66 | // Act 67 | var result = _mediator.BroadcastPurchaseCompletion(purchaser); 68 | // Assert 69 | result.Should().BeFalse(); 70 | } 71 | 72 | [Fact] 73 | public void ReturnFalse_WhenPurchaserRegisteredTwice() 74 | { 75 | // Arrange 76 | var purchaser = Mock.Of(); 77 | // Act 78 | var result = _mediator.AddPurchaser(purchaser) && _mediator.AddPurchaser(purchaser); 79 | // Assert 80 | result.Should().BeFalse(); 81 | } 82 | 83 | [Fact] 84 | public void NotCallAlertScreen_WhenThereIsNoPurchaserToAlert() 85 | { 86 | // Arrange 87 | var alertScreen = Mock.Of(); 88 | var purchaser = new Purchaser(alertScreen, _mediator); 89 | // Act 90 | _mediator.AddPurchaser(purchaser); 91 | purchaser.Complete(_product); 92 | // Assert 93 | Mock.Get(alertScreen).Verify(asc => asc.ShowMessage(It.IsAny(), It.IsAny()), Times.Never); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Tests/ChainOfResponsibilityTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using Moq; 4 | using FluentAssertions; 5 | using System.Collections.Generic; 6 | 7 | namespace ChainOfResponsibility.Test 8 | { 9 | public static class GatewayExtension 10 | { 11 | public static void IsCalledWith(this IPaymentGateway gateway, ICreditCard creditCard, Times times, ICreditCardHandler creditCardHandler) 12 | { 13 | Mock.Get(gateway) 14 | .Verify(paymentGateway => paymentGateway 15 | .SubmitVerification(It.Is(cch => cch.GetType() == creditCardHandler.GetType()), creditCard), times); 16 | } 17 | } 18 | 19 | public class ChainOfResponsibilityShould 20 | { 21 | private ICreditCardHandler _creditCardHandler; 22 | private IPaymentGateway _paymentGateway; 23 | 24 | private List validStartingCardNumbers = new List{ "3", "4", "5"}; 25 | 26 | public ChainOfResponsibilityShould() 27 | { 28 | _paymentGateway = Mock.Of(); 29 | _creditCardHandler = new VisaCardHandler(_paymentGateway); 30 | _creditCardHandler.SetNext(new AmexCardHandler(_paymentGateway)) 31 | .SetNext(new MastercardHandler(_paymentGateway)); 32 | } 33 | 34 | private ICreditCard PrepareCreditCardPayment(string cardNumber) 35 | { 36 | Mock.Get(_paymentGateway) 37 | .Setup(gateway => gateway 38 | .SubmitVerification(It.IsAny(), It.IsAny())) 39 | .Returns(validStartingCardNumbers.Contains(cardNumber.Substring(0,1))); 40 | return Mock.Of(creditCard => creditCard.Number == cardNumber); 41 | } 42 | 43 | public delegate ICreditCardHandler CreditCardHandlerCreator(IPaymentGateway gateway); 44 | public static IEnumerable ValidCreditCardNumbers 45 | { 46 | get 47 | { 48 | yield return new object[] {new CreditCardHandlerCreator((gateway) => new VisaCardHandler(gateway)), "41263434" }; 49 | yield return new object[] {new CreditCardHandlerCreator((gateway) => new AmexCardHandler(gateway)), "341263434" }; 50 | yield return new object[] {new CreditCardHandlerCreator((gateway) => new MastercardHandler(gateway)), "5384683" }; 51 | } 52 | } 53 | 54 | [Theory] 55 | [MemberData(nameof(ValidCreditCardNumbers))] 56 | public void BeValidWithCorrectCardType_WhenCreditCardSubmittedForVerification(CreditCardHandlerCreator creditCardHandlerCreator, string cardNumber) 57 | { 58 | // Arrange 59 | var creditCard = PrepareCreditCardPayment(cardNumber); 60 | var expectedCreditCardHandler = creditCardHandlerCreator(_paymentGateway); 61 | 62 | // Act 63 | var isValid = _creditCardHandler.IsCreditCardValid(creditCard); 64 | 65 | // Assert 66 | using (new FluentAssertions.Execution.AssertionScope("credit card")) 67 | { 68 | isValid.Should().BeTrue(); 69 | _paymentGateway.IsCalledWith(creditCard, Times.Once(), expectedCreditCardHandler); 70 | } 71 | } 72 | 73 | [Fact] 74 | public void NotSubmitPayments_WhenInvalidCardSubmittedForVerification() 75 | { 76 | // Arrange 77 | var invalidCreditCard = PrepareCreditCardPayment("9244683"); 78 | 79 | // Act 80 | var isValid = _creditCardHandler.IsCreditCardValid(invalidCreditCard); 81 | 82 | // Assert 83 | using (new FluentAssertions.Execution.AssertionScope("credit card")) 84 | { 85 | isValid.Should().BeFalse(); 86 | _paymentGateway.IsCalledWith(It.IsAny(), Times.Never(), It.IsAny()); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Tests/StrategyTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using Moq; 3 | using FluentAssertions; 4 | 5 | namespace Strategy.Test 6 | { 7 | public class StrategyShould 8 | { 9 | private delegate decimal PriceCalc(IProduct product); 10 | private ICoupon? _mockCoupon; 11 | private IProduct? _mockProduct; 12 | private IDiscountScheme? _discountScheme; 13 | 14 | ICoupon CreateMockCoupon(bool isExpired, int discountPercentage) 15 | { 16 | return Mock.Of 17 | ( 18 | coupon => 19 | coupon.IsExpired() == isExpired && 20 | coupon.DiscountPercentage() == discountPercentage 21 | ); 22 | } 23 | 24 | IProduct CreateMockProduct(decimal sellingPrice, bool isOnSale) 25 | { 26 | return Mock.Of 27 | ( 28 | product => 29 | product.SellingPrice() == sellingPrice && 30 | product.IsOnSale() == isOnSale 31 | ); 32 | } 33 | 34 | [Fact] 35 | public void ReturnMemberDiscountedPrice_WhenMemberWithValidCouponForRegularItem() 36 | { 37 | // Arrange 38 | _mockCoupon = CreateMockCoupon(false, 5); 39 | _mockProduct = CreateMockProduct(100M, false); 40 | _discountScheme = new MemberDiscountScheme(); 41 | 42 | // Act 43 | var price = _discountScheme.ComputePrice(_mockProduct, _mockCoupon); 44 | 45 | // Assert 46 | price.Should().Be(90M); 47 | } 48 | 49 | [Fact] 50 | public void ReturnNonMemberDiscountedPrice_WhenNonMemberWithValidCouponForRegularItem() 51 | { 52 | // Arrange 53 | _mockCoupon = CreateMockCoupon(false, 5); 54 | _mockProduct = CreateMockProduct(100M, false); 55 | _discountScheme = new NonMemberDiscountScheme(); 56 | 57 | // Act 58 | var price = _discountScheme.ComputePrice(_mockProduct, _mockCoupon); 59 | 60 | // Assert 61 | price.Should().Be(95M); 62 | } 63 | 64 | [Fact] 65 | public void ReturnBaseMemberDiscountOnly_WhenMemberUsesExpiredCoupon() 66 | { 67 | // Arrange 68 | _mockCoupon = CreateMockCoupon(true, 5); 69 | _mockProduct = CreateMockProduct(100M, false); 70 | _discountScheme = new MemberDiscountScheme(); 71 | 72 | // Act 73 | var price = _discountScheme.ComputePrice(_mockProduct, _mockCoupon); 74 | 75 | // Assert 76 | price.Should().Be(95M); 77 | } 78 | 79 | [Fact] 80 | public void ReturnNoDiscount_WhenNonMemberUsesExpiredCoupon() 81 | { 82 | // Arrange 83 | _mockCoupon = CreateMockCoupon(true, 5); 84 | _mockProduct = CreateMockProduct(100M, false); 85 | _discountScheme = new NonMemberDiscountScheme(); 86 | 87 | // Act 88 | var price = _discountScheme.ComputePrice(_mockProduct, _mockCoupon); 89 | 90 | // Assert 91 | price.Should().Be(100M); 92 | } 93 | 94 | [Fact] 95 | public void ReturnBaseMemberDiscountedPrice_WhenMemberUsesValidCouponForProductAlreadyOnSale() 96 | { 97 | // Arrange 98 | _mockCoupon = CreateMockCoupon(false, 5); 99 | _mockProduct = CreateMockProduct(100M, true); 100 | _discountScheme = new MemberDiscountScheme(); 101 | 102 | // Act 103 | var price = _discountScheme.ComputePrice(_mockProduct, _mockCoupon); 104 | 105 | // Assert 106 | price.Should().Be(90M); 107 | } 108 | 109 | [Fact] 110 | public void ReturnSalePriceOnly_WhenNonMemberUsesValidCouponForProductAlreadyOnSale() 111 | { 112 | // Arrange 113 | _mockCoupon = CreateMockCoupon(false, 5); 114 | _mockProduct = CreateMockProduct(97M, true); 115 | _discountScheme = new NonMemberDiscountScheme(); 116 | 117 | // Act 118 | var price = _discountScheme.ComputePrice(_mockProduct, _mockCoupon); 119 | 120 | // Assert 121 | price.Should().Be(97M); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Tests/DecoratorTest.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using Xunit; 3 | using FluentAssertions; 4 | using AutoFixture; 5 | 6 | namespace Decorator.Test 7 | { 8 | public class DecoratorShould 9 | { 10 | private readonly Fixture _fixture = new(); 11 | private IHtmlElement _htmlElement; 12 | private string _DummyElement; 13 | private string _DummyLink; 14 | 15 | public DecoratorShould() 16 | { 17 | _htmlElement = Mock.Of(); 18 | _DummyElement = _fixture.Create(); 19 | _DummyLink = "www." + _fixture.Create() + ".com"; 20 | Mock.Get(_htmlElement).Setup(htmlElement => htmlElement.GetHtmlElement()).Returns(_DummyElement); 21 | } 22 | 23 | [Fact] 24 | public void BoldenHtmlElement_WhenUsingBoldenDecorator() 25 | { 26 | // Arrange 27 | var bolden = new BoldenHtmlElement(_htmlElement); 28 | // Act 29 | var result = bolden.GetHtmlElement(); 30 | // Assert 31 | using (new FluentAssertions.Execution.AssertionScope("boldened html element")) 32 | { 33 | result.Should().Be($"{_DummyElement}"); 34 | Mock.Get(_htmlElement).Verify(htmlElement => htmlElement.GetHtmlElement(), Times.Once); 35 | } 36 | } 37 | 38 | [Fact] 39 | public void ItalicizeHtmlElement_WhenUsingItalicizeDecorator() 40 | { 41 | // Arrange 42 | var italicized = new ItalicizeHtmlElement(_htmlElement); 43 | // Act 44 | var result = italicized.GetHtmlElement(); 45 | // Assert 46 | using (new FluentAssertions.Execution.AssertionScope("italicized html element")) 47 | { 48 | result.Should().Be($"{_DummyElement}"); 49 | Mock.Get(_htmlElement).Verify(htmlElement => htmlElement.GetHtmlElement(), Times.Once); 50 | } 51 | } 52 | 53 | [Fact] 54 | public void HyperlinkHtmlElement_WhenUsingHyperlinkifyDecorator() 55 | { 56 | // Arrange 57 | var hyperlinked = new HyperLinkifyHtmlElement(_DummyLink, _htmlElement); 58 | // Act 59 | var result = hyperlinked.GetHtmlElement(); 60 | // Assert 61 | using (new FluentAssertions.Execution.AssertionScope("hyperlinked html element")) 62 | { 63 | result.Should().Be($"{_DummyElement}"); 64 | Mock.Get(_htmlElement).Verify(htmlElement => htmlElement.GetHtmlElement(), Times.Once); 65 | } 66 | } 67 | 68 | [Fact] 69 | public void HyperlinkAndBoldenHtmlElement_WhenUsingBoldenHyperlinkifyDecorator() 70 | { 71 | // Arrange 72 | var hyperlinked = new HyperLinkifyHtmlElement(_DummyLink, _htmlElement); 73 | var boldenedHyperlinked = new BoldenHtmlElement(hyperlinked); 74 | // Act 75 | var result = boldenedHyperlinked.GetHtmlElement(); 76 | // Assert 77 | using (new FluentAssertions.Execution.AssertionScope("hyperlinked and boldened html element")) 78 | { 79 | result.Should().Be($"{_DummyElement}"); 80 | Mock.Get(_htmlElement).Verify(htmlElement => htmlElement.GetHtmlElement(), Times.Once); 81 | } 82 | } 83 | 84 | 85 | [Fact] 86 | public void HyperlinkItalicizeAndBoldenHtmlElement_WhenUsingHyperlinkifyItalicizeBoldenDecorator() 87 | { 88 | // Arrange 89 | var hyperlinked = new HyperLinkifyHtmlElement(_DummyLink, _htmlElement); 90 | var italicizedHyperlinked = new ItalicizeHtmlElement(hyperlinked); 91 | var boldenedItalicizedHyperlinked = new BoldenHtmlElement(italicizedHyperlinked); 92 | 93 | // Act 94 | var result = boldenedItalicizedHyperlinked.GetHtmlElement(); 95 | // Assert 96 | using (new FluentAssertions.Execution.AssertionScope("hyperlinked and boldened html element")) 97 | { 98 | result.Should().Be($"{_DummyElement}"); 99 | Mock.Get(_htmlElement).Verify(htmlElement => htmlElement.GetHtmlElement(), Times.Once); 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Tests/CommandTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Xunit; 4 | using Moq; 5 | using FluentAssertions; 6 | 7 | namespace Command.Test 8 | { 9 | public class CommandShould 10 | { 11 | private IProductList _ProductList; 12 | private IInvoker _productInvoker; 13 | 14 | public CommandShould() 15 | { 16 | _ProductList = Mock.Of 17 | ( 18 | productList => 19 | productList.Name == "WishList" && 20 | productList.Products == (new List()) 21 | ); 22 | 23 | _productInvoker = new ProductCommandInvoker(); 24 | } 25 | 26 | public static IEnumerable GetProducts() 27 | { 28 | yield return new object[] { 29 | Mock.Of 30 | ( 31 | product => 32 | product.Name == "Product 1" && 33 | product.Price == 45.00M 34 | ), 35 | Mock.Of 36 | ( 37 | product => 38 | product.Name == "Product 2" && 39 | product.Price == 55.00M 40 | ) 41 | }; 42 | } 43 | 44 | 45 | [Theory] 46 | [MemberData(nameof(GetProducts))] 47 | public void AddItems_WhenUsingAddCommand(IProduct product1, IProduct product2) 48 | { 49 | // Arrange 50 | var addProduct1Command = new AddCommand(_ProductList, product1); 51 | var addProduct2Command = new AddCommand(_ProductList, product2); 52 | 53 | // Act 54 | _productInvoker.AddCommand("addproduct1", addProduct1Command); 55 | _productInvoker.AddCommand("addproduct2", addProduct2Command); 56 | _productInvoker.InvokeCommand("addproduct1"); 57 | _productInvoker.InvokeCommand("addproduct2"); 58 | 59 | // Assert 60 | _ProductList.Products.Should().NotBeNullOrEmpty() 61 | .And 62 | .HaveCount(2) 63 | .And 64 | .OnlyHaveUniqueItems() 65 | .And 66 | .Contain(product1) 67 | .And 68 | .Contain(product2); 69 | } 70 | 71 | [Theory] 72 | [MemberData(nameof(GetProducts))] 73 | public void AddTwoRemoveOneCorrectly_WhenUsingRemoveCommand(IProduct product1, IProduct product2) 74 | { 75 | // Arrange 76 | var addProduct1Command = new AddCommand(_ProductList, product1); 77 | _productInvoker.AddCommand("addproduct1", addProduct1Command); 78 | 79 | var addProduct2Command = new AddCommand(_ProductList, product2); 80 | _productInvoker.AddCommand("addproduct2", addProduct2Command); 81 | 82 | var removeProduct2Command = new RemoveCommand(_ProductList, product2); 83 | _productInvoker.AddCommand("removeproduct2", removeProduct2Command); 84 | 85 | // Act 86 | _productInvoker.InvokeCommand("addproduct1"); 87 | _productInvoker.InvokeCommand("addproduct2"); 88 | _productInvoker.InvokeCommand("removeproduct2"); 89 | 90 | // Assert 91 | _ProductList.Products.Should().NotBeNullOrEmpty() 92 | .And 93 | .HaveCount(1) 94 | .And 95 | .OnlyHaveUniqueItems() 96 | .And 97 | .NotContain(product2) 98 | .And 99 | .Contain(product1); 100 | } 101 | 102 | [Theory] 103 | [MemberData(nameof(GetProducts))] 104 | public void ClearItems_WhenUsingClearCommand(IProduct product1, IProduct product2) 105 | { 106 | // Arrange 107 | var addProduct1Command = new AddCommand(_ProductList, product1); 108 | _productInvoker.AddCommand("addproduct1", addProduct1Command); 109 | 110 | var addProduct2Command = new AddCommand(_ProductList, product2); 111 | _productInvoker.AddCommand("addproduct2", addProduct2Command); 112 | 113 | var clearCommand = new ClearCommand(_ProductList); 114 | _productInvoker.AddCommand("clearall", clearCommand); 115 | 116 | // Act 117 | _productInvoker.InvokeCommand("addproduct1"); 118 | _productInvoker.InvokeCommand("addproduct2"); 119 | _productInvoker.InvokeCommand("clearall"); 120 | 121 | // Assert 122 | _ProductList.Products.Should().BeEmpty(); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /DesignPatterns.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{1FDEC8DA-7520-4F55-BC93-173BEAE39827}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Patterns", "Patterns", "{A4B3BA63-F524-47C8-9DF8-1DB344F372D3}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AbstractFactory", "Patterns\AbstractFactory\AbstractFactory.csproj", "{7A711FEA-526A-4D96-9411-153B7367508F}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Adapter", "Patterns\Adapter\Adapter.csproj", "{D67A928C-6EAC-40FA-BD5E-7962937F938A}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bridge", "Patterns\Bridge\Bridge.csproj", "{58011005-A288-48BB-9125-7C206668C681}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Builder", "Patterns\Builder\Builder.csproj", "{2BBB4134-1732-4654-B4BC-7C00A9B3CA5A}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChainOfResponsibility", "Patterns\ChainOfResponsibility\ChainOfResponsibility.csproj", "{6E6084A9-4B09-49A8-8078-81562890F80A}" 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Command", "Patterns\Command\Command.csproj", "{40B33EB5-4CE2-4B45-8581-4F9415E79F79}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Composite", "Patterns\Composite\Composite.csproj", "{98CBDEE5-BCC2-4170-A556-525B3DBC017F}" 23 | EndProject 24 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Decorator", "Patterns\Decorator\Decorator.csproj", "{A736C257-61CA-4271-B7F8-422FFD6C0AD9}" 25 | EndProject 26 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Facade", "Patterns\Facade\Facade.csproj", "{4CF9FCA4-987E-483F-BF15-65F8286D6F1F}" 27 | EndProject 28 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FactoryMethod", "Patterns\FactoryMethod\FactoryMethod.csproj", "{A7F9A11F-AC66-41B3-99F3-CA8DB7408EA8}" 29 | EndProject 30 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flyweight", "Patterns\Flyweight\Flyweight.csproj", "{E49A6C50-271A-4D21-8523-D6554438F3E0}" 31 | EndProject 32 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mediator", "Patterns\Mediator\Mediator.csproj", "{E17E2548-287E-4E81-A839-D043F38CDEEB}" 33 | EndProject 34 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Memento", "Patterns\Memento\Memento.csproj", "{A9F0813C-F254-4AB1-8759-E47A3E3DE87C}" 35 | EndProject 36 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Observer", "Patterns\Observer\Observer.csproj", "{C079542D-0682-4D79-8443-CD167F6A27F7}" 37 | EndProject 38 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prototype", "Patterns\Prototype\Prototype.csproj", "{39E0C179-AD30-4E9D-B774-4A4EA7880949}" 39 | EndProject 40 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Proxy", "Patterns\Proxy\Proxy.csproj", "{3A3E6817-728A-4F65-AF2C-711715E35458}" 41 | EndProject 42 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "State", "Patterns\State\State.csproj", "{E70DF3C6-7548-48EA-B07F-C421D251EAF4}" 43 | EndProject 44 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Strategy", "Patterns\Strategy\Strategy.csproj", "{D9770ACB-BA41-462E-BA1C-FC5F2C3C1BAC}" 45 | EndProject 46 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemplateMethod", "Patterns\TemplateMethod\TemplateMethod.csproj", "{12CEDEEA-ADC1-4247-8B78-BA64FAE559AA}" 47 | EndProject 48 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SingletonDI", "Patterns\SingletonDependencyInjection\SingletonDI.csproj", "{707DF674-6E7C-4EE4-8BF7-E8E806C5A704}" 49 | EndProject 50 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Visitor", "Patterns\Visitor\Visitor.csproj", "{99E7FED9-E1D3-4F8D-895B-A31258F6BCB2}" 51 | EndProject 52 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Iterator", "Patterns\Iterator\Iterator.csproj", "{E63E0F5B-5DCF-4CDA-AFBB-DB99F20E0C99}" 53 | EndProject 54 | Global 55 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 56 | Debug|Any CPU = Debug|Any CPU 57 | Debug|x64 = Debug|x64 58 | Debug|x86 = Debug|x86 59 | Release|Any CPU = Release|Any CPU 60 | Release|x64 = Release|x64 61 | Release|x86 = Release|x86 62 | EndGlobalSection 63 | GlobalSection(SolutionProperties) = preSolution 64 | HideSolutionNode = FALSE 65 | EndGlobalSection 66 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 67 | {1FDEC8DA-7520-4F55-BC93-173BEAE39827}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 68 | {1FDEC8DA-7520-4F55-BC93-173BEAE39827}.Debug|Any CPU.Build.0 = Debug|Any CPU 69 | {1FDEC8DA-7520-4F55-BC93-173BEAE39827}.Debug|x64.ActiveCfg = Debug|Any CPU 70 | {1FDEC8DA-7520-4F55-BC93-173BEAE39827}.Debug|x64.Build.0 = Debug|Any CPU 71 | {1FDEC8DA-7520-4F55-BC93-173BEAE39827}.Debug|x86.ActiveCfg = Debug|Any CPU 72 | {1FDEC8DA-7520-4F55-BC93-173BEAE39827}.Debug|x86.Build.0 = Debug|Any CPU 73 | {1FDEC8DA-7520-4F55-BC93-173BEAE39827}.Release|Any CPU.ActiveCfg = Release|Any CPU 74 | {1FDEC8DA-7520-4F55-BC93-173BEAE39827}.Release|Any CPU.Build.0 = Release|Any CPU 75 | {1FDEC8DA-7520-4F55-BC93-173BEAE39827}.Release|x64.ActiveCfg = Release|Any CPU 76 | {1FDEC8DA-7520-4F55-BC93-173BEAE39827}.Release|x64.Build.0 = Release|Any CPU 77 | {1FDEC8DA-7520-4F55-BC93-173BEAE39827}.Release|x86.ActiveCfg = Release|Any CPU 78 | {1FDEC8DA-7520-4F55-BC93-173BEAE39827}.Release|x86.Build.0 = Release|Any CPU 79 | {7A711FEA-526A-4D96-9411-153B7367508F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 80 | {7A711FEA-526A-4D96-9411-153B7367508F}.Debug|Any CPU.Build.0 = Debug|Any CPU 81 | {7A711FEA-526A-4D96-9411-153B7367508F}.Debug|x64.ActiveCfg = Debug|Any CPU 82 | {7A711FEA-526A-4D96-9411-153B7367508F}.Debug|x64.Build.0 = Debug|Any CPU 83 | {7A711FEA-526A-4D96-9411-153B7367508F}.Debug|x86.ActiveCfg = Debug|Any CPU 84 | {7A711FEA-526A-4D96-9411-153B7367508F}.Debug|x86.Build.0 = Debug|Any CPU 85 | {7A711FEA-526A-4D96-9411-153B7367508F}.Release|Any CPU.ActiveCfg = Release|Any CPU 86 | {7A711FEA-526A-4D96-9411-153B7367508F}.Release|Any CPU.Build.0 = Release|Any CPU 87 | {7A711FEA-526A-4D96-9411-153B7367508F}.Release|x64.ActiveCfg = Release|Any CPU 88 | {7A711FEA-526A-4D96-9411-153B7367508F}.Release|x64.Build.0 = Release|Any CPU 89 | {7A711FEA-526A-4D96-9411-153B7367508F}.Release|x86.ActiveCfg = Release|Any CPU 90 | {7A711FEA-526A-4D96-9411-153B7367508F}.Release|x86.Build.0 = Release|Any CPU 91 | {D67A928C-6EAC-40FA-BD5E-7962937F938A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 92 | {D67A928C-6EAC-40FA-BD5E-7962937F938A}.Debug|Any CPU.Build.0 = Debug|Any CPU 93 | {D67A928C-6EAC-40FA-BD5E-7962937F938A}.Debug|x64.ActiveCfg = Debug|Any CPU 94 | {D67A928C-6EAC-40FA-BD5E-7962937F938A}.Debug|x64.Build.0 = Debug|Any CPU 95 | {D67A928C-6EAC-40FA-BD5E-7962937F938A}.Debug|x86.ActiveCfg = Debug|Any CPU 96 | {D67A928C-6EAC-40FA-BD5E-7962937F938A}.Debug|x86.Build.0 = Debug|Any CPU 97 | {D67A928C-6EAC-40FA-BD5E-7962937F938A}.Release|Any CPU.ActiveCfg = Release|Any CPU 98 | {D67A928C-6EAC-40FA-BD5E-7962937F938A}.Release|Any CPU.Build.0 = Release|Any CPU 99 | {D67A928C-6EAC-40FA-BD5E-7962937F938A}.Release|x64.ActiveCfg = Release|Any CPU 100 | {D67A928C-6EAC-40FA-BD5E-7962937F938A}.Release|x64.Build.0 = Release|Any CPU 101 | {D67A928C-6EAC-40FA-BD5E-7962937F938A}.Release|x86.ActiveCfg = Release|Any CPU 102 | {D67A928C-6EAC-40FA-BD5E-7962937F938A}.Release|x86.Build.0 = Release|Any CPU 103 | {58011005-A288-48BB-9125-7C206668C681}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 104 | {58011005-A288-48BB-9125-7C206668C681}.Debug|Any CPU.Build.0 = Debug|Any CPU 105 | {58011005-A288-48BB-9125-7C206668C681}.Debug|x64.ActiveCfg = Debug|Any CPU 106 | {58011005-A288-48BB-9125-7C206668C681}.Debug|x64.Build.0 = Debug|Any CPU 107 | {58011005-A288-48BB-9125-7C206668C681}.Debug|x86.ActiveCfg = Debug|Any CPU 108 | {58011005-A288-48BB-9125-7C206668C681}.Debug|x86.Build.0 = Debug|Any CPU 109 | {58011005-A288-48BB-9125-7C206668C681}.Release|Any CPU.ActiveCfg = Release|Any CPU 110 | {58011005-A288-48BB-9125-7C206668C681}.Release|Any CPU.Build.0 = Release|Any CPU 111 | {58011005-A288-48BB-9125-7C206668C681}.Release|x64.ActiveCfg = Release|Any CPU 112 | {58011005-A288-48BB-9125-7C206668C681}.Release|x64.Build.0 = Release|Any CPU 113 | {58011005-A288-48BB-9125-7C206668C681}.Release|x86.ActiveCfg = Release|Any CPU 114 | {58011005-A288-48BB-9125-7C206668C681}.Release|x86.Build.0 = Release|Any CPU 115 | {2BBB4134-1732-4654-B4BC-7C00A9B3CA5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 116 | {2BBB4134-1732-4654-B4BC-7C00A9B3CA5A}.Debug|Any CPU.Build.0 = Debug|Any CPU 117 | {2BBB4134-1732-4654-B4BC-7C00A9B3CA5A}.Debug|x64.ActiveCfg = Debug|Any CPU 118 | {2BBB4134-1732-4654-B4BC-7C00A9B3CA5A}.Debug|x64.Build.0 = Debug|Any CPU 119 | {2BBB4134-1732-4654-B4BC-7C00A9B3CA5A}.Debug|x86.ActiveCfg = Debug|Any CPU 120 | {2BBB4134-1732-4654-B4BC-7C00A9B3CA5A}.Debug|x86.Build.0 = Debug|Any CPU 121 | {2BBB4134-1732-4654-B4BC-7C00A9B3CA5A}.Release|Any CPU.ActiveCfg = Release|Any CPU 122 | {2BBB4134-1732-4654-B4BC-7C00A9B3CA5A}.Release|Any CPU.Build.0 = Release|Any CPU 123 | {2BBB4134-1732-4654-B4BC-7C00A9B3CA5A}.Release|x64.ActiveCfg = Release|Any CPU 124 | {2BBB4134-1732-4654-B4BC-7C00A9B3CA5A}.Release|x64.Build.0 = Release|Any CPU 125 | {2BBB4134-1732-4654-B4BC-7C00A9B3CA5A}.Release|x86.ActiveCfg = Release|Any CPU 126 | {2BBB4134-1732-4654-B4BC-7C00A9B3CA5A}.Release|x86.Build.0 = Release|Any CPU 127 | {6E6084A9-4B09-49A8-8078-81562890F80A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 128 | {6E6084A9-4B09-49A8-8078-81562890F80A}.Debug|Any CPU.Build.0 = Debug|Any CPU 129 | {6E6084A9-4B09-49A8-8078-81562890F80A}.Debug|x64.ActiveCfg = Debug|Any CPU 130 | {6E6084A9-4B09-49A8-8078-81562890F80A}.Debug|x64.Build.0 = Debug|Any CPU 131 | {6E6084A9-4B09-49A8-8078-81562890F80A}.Debug|x86.ActiveCfg = Debug|Any CPU 132 | {6E6084A9-4B09-49A8-8078-81562890F80A}.Debug|x86.Build.0 = Debug|Any CPU 133 | {6E6084A9-4B09-49A8-8078-81562890F80A}.Release|Any CPU.ActiveCfg = Release|Any CPU 134 | {6E6084A9-4B09-49A8-8078-81562890F80A}.Release|Any CPU.Build.0 = Release|Any CPU 135 | {6E6084A9-4B09-49A8-8078-81562890F80A}.Release|x64.ActiveCfg = Release|Any CPU 136 | {6E6084A9-4B09-49A8-8078-81562890F80A}.Release|x64.Build.0 = Release|Any CPU 137 | {6E6084A9-4B09-49A8-8078-81562890F80A}.Release|x86.ActiveCfg = Release|Any CPU 138 | {6E6084A9-4B09-49A8-8078-81562890F80A}.Release|x86.Build.0 = Release|Any CPU 139 | {40B33EB5-4CE2-4B45-8581-4F9415E79F79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 140 | {40B33EB5-4CE2-4B45-8581-4F9415E79F79}.Debug|Any CPU.Build.0 = Debug|Any CPU 141 | {40B33EB5-4CE2-4B45-8581-4F9415E79F79}.Debug|x64.ActiveCfg = Debug|Any CPU 142 | {40B33EB5-4CE2-4B45-8581-4F9415E79F79}.Debug|x64.Build.0 = Debug|Any CPU 143 | {40B33EB5-4CE2-4B45-8581-4F9415E79F79}.Debug|x86.ActiveCfg = Debug|Any CPU 144 | {40B33EB5-4CE2-4B45-8581-4F9415E79F79}.Debug|x86.Build.0 = Debug|Any CPU 145 | {40B33EB5-4CE2-4B45-8581-4F9415E79F79}.Release|Any CPU.ActiveCfg = Release|Any CPU 146 | {40B33EB5-4CE2-4B45-8581-4F9415E79F79}.Release|Any CPU.Build.0 = Release|Any CPU 147 | {40B33EB5-4CE2-4B45-8581-4F9415E79F79}.Release|x64.ActiveCfg = Release|Any CPU 148 | {40B33EB5-4CE2-4B45-8581-4F9415E79F79}.Release|x64.Build.0 = Release|Any CPU 149 | {40B33EB5-4CE2-4B45-8581-4F9415E79F79}.Release|x86.ActiveCfg = Release|Any CPU 150 | {40B33EB5-4CE2-4B45-8581-4F9415E79F79}.Release|x86.Build.0 = Release|Any CPU 151 | {98CBDEE5-BCC2-4170-A556-525B3DBC017F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 152 | {98CBDEE5-BCC2-4170-A556-525B3DBC017F}.Debug|Any CPU.Build.0 = Debug|Any CPU 153 | {98CBDEE5-BCC2-4170-A556-525B3DBC017F}.Debug|x64.ActiveCfg = Debug|Any CPU 154 | {98CBDEE5-BCC2-4170-A556-525B3DBC017F}.Debug|x64.Build.0 = Debug|Any CPU 155 | {98CBDEE5-BCC2-4170-A556-525B3DBC017F}.Debug|x86.ActiveCfg = Debug|Any CPU 156 | {98CBDEE5-BCC2-4170-A556-525B3DBC017F}.Debug|x86.Build.0 = Debug|Any CPU 157 | {98CBDEE5-BCC2-4170-A556-525B3DBC017F}.Release|Any CPU.ActiveCfg = Release|Any CPU 158 | {98CBDEE5-BCC2-4170-A556-525B3DBC017F}.Release|Any CPU.Build.0 = Release|Any CPU 159 | {98CBDEE5-BCC2-4170-A556-525B3DBC017F}.Release|x64.ActiveCfg = Release|Any CPU 160 | {98CBDEE5-BCC2-4170-A556-525B3DBC017F}.Release|x64.Build.0 = Release|Any CPU 161 | {98CBDEE5-BCC2-4170-A556-525B3DBC017F}.Release|x86.ActiveCfg = Release|Any CPU 162 | {98CBDEE5-BCC2-4170-A556-525B3DBC017F}.Release|x86.Build.0 = Release|Any CPU 163 | {A736C257-61CA-4271-B7F8-422FFD6C0AD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 164 | {A736C257-61CA-4271-B7F8-422FFD6C0AD9}.Debug|Any CPU.Build.0 = Debug|Any CPU 165 | {A736C257-61CA-4271-B7F8-422FFD6C0AD9}.Debug|x64.ActiveCfg = Debug|Any CPU 166 | {A736C257-61CA-4271-B7F8-422FFD6C0AD9}.Debug|x64.Build.0 = Debug|Any CPU 167 | {A736C257-61CA-4271-B7F8-422FFD6C0AD9}.Debug|x86.ActiveCfg = Debug|Any CPU 168 | {A736C257-61CA-4271-B7F8-422FFD6C0AD9}.Debug|x86.Build.0 = Debug|Any CPU 169 | {A736C257-61CA-4271-B7F8-422FFD6C0AD9}.Release|Any CPU.ActiveCfg = Release|Any CPU 170 | {A736C257-61CA-4271-B7F8-422FFD6C0AD9}.Release|Any CPU.Build.0 = Release|Any CPU 171 | {A736C257-61CA-4271-B7F8-422FFD6C0AD9}.Release|x64.ActiveCfg = Release|Any CPU 172 | {A736C257-61CA-4271-B7F8-422FFD6C0AD9}.Release|x64.Build.0 = Release|Any CPU 173 | {A736C257-61CA-4271-B7F8-422FFD6C0AD9}.Release|x86.ActiveCfg = Release|Any CPU 174 | {A736C257-61CA-4271-B7F8-422FFD6C0AD9}.Release|x86.Build.0 = Release|Any CPU 175 | {4CF9FCA4-987E-483F-BF15-65F8286D6F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 176 | {4CF9FCA4-987E-483F-BF15-65F8286D6F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU 177 | {4CF9FCA4-987E-483F-BF15-65F8286D6F1F}.Debug|x64.ActiveCfg = Debug|Any CPU 178 | {4CF9FCA4-987E-483F-BF15-65F8286D6F1F}.Debug|x64.Build.0 = Debug|Any CPU 179 | {4CF9FCA4-987E-483F-BF15-65F8286D6F1F}.Debug|x86.ActiveCfg = Debug|Any CPU 180 | {4CF9FCA4-987E-483F-BF15-65F8286D6F1F}.Debug|x86.Build.0 = Debug|Any CPU 181 | {4CF9FCA4-987E-483F-BF15-65F8286D6F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU 182 | {4CF9FCA4-987E-483F-BF15-65F8286D6F1F}.Release|Any CPU.Build.0 = Release|Any CPU 183 | {4CF9FCA4-987E-483F-BF15-65F8286D6F1F}.Release|x64.ActiveCfg = Release|Any CPU 184 | {4CF9FCA4-987E-483F-BF15-65F8286D6F1F}.Release|x64.Build.0 = Release|Any CPU 185 | {4CF9FCA4-987E-483F-BF15-65F8286D6F1F}.Release|x86.ActiveCfg = Release|Any CPU 186 | {4CF9FCA4-987E-483F-BF15-65F8286D6F1F}.Release|x86.Build.0 = Release|Any CPU 187 | {A7F9A11F-AC66-41B3-99F3-CA8DB7408EA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 188 | {A7F9A11F-AC66-41B3-99F3-CA8DB7408EA8}.Debug|Any CPU.Build.0 = Debug|Any CPU 189 | {A7F9A11F-AC66-41B3-99F3-CA8DB7408EA8}.Debug|x64.ActiveCfg = Debug|Any CPU 190 | {A7F9A11F-AC66-41B3-99F3-CA8DB7408EA8}.Debug|x64.Build.0 = Debug|Any CPU 191 | {A7F9A11F-AC66-41B3-99F3-CA8DB7408EA8}.Debug|x86.ActiveCfg = Debug|Any CPU 192 | {A7F9A11F-AC66-41B3-99F3-CA8DB7408EA8}.Debug|x86.Build.0 = Debug|Any CPU 193 | {A7F9A11F-AC66-41B3-99F3-CA8DB7408EA8}.Release|Any CPU.ActiveCfg = Release|Any CPU 194 | {A7F9A11F-AC66-41B3-99F3-CA8DB7408EA8}.Release|Any CPU.Build.0 = Release|Any CPU 195 | {A7F9A11F-AC66-41B3-99F3-CA8DB7408EA8}.Release|x64.ActiveCfg = Release|Any CPU 196 | {A7F9A11F-AC66-41B3-99F3-CA8DB7408EA8}.Release|x64.Build.0 = Release|Any CPU 197 | {A7F9A11F-AC66-41B3-99F3-CA8DB7408EA8}.Release|x86.ActiveCfg = Release|Any CPU 198 | {A7F9A11F-AC66-41B3-99F3-CA8DB7408EA8}.Release|x86.Build.0 = Release|Any CPU 199 | {E49A6C50-271A-4D21-8523-D6554438F3E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 200 | {E49A6C50-271A-4D21-8523-D6554438F3E0}.Debug|Any CPU.Build.0 = Debug|Any CPU 201 | {E49A6C50-271A-4D21-8523-D6554438F3E0}.Debug|x64.ActiveCfg = Debug|Any CPU 202 | {E49A6C50-271A-4D21-8523-D6554438F3E0}.Debug|x64.Build.0 = Debug|Any CPU 203 | {E49A6C50-271A-4D21-8523-D6554438F3E0}.Debug|x86.ActiveCfg = Debug|Any CPU 204 | {E49A6C50-271A-4D21-8523-D6554438F3E0}.Debug|x86.Build.0 = Debug|Any CPU 205 | {E49A6C50-271A-4D21-8523-D6554438F3E0}.Release|Any CPU.ActiveCfg = Release|Any CPU 206 | {E49A6C50-271A-4D21-8523-D6554438F3E0}.Release|Any CPU.Build.0 = Release|Any CPU 207 | {E49A6C50-271A-4D21-8523-D6554438F3E0}.Release|x64.ActiveCfg = Release|Any CPU 208 | {E49A6C50-271A-4D21-8523-D6554438F3E0}.Release|x64.Build.0 = Release|Any CPU 209 | {E49A6C50-271A-4D21-8523-D6554438F3E0}.Release|x86.ActiveCfg = Release|Any CPU 210 | {E49A6C50-271A-4D21-8523-D6554438F3E0}.Release|x86.Build.0 = Release|Any CPU 211 | {E17E2548-287E-4E81-A839-D043F38CDEEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 212 | {E17E2548-287E-4E81-A839-D043F38CDEEB}.Debug|Any CPU.Build.0 = Debug|Any CPU 213 | {E17E2548-287E-4E81-A839-D043F38CDEEB}.Debug|x64.ActiveCfg = Debug|Any CPU 214 | {E17E2548-287E-4E81-A839-D043F38CDEEB}.Debug|x64.Build.0 = Debug|Any CPU 215 | {E17E2548-287E-4E81-A839-D043F38CDEEB}.Debug|x86.ActiveCfg = Debug|Any CPU 216 | {E17E2548-287E-4E81-A839-D043F38CDEEB}.Debug|x86.Build.0 = Debug|Any CPU 217 | {E17E2548-287E-4E81-A839-D043F38CDEEB}.Release|Any CPU.ActiveCfg = Release|Any CPU 218 | {E17E2548-287E-4E81-A839-D043F38CDEEB}.Release|Any CPU.Build.0 = Release|Any CPU 219 | {E17E2548-287E-4E81-A839-D043F38CDEEB}.Release|x64.ActiveCfg = Release|Any CPU 220 | {E17E2548-287E-4E81-A839-D043F38CDEEB}.Release|x64.Build.0 = Release|Any CPU 221 | {E17E2548-287E-4E81-A839-D043F38CDEEB}.Release|x86.ActiveCfg = Release|Any CPU 222 | {E17E2548-287E-4E81-A839-D043F38CDEEB}.Release|x86.Build.0 = Release|Any CPU 223 | {A9F0813C-F254-4AB1-8759-E47A3E3DE87C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 224 | {A9F0813C-F254-4AB1-8759-E47A3E3DE87C}.Debug|Any CPU.Build.0 = Debug|Any CPU 225 | {A9F0813C-F254-4AB1-8759-E47A3E3DE87C}.Debug|x64.ActiveCfg = Debug|Any CPU 226 | {A9F0813C-F254-4AB1-8759-E47A3E3DE87C}.Debug|x64.Build.0 = Debug|Any CPU 227 | {A9F0813C-F254-4AB1-8759-E47A3E3DE87C}.Debug|x86.ActiveCfg = Debug|Any CPU 228 | {A9F0813C-F254-4AB1-8759-E47A3E3DE87C}.Debug|x86.Build.0 = Debug|Any CPU 229 | {A9F0813C-F254-4AB1-8759-E47A3E3DE87C}.Release|Any CPU.ActiveCfg = Release|Any CPU 230 | {A9F0813C-F254-4AB1-8759-E47A3E3DE87C}.Release|Any CPU.Build.0 = Release|Any CPU 231 | {A9F0813C-F254-4AB1-8759-E47A3E3DE87C}.Release|x64.ActiveCfg = Release|Any CPU 232 | {A9F0813C-F254-4AB1-8759-E47A3E3DE87C}.Release|x64.Build.0 = Release|Any CPU 233 | {A9F0813C-F254-4AB1-8759-E47A3E3DE87C}.Release|x86.ActiveCfg = Release|Any CPU 234 | {A9F0813C-F254-4AB1-8759-E47A3E3DE87C}.Release|x86.Build.0 = Release|Any CPU 235 | {C079542D-0682-4D79-8443-CD167F6A27F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 236 | {C079542D-0682-4D79-8443-CD167F6A27F7}.Debug|Any CPU.Build.0 = Debug|Any CPU 237 | {C079542D-0682-4D79-8443-CD167F6A27F7}.Debug|x64.ActiveCfg = Debug|Any CPU 238 | {C079542D-0682-4D79-8443-CD167F6A27F7}.Debug|x64.Build.0 = Debug|Any CPU 239 | {C079542D-0682-4D79-8443-CD167F6A27F7}.Debug|x86.ActiveCfg = Debug|Any CPU 240 | {C079542D-0682-4D79-8443-CD167F6A27F7}.Debug|x86.Build.0 = Debug|Any CPU 241 | {C079542D-0682-4D79-8443-CD167F6A27F7}.Release|Any CPU.ActiveCfg = Release|Any CPU 242 | {C079542D-0682-4D79-8443-CD167F6A27F7}.Release|Any CPU.Build.0 = Release|Any CPU 243 | {C079542D-0682-4D79-8443-CD167F6A27F7}.Release|x64.ActiveCfg = Release|Any CPU 244 | {C079542D-0682-4D79-8443-CD167F6A27F7}.Release|x64.Build.0 = Release|Any CPU 245 | {C079542D-0682-4D79-8443-CD167F6A27F7}.Release|x86.ActiveCfg = Release|Any CPU 246 | {C079542D-0682-4D79-8443-CD167F6A27F7}.Release|x86.Build.0 = Release|Any CPU 247 | {39E0C179-AD30-4E9D-B774-4A4EA7880949}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 248 | {39E0C179-AD30-4E9D-B774-4A4EA7880949}.Debug|Any CPU.Build.0 = Debug|Any CPU 249 | {39E0C179-AD30-4E9D-B774-4A4EA7880949}.Debug|x64.ActiveCfg = Debug|Any CPU 250 | {39E0C179-AD30-4E9D-B774-4A4EA7880949}.Debug|x64.Build.0 = Debug|Any CPU 251 | {39E0C179-AD30-4E9D-B774-4A4EA7880949}.Debug|x86.ActiveCfg = Debug|Any CPU 252 | {39E0C179-AD30-4E9D-B774-4A4EA7880949}.Debug|x86.Build.0 = Debug|Any CPU 253 | {39E0C179-AD30-4E9D-B774-4A4EA7880949}.Release|Any CPU.ActiveCfg = Release|Any CPU 254 | {39E0C179-AD30-4E9D-B774-4A4EA7880949}.Release|Any CPU.Build.0 = Release|Any CPU 255 | {39E0C179-AD30-4E9D-B774-4A4EA7880949}.Release|x64.ActiveCfg = Release|Any CPU 256 | {39E0C179-AD30-4E9D-B774-4A4EA7880949}.Release|x64.Build.0 = Release|Any CPU 257 | {39E0C179-AD30-4E9D-B774-4A4EA7880949}.Release|x86.ActiveCfg = Release|Any CPU 258 | {39E0C179-AD30-4E9D-B774-4A4EA7880949}.Release|x86.Build.0 = Release|Any CPU 259 | {3A3E6817-728A-4F65-AF2C-711715E35458}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 260 | {3A3E6817-728A-4F65-AF2C-711715E35458}.Debug|Any CPU.Build.0 = Debug|Any CPU 261 | {3A3E6817-728A-4F65-AF2C-711715E35458}.Debug|x64.ActiveCfg = Debug|Any CPU 262 | {3A3E6817-728A-4F65-AF2C-711715E35458}.Debug|x64.Build.0 = Debug|Any CPU 263 | {3A3E6817-728A-4F65-AF2C-711715E35458}.Debug|x86.ActiveCfg = Debug|Any CPU 264 | {3A3E6817-728A-4F65-AF2C-711715E35458}.Debug|x86.Build.0 = Debug|Any CPU 265 | {3A3E6817-728A-4F65-AF2C-711715E35458}.Release|Any CPU.ActiveCfg = Release|Any CPU 266 | {3A3E6817-728A-4F65-AF2C-711715E35458}.Release|Any CPU.Build.0 = Release|Any CPU 267 | {3A3E6817-728A-4F65-AF2C-711715E35458}.Release|x64.ActiveCfg = Release|Any CPU 268 | {3A3E6817-728A-4F65-AF2C-711715E35458}.Release|x64.Build.0 = Release|Any CPU 269 | {3A3E6817-728A-4F65-AF2C-711715E35458}.Release|x86.ActiveCfg = Release|Any CPU 270 | {3A3E6817-728A-4F65-AF2C-711715E35458}.Release|x86.Build.0 = Release|Any CPU 271 | {E70DF3C6-7548-48EA-B07F-C421D251EAF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 272 | {E70DF3C6-7548-48EA-B07F-C421D251EAF4}.Debug|Any CPU.Build.0 = Debug|Any CPU 273 | {E70DF3C6-7548-48EA-B07F-C421D251EAF4}.Debug|x64.ActiveCfg = Debug|Any CPU 274 | {E70DF3C6-7548-48EA-B07F-C421D251EAF4}.Debug|x64.Build.0 = Debug|Any CPU 275 | {E70DF3C6-7548-48EA-B07F-C421D251EAF4}.Debug|x86.ActiveCfg = Debug|Any CPU 276 | {E70DF3C6-7548-48EA-B07F-C421D251EAF4}.Debug|x86.Build.0 = Debug|Any CPU 277 | {E70DF3C6-7548-48EA-B07F-C421D251EAF4}.Release|Any CPU.ActiveCfg = Release|Any CPU 278 | {E70DF3C6-7548-48EA-B07F-C421D251EAF4}.Release|Any CPU.Build.0 = Release|Any CPU 279 | {E70DF3C6-7548-48EA-B07F-C421D251EAF4}.Release|x64.ActiveCfg = Release|Any CPU 280 | {E70DF3C6-7548-48EA-B07F-C421D251EAF4}.Release|x64.Build.0 = Release|Any CPU 281 | {E70DF3C6-7548-48EA-B07F-C421D251EAF4}.Release|x86.ActiveCfg = Release|Any CPU 282 | {E70DF3C6-7548-48EA-B07F-C421D251EAF4}.Release|x86.Build.0 = Release|Any CPU 283 | {D9770ACB-BA41-462E-BA1C-FC5F2C3C1BAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 284 | {D9770ACB-BA41-462E-BA1C-FC5F2C3C1BAC}.Debug|Any CPU.Build.0 = Debug|Any CPU 285 | {D9770ACB-BA41-462E-BA1C-FC5F2C3C1BAC}.Debug|x64.ActiveCfg = Debug|Any CPU 286 | {D9770ACB-BA41-462E-BA1C-FC5F2C3C1BAC}.Debug|x64.Build.0 = Debug|Any CPU 287 | {D9770ACB-BA41-462E-BA1C-FC5F2C3C1BAC}.Debug|x86.ActiveCfg = Debug|Any CPU 288 | {D9770ACB-BA41-462E-BA1C-FC5F2C3C1BAC}.Debug|x86.Build.0 = Debug|Any CPU 289 | {D9770ACB-BA41-462E-BA1C-FC5F2C3C1BAC}.Release|Any CPU.ActiveCfg = Release|Any CPU 290 | {D9770ACB-BA41-462E-BA1C-FC5F2C3C1BAC}.Release|Any CPU.Build.0 = Release|Any CPU 291 | {D9770ACB-BA41-462E-BA1C-FC5F2C3C1BAC}.Release|x64.ActiveCfg = Release|Any CPU 292 | {D9770ACB-BA41-462E-BA1C-FC5F2C3C1BAC}.Release|x64.Build.0 = Release|Any CPU 293 | {D9770ACB-BA41-462E-BA1C-FC5F2C3C1BAC}.Release|x86.ActiveCfg = Release|Any CPU 294 | {D9770ACB-BA41-462E-BA1C-FC5F2C3C1BAC}.Release|x86.Build.0 = Release|Any CPU 295 | {12CEDEEA-ADC1-4247-8B78-BA64FAE559AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 296 | {12CEDEEA-ADC1-4247-8B78-BA64FAE559AA}.Debug|Any CPU.Build.0 = Debug|Any CPU 297 | {12CEDEEA-ADC1-4247-8B78-BA64FAE559AA}.Debug|x64.ActiveCfg = Debug|Any CPU 298 | {12CEDEEA-ADC1-4247-8B78-BA64FAE559AA}.Debug|x64.Build.0 = Debug|Any CPU 299 | {12CEDEEA-ADC1-4247-8B78-BA64FAE559AA}.Debug|x86.ActiveCfg = Debug|Any CPU 300 | {12CEDEEA-ADC1-4247-8B78-BA64FAE559AA}.Debug|x86.Build.0 = Debug|Any CPU 301 | {12CEDEEA-ADC1-4247-8B78-BA64FAE559AA}.Release|Any CPU.ActiveCfg = Release|Any CPU 302 | {12CEDEEA-ADC1-4247-8B78-BA64FAE559AA}.Release|Any CPU.Build.0 = Release|Any CPU 303 | {12CEDEEA-ADC1-4247-8B78-BA64FAE559AA}.Release|x64.ActiveCfg = Release|Any CPU 304 | {12CEDEEA-ADC1-4247-8B78-BA64FAE559AA}.Release|x64.Build.0 = Release|Any CPU 305 | {12CEDEEA-ADC1-4247-8B78-BA64FAE559AA}.Release|x86.ActiveCfg = Release|Any CPU 306 | {12CEDEEA-ADC1-4247-8B78-BA64FAE559AA}.Release|x86.Build.0 = Release|Any CPU 307 | {707DF674-6E7C-4EE4-8BF7-E8E806C5A704}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 308 | {707DF674-6E7C-4EE4-8BF7-E8E806C5A704}.Debug|Any CPU.Build.0 = Debug|Any CPU 309 | {707DF674-6E7C-4EE4-8BF7-E8E806C5A704}.Debug|x64.ActiveCfg = Debug|Any CPU 310 | {707DF674-6E7C-4EE4-8BF7-E8E806C5A704}.Debug|x64.Build.0 = Debug|Any CPU 311 | {707DF674-6E7C-4EE4-8BF7-E8E806C5A704}.Debug|x86.ActiveCfg = Debug|Any CPU 312 | {707DF674-6E7C-4EE4-8BF7-E8E806C5A704}.Debug|x86.Build.0 = Debug|Any CPU 313 | {707DF674-6E7C-4EE4-8BF7-E8E806C5A704}.Release|Any CPU.ActiveCfg = Release|Any CPU 314 | {707DF674-6E7C-4EE4-8BF7-E8E806C5A704}.Release|Any CPU.Build.0 = Release|Any CPU 315 | {707DF674-6E7C-4EE4-8BF7-E8E806C5A704}.Release|x64.ActiveCfg = Release|Any CPU 316 | {707DF674-6E7C-4EE4-8BF7-E8E806C5A704}.Release|x64.Build.0 = Release|Any CPU 317 | {707DF674-6E7C-4EE4-8BF7-E8E806C5A704}.Release|x86.ActiveCfg = Release|Any CPU 318 | {707DF674-6E7C-4EE4-8BF7-E8E806C5A704}.Release|x86.Build.0 = Release|Any CPU 319 | {99E7FED9-E1D3-4F8D-895B-A31258F6BCB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 320 | {99E7FED9-E1D3-4F8D-895B-A31258F6BCB2}.Debug|Any CPU.Build.0 = Debug|Any CPU 321 | {99E7FED9-E1D3-4F8D-895B-A31258F6BCB2}.Debug|x64.ActiveCfg = Debug|Any CPU 322 | {99E7FED9-E1D3-4F8D-895B-A31258F6BCB2}.Debug|x64.Build.0 = Debug|Any CPU 323 | {99E7FED9-E1D3-4F8D-895B-A31258F6BCB2}.Debug|x86.ActiveCfg = Debug|Any CPU 324 | {99E7FED9-E1D3-4F8D-895B-A31258F6BCB2}.Debug|x86.Build.0 = Debug|Any CPU 325 | {99E7FED9-E1D3-4F8D-895B-A31258F6BCB2}.Release|Any CPU.ActiveCfg = Release|Any CPU 326 | {99E7FED9-E1D3-4F8D-895B-A31258F6BCB2}.Release|Any CPU.Build.0 = Release|Any CPU 327 | {99E7FED9-E1D3-4F8D-895B-A31258F6BCB2}.Release|x64.ActiveCfg = Release|Any CPU 328 | {99E7FED9-E1D3-4F8D-895B-A31258F6BCB2}.Release|x64.Build.0 = Release|Any CPU 329 | {99E7FED9-E1D3-4F8D-895B-A31258F6BCB2}.Release|x86.ActiveCfg = Release|Any CPU 330 | {99E7FED9-E1D3-4F8D-895B-A31258F6BCB2}.Release|x86.Build.0 = Release|Any CPU 331 | {E63E0F5B-5DCF-4CDA-AFBB-DB99F20E0C99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 332 | {E63E0F5B-5DCF-4CDA-AFBB-DB99F20E0C99}.Debug|Any CPU.Build.0 = Debug|Any CPU 333 | {E63E0F5B-5DCF-4CDA-AFBB-DB99F20E0C99}.Debug|x64.ActiveCfg = Debug|Any CPU 334 | {E63E0F5B-5DCF-4CDA-AFBB-DB99F20E0C99}.Debug|x64.Build.0 = Debug|Any CPU 335 | {E63E0F5B-5DCF-4CDA-AFBB-DB99F20E0C99}.Debug|x86.ActiveCfg = Debug|Any CPU 336 | {E63E0F5B-5DCF-4CDA-AFBB-DB99F20E0C99}.Debug|x86.Build.0 = Debug|Any CPU 337 | {E63E0F5B-5DCF-4CDA-AFBB-DB99F20E0C99}.Release|Any CPU.ActiveCfg = Release|Any CPU 338 | {E63E0F5B-5DCF-4CDA-AFBB-DB99F20E0C99}.Release|Any CPU.Build.0 = Release|Any CPU 339 | {E63E0F5B-5DCF-4CDA-AFBB-DB99F20E0C99}.Release|x64.ActiveCfg = Release|Any CPU 340 | {E63E0F5B-5DCF-4CDA-AFBB-DB99F20E0C99}.Release|x64.Build.0 = Release|Any CPU 341 | {E63E0F5B-5DCF-4CDA-AFBB-DB99F20E0C99}.Release|x86.ActiveCfg = Release|Any CPU 342 | {E63E0F5B-5DCF-4CDA-AFBB-DB99F20E0C99}.Release|x86.Build.0 = Release|Any CPU 343 | EndGlobalSection 344 | GlobalSection(NestedProjects) = preSolution 345 | {7A711FEA-526A-4D96-9411-153B7367508F} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 346 | {D67A928C-6EAC-40FA-BD5E-7962937F938A} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 347 | {58011005-A288-48BB-9125-7C206668C681} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 348 | {2BBB4134-1732-4654-B4BC-7C00A9B3CA5A} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 349 | {6E6084A9-4B09-49A8-8078-81562890F80A} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 350 | {40B33EB5-4CE2-4B45-8581-4F9415E79F79} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 351 | {98CBDEE5-BCC2-4170-A556-525B3DBC017F} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 352 | {A736C257-61CA-4271-B7F8-422FFD6C0AD9} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 353 | {4CF9FCA4-987E-483F-BF15-65F8286D6F1F} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 354 | {A7F9A11F-AC66-41B3-99F3-CA8DB7408EA8} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 355 | {E49A6C50-271A-4D21-8523-D6554438F3E0} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 356 | {E17E2548-287E-4E81-A839-D043F38CDEEB} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 357 | {A9F0813C-F254-4AB1-8759-E47A3E3DE87C} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 358 | {C079542D-0682-4D79-8443-CD167F6A27F7} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 359 | {39E0C179-AD30-4E9D-B774-4A4EA7880949} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 360 | {3A3E6817-728A-4F65-AF2C-711715E35458} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 361 | {E70DF3C6-7548-48EA-B07F-C421D251EAF4} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 362 | {D9770ACB-BA41-462E-BA1C-FC5F2C3C1BAC} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 363 | {12CEDEEA-ADC1-4247-8B78-BA64FAE559AA} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 364 | {707DF674-6E7C-4EE4-8BF7-E8E806C5A704} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 365 | {99E7FED9-E1D3-4F8D-895B-A31258F6BCB2} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 366 | {E63E0F5B-5DCF-4CDA-AFBB-DB99F20E0C99} = {A4B3BA63-F524-47C8-9DF8-1DB344F372D3} 367 | EndGlobalSection 368 | EndGlobal 369 | --------------------------------------------------------------------------------