diff --git a/Examples/Examples.sln b/Examples/Examples.sln index 0fd3d0a..1e9ca21 100644 --- a/Examples/Examples.sln +++ b/Examples/Examples.sln @@ -11,6 +11,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RealExample", "RealExample" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Payment", "..\Payment\Payment.csproj", "{E7F0B3A7-6105-43CB-B4E1-3733111D4EB8}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReportingSystem", "ReportingSystem", "{7F5E5CAF-808E-4B01-9020-FFFB3E91E830}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reporting", "Reporting\Reporting.csproj", "{E3DE53A2-4252-4A90-91B8-2F4881F0169A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -29,12 +33,17 @@ Global {E7F0B3A7-6105-43CB-B4E1-3733111D4EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU {E7F0B3A7-6105-43CB-B4E1-3733111D4EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU {E7F0B3A7-6105-43CB-B4E1-3733111D4EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {E3DE53A2-4252-4A90-91B8-2F4881F0169A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E3DE53A2-4252-4A90-91B8-2F4881F0169A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E3DE53A2-4252-4A90-91B8-2F4881F0169A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E3DE53A2-4252-4A90-91B8-2F4881F0169A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {E7F0B3A7-6105-43CB-B4E1-3733111D4EB8} = {BEFC8EDE-A568-4AC3-B3EA-B68427103370} + {E3DE53A2-4252-4A90-91B8-2F4881F0169A} = {7F5E5CAF-808E-4B01-9020-FFFB3E91E830} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {22B9C537-2B7B-4D6D-B152-E2C4BA31F193} diff --git a/Examples/Reporting/Program.cs b/Examples/Reporting/Program.cs new file mode 100644 index 0000000..8307ac5 --- /dev/null +++ b/Examples/Reporting/Program.cs @@ -0,0 +1,166 @@ +using Microsoft.Extensions.DependencyInjection; +using System.ComponentModel; +using System.Reflection; + +public class Program +{ + public static void Main() + { + // DI + var serviceProvider = ConfigureServices(); + var connectors = serviceProvider.GetRequiredService(); + + Console.Write("Enter Title: "); + var title = Console.ReadLine(); + + Console.Write("Enter Description: "); + var description = Console.ReadLine(); + + Console.Write("Enter Severity(Dangerous, Medium, LowImportance): "); + var severityInput = Console.ReadLine(); + var severity = Enum.TryParse(severityInput, out Severity parsedSeverity) ? parsedSeverity : Severity.LowImportance; + + var report = new BugReport + { + Title = title, + Description = description, + Severity = severity + }; + + report.Validate(); + + Console.Write("Enter Connector (Discord, Jira, Slack): "); + var connectorType = Console.ReadLine(); + + connectors.Create(connectorType); + } + + // Config DI + private static ServiceProvider ConfigureServices() + { + return new ServiceCollection() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .BuildServiceProvider(); + } +} + +public class Connectors +{ + private readonly IEnumerable _factories; + + public Connectors(IEnumerable factories) + { + _factories = factories; + } + + public IConnectors Create(string connectorType) + { + var factory = _factories.FirstOrDefault(f => f.GetType().Name.ToLower().Contains(connectorType)); + return factory?.CreateInstance(); + } +} + +public class BugReport +{ + public string Title { get; init; } = default!; + public string Description { get; init; } = default!; + public Severity Severity { get; init; } + public DateTime CreationDate { get;} = DateTime.UtcNow; + + public void Validate() + { + if (string.IsNullOrWhiteSpace(Title)) + throw new ArgumentException("Title cannot be null, empty, or whitespace.", nameof(Title)); + + if (string.IsNullOrWhiteSpace(Description)) + throw new ArgumentException("Description cannot be null, empty, or whitespace.", nameof(Description)); + } + + public string Format() + { + var severityLabel = Severity.GetDescription(); + return $"[{severityLabel}] {Title}: {Description} (Created On: {CreationDate:yyyy-MM-dd HH:mm:ss})"; + } +} + +public enum Severity +{ + [Description("High")] + Dangerous = 0, + [Description("Medium")] + Medium = 1, + [Description("Low")] + LowImportance = 2 +} + +public static class EnumExtensions +{ + public static string GetDescription(this Enum value) + { + var field = value.GetType().GetField(value.ToString()); + var attribute = field!.GetCustomAttribute(); + return attribute == null ? value.ToString() : attribute.Description; + } +} + +public interface IConnectors +{ + void SendBugReport(BugReport report); +} + +public class Jira : IConnectors +{ + public void SendBugReport(BugReport report) + { + Console.WriteLine($"[Jira] Bug Report Sent: {report.Format()}"); + } +} + +public class Slack : IConnectors +{ + public void SendBugReport(BugReport report) + { + Console.WriteLine($"[Slack] Bug Report Sent: {report.Format()}"); + } +} + +public class Discord : IConnectors +{ + public void SendBugReport(BugReport report) + { + Console.WriteLine($"[Discord] Bug Report Sent: {report.Format()}"); + } +} + +public interface IConnectorsFactory +{ + IConnectors CreateInstance(); +} + +public class JiraFactory : IConnectorsFactory +{ + public IConnectors CreateInstance() + { + return new Jira(); + } +} + +public class SlackFactory : IConnectorsFactory +{ + public IConnectors CreateInstance() + { + return new Slack(); + } +} + +public class DiscordFactory : IConnectorsFactory +{ + public IConnectors CreateInstance() + { + return new Discord(); + } +} + diff --git a/Examples/Reporting/Reporting.csproj b/Examples/Reporting/Reporting.csproj new file mode 100644 index 0000000..6b07759 --- /dev/null +++ b/Examples/Reporting/Reporting.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + diff --git a/Payment/Payment.csproj b/Payment/Payment.csproj index 2150e37..e491120 100644 --- a/Payment/Payment.csproj +++ b/Payment/Payment.csproj @@ -7,4 +7,9 @@ enable + + + + + diff --git a/Payment/Program.cs b/Payment/Program.cs index 3751555..6d56e77 100644 --- a/Payment/Program.cs +++ b/Payment/Program.cs @@ -1,2 +1,241 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); +using Microsoft.Extensions.DependencyInjection; + +namespace Payment; + +public class Program +{ + public static void Main() + { + // DI + var serviceProvider = ConfigureServices(); + var paymentService = serviceProvider.GetRequiredService(); + + Console.Write("Enter payment type (CreditCard/CryptoCurrency): "); + var input = Console.ReadLine(); + + if (!Enum.TryParse(input, true, out PaymentType paymentType)) + { + paymentType = PaymentType.Unsupported; + } + + Console.Write("Enter payment amount: "); + if (!decimal.TryParse(Console.ReadLine(), out var price)) + { + Console.WriteLine("Invalid price. Please enter a valid number."); + return; + } + + var payment = paymentService.Create(paymentType); + var response = payment.ProcessPayment(price, input); + + Console.WriteLine(response.Message); + } + + // Config DI + private static ServiceProvider ConfigureServices() + { + return new ServiceCollection() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient() + .BuildServiceProvider(); + } +} + +public class PaymentService(IEnumerable factories) +{ + public IPaymentService Create(PaymentType type) + { + return factories + .FirstOrDefault(f => f.PaymentType == type)?.CreateInstance() + ?? new UnsupportedPayment(); + } +} + +public interface IPaymentService +{ + Payment ProcessPayment(decimal price, string type); +} + +public sealed class Payment +{ + public decimal? Price { get; init; } + public string? Message { get; init; } +} + +public enum PaymentType +{ + CreditCard, + CryptoCurrency, + Unsupported +} + + +public class CreditCard : IPaymentService +{ + private readonly IPaymentCalculator _paymentCalculator = new CreditCardCalculator(); + + public Payment ProcessPayment(decimal price, string type) + { + var finalAmount = _paymentCalculator.CalculatePayment(price); + + var response = new Payment + { + Message = PaymentMessageGenerator.GeneratePaymentMessage($"{type}", price, finalAmount), + Price = finalAmount + }; + return response; + } +} + +public class CryptoCurrencyAdapter : IPaymentService +{ + private readonly CryptoCurrency _cryptoCurrency = new(); + private readonly CryptoCurrencyCalculator _paymentCalculator = new(); + + public Payment ProcessPayment(decimal price, string type) + { + var adjustedPrice = _paymentCalculator.CalculatePayment(price); + var finalCryptoAmount = _cryptoCurrency.PayByCrypto(adjustedPrice); + + var response = new Payment + { + Message = PaymentMessageGenerator.GeneratePaymentMessage( + $"{type}", + adjustedPrice, + finalCryptoAmount), + Price = finalCryptoAmount + }; + return response; + } +} + + +public interface IPaymentCalculator +{ + decimal CalculatePayment(decimal price); +} + +public class CreditCardCalculator : IPaymentCalculator +{ + public decimal CalculatePayment(decimal price) + { + return price + price * (decimal)0.02; + } +} + +public class CryptoCurrencyCalculator : IPaymentCalculator +{ + private const decimal TaxRate = 0.05m; + private const decimal DiscountRate = 0.1m; + private const decimal TransactionFeeRate = 0.02m; + + public decimal CalculatePayment(decimal price) + { + var discountedPrice = price - (price * DiscountRate); + + var taxedPrice = discountedPrice + (discountedPrice * TaxRate); + var finalPrice = taxedPrice - (taxedPrice * TransactionFeeRate); + + return finalPrice; + } +} + + +public class UnsupportedPayment : IPaymentService +{ + private readonly ILogger _logger = new ConsoleLogger(); + + public Payment ProcessPayment(decimal price,string type) + { + _logger.Log($"Unsupported payment type {type}. Price: {price}. Timestamp: {DateTime.UtcNow}"); + + return new Payment + { + Message = $"Unsupported {type} payment. Unable to process {price} at this time." + }; + } +} + +public interface ILogger +{ + void Log(string message); +} + +public class ConsoleLogger : ILogger +{ + public void Log(string message) + { + Console.WriteLine($"[LOG]: {message}"); + } +} + + +public interface IPaymentServiceFactory +{ + PaymentType PaymentType { get; } + IPaymentService CreateInstance(); +} + +public class CreditCardFactory : IPaymentServiceFactory +{ + public PaymentType PaymentType => PaymentType.CreditCard; + + public IPaymentService CreateInstance() + { + return new CreditCard(); + } +} + +public class CryptoCurrencyFactory : IPaymentServiceFactory +{ + public PaymentType PaymentType => PaymentType.CryptoCurrency; + + public IPaymentService CreateInstance() + { + return new CryptoCurrencyAdapter(); + } +} + +public static class PaymentMessageGenerator +{ + public static string GeneratePaymentMessage(string paymentType, decimal originalPrice, decimal adjustedPrice) + { + return adjustedPrice == originalPrice + ? $"Payment via {paymentType}: Amount {originalPrice}." + : $"Payment via {paymentType}: Original amount {originalPrice}, Final amount after adjustment {adjustedPrice}."; + } +} + + +// External library class (inaccessible for modification) + +public sealed class CryptoCurrency +{ + private const decimal ConversionRate = 0.0005m; + private const decimal TransactionFee = 0.02m; + + public decimal PayByCrypto(decimal price) + { + var cryptoAmount = ConvertToCrypto(price); + var totalAmount = ApplyTransactionFee(cryptoAmount); + + Console.WriteLine($"Processing cryptocurrency payment of {totalAmount} units."); + + return totalAmount; + } + + private decimal ConvertToCrypto(decimal price) + { + return price * ConversionRate; + } + + private decimal ApplyTransactionFee(decimal cryptoAmount) + { + return cryptoAmount - (cryptoAmount * TransactionFee); + } +} + +