Real-Time C# Factory Design Pattern Example: Integration of Payment Gateway :
using System;
namespace FactoryDesignPattern
{
//Define the Product Interface
public interface IPaymentGateway
{
void ProcessPayment(decimal amount);
}
//Concrete Implementations for the Products
public class PayPalGateway : IPaymentGateway
{
public void ProcessPayment(decimal amount)
{
Console.WriteLine($"Processing ${amount} payment through PayPal...");
// Actual integration and logic for PayPal
}
}
public class StripeGateway : IPaymentGateway
{
public void ProcessPayment(decimal amount)
{
Console.WriteLine($"Processing ${amount} payment through Stripe...");
// Actual integration and logic for Stripe
}
}
//Factory Class to Produce the Products
switch (gatewayName.ToLower())
{
case "paypal":
return new PayPalGateway();
case "stripe":
return new StripeGateway();
case "creditcard":
return new CreditCardGateway();
default:
throw new ArgumentException("Invalid payment gateway specified");
}
}
}
// Testing the Factory Design Pattern
public class Program
{
public static void Main()
{
Console.WriteLine("Select the payment gateway (PayPal, Stripe, CreditCard): ");
string gatewayName = Console.ReadLine();
catch (ArgumentException ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
}
}
With this design, integrating additional payment options into the online shopping platform becomes straightforward. For instance, suppose a novel payment solution such as Bitcoin emerges in the future. In that case, you have the flexibility to adjust the PaymentGatewayFactory and introduce a new BitcoinGateway class without any alterations to the existing client code. The Factory Design Pattern guarantees scalability and ease of maintenance in these scenarios through the encapsulation of the creation logic. Upon executing the provided code, the following result will be displayed.
Output:
The <style> element is displayed in the code snippet below.
.placeholder-diagram { background: linear-gradient(135deg, #374151 0%, #1f2937 100%); border-radius: 12px; padding: 40px; margin: 20px 0; text-align: center; }
.placeholder-diagram .placeholder-icon { font-size: 3rem; margin-bottom: 10px; }
.placeholder-diagram .placeholder-text { color: #9ca3af; font-size: 1rem; }
Real-Time Example of Factory Design Pattern in C#: Document Conversion System
Imagine a software application that allows users to transform files into various formats like TXT, PDF, and DOCX. The tool will utilize the designated converter as per the user's selection.
using System;
namespace FactoryDesignPattern
{
//Define the Product Interface
public interface IDocumentConverter
{
string Convert(string content);
string TargetExtension { get; }
}
//Concrete Implementations for the Products
public class DocxConverter : IDocumentConverter
{
public string Convert(string content)
{
Console.WriteLine("Converting content to DOCX format...");
// Logic for DOCX conversion, simplified for this example
return content + " [Converted to DOCX]";
}
public string TargetExtension => ".docx";
}
public class PdfConverter : IDocumentConverter
{
public string Convert(string content)
{
Console.WriteLine("Converting content to PDF format...");
// Logic for PDF conversion, simplified for this example
return content + " [Converted to PDF]";
}
public string TargetExtension => ".pdf";
}
// Testing the Factory Design Pattern
public class Program
{
public static void Main()
{
Console.WriteLine("Enter the content to convert:");
string content = Console.ReadLine();
Console.WriteLine("Select the target format (DOCX, PDF, TXT):");
string format = Console.ReadLine();
try
{
IDocumentConverter converter = DocumentConverterFactory.CreateDocumentConverter(format);
string convertedContent = converter.Convert(content);
Console.WriteLine($"Converted content ({converter.TargetExtension}): {convertedContent}");
}
catch (ArgumentException ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
}
}
Thanks to its architecture, the system is capable of seamlessly accommodating additional document formats. The core functionality would stay consistent even with the introduction of a new format such as RTF. In such a scenario, creating a new class like RtfConverter and updating the DocumentConverterFactory would be sufficient. The Factory Design Pattern simplifies the process of extending or altering features by promoting modularity and the segregation of responsibilities. Upon executing the provided code, you will observe the following output.
Output:
The <style> section consists of a CSS code snippet that defines the styling for a placeholder diagram. This code includes specifications for the background color, border radius, padding, margin, and text alignment of the placeholder diagram. Additionally, it sets the font size and margin for the placeholder icon within the diagram, as well as the color and font size for the placeholder text. The complete styling is encapsulated within a class named "placeholder-diagram" for easy application and management in web development projects.
Real-Time Example of Factory Design Pattern in C#: Logging System
Examining a real-time logging system example, many software programs need to record messages to various destinations like files, consoles, or remote servers. Employing the Factory Design Pattern allows for easy switching between different logging strategies and effectively isolates the client code from specific logger implementations.
using System;
namespace FactoryDesignPattern
{
//Define the Product Interface
public interface ILogger
{
void Log(string message);
}
//Concrete Implementations for the Products
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine("Console: " + message);
}
}
public class FileLogger : ILogger
{
private string _filePath;
public FileLogger(string filePath)
{
_filePath = filePath;
}
public void Log(string message)
{
File.AppendAllText(_filePath, "File: " + message + Environment.NewLine);
}
}
// You can also add other loggers like DatabaseLogger, CloudLogger, etc.
//Factory Class to Produce the Products
public static class LoggerFactory
{
public static ILogger CreateLogger(string type)
{
switch (type.ToLower())
{
case "console":
return new ConsoleLogger();
case "file":
return new FileLogger("log.txt");
// For simplicity, file path is hardcoded
// You can extend here with other logger types
default:
throw new ArgumentException("Invalid logger type");
}
}
}
// Testing the Factory Design Pattern
public class Program
{
public static void Main()
{
ILogger logger;
logger = LoggerFactory.CreateLogger("console");
logger.Log("This is a console log!");
logger = LoggerFactory.CreateLogger("file");
logger.Log("This message is written to a file.");
// The beauty of this approach is that the client code remains unchanged
// even if we introduce new logger types in the future.
Console.ReadKey();
}
}
}
Here, we've shown a method to easily change logging methods without altering the client code by employing the Factory Design Pattern. Suppose a new need arises later, such as logging to a database. In that case, the existing client code and logger implementations can remain untouched while we enhance our LoggerFactory to support the new logging mechanism.
Real-Time Example of Factory Design Pattern in C#: A Simple System to Handle Notifications
Let's explore a different real-time situation: a fundamental notification system. In this case, an application might need to inform users through push notifications, text messages, or email alerts, along with other methods.
using System;
namespace FactoryDesignPattern
{
//Define the Product Interface
public interface INotificationSender
{
void SendNotification(string message);
}
//Concrete Implementations for the Products
public class EmailNotification : INotificationSender
{
public void SendNotification(string message)
{
Console.WriteLine($"Sending EMAIL notification: {message}");
// Logic to send email here...
}
}
public class PushNotification : INotificationSender
{
public void SendNotification(string message)
{
Console.WriteLine($"Sending PUSH notification: {message}");
// Logic to send push notification here...
}
}
//Factory Class to Produce the Products
public static class NotificationFactory
{
public static INotificationSender CreateNotificationSender(string type)
{
switch (type.ToLower())
{
case "email":
return new EmailNotification();
case "sms":
return new SMSNotification();
case "push":
return new PushNotification();
default:
throw new ArgumentException("Invalid notification type");
}
}
}
// Testing the Factory Design Pattern
public class Program
{
public static void Main()
{
INotificationSender notificationSender;
notificationSender = NotificationFactory.CreateNotificationSender("email");
notificationSender.SendNotification("This is an email notification!");
notificationSender = NotificationFactory.CreateNotificationSender("sms");
notificationSender.SendNotification("This is an SMS notification!");
notificationSender = NotificationFactory.CreateNotificationSender("push");
notificationSender.SendNotification("This is a push notification!");
// As with other factory examples, adding new notification methods
// would only require extending the factory, without altering the client code.
Console.ReadKey();
}
}
}
The Factory Design Pattern enables us to encapsulate the details of creating multiple notification senders in this live scenario. This abstraction allows us to scale our factory easily in the future to accommodate additional notification types, ensuring that the client code remains unchanged and coherent. Running the aforementioned code will display the following result.
Output:
Styling for a diagram placeholder is defined by the following CSS properties:
- The background is set as a linear gradient from #374151 to #1f2937 with a border radius of 12 pixels.
- It has a padding of 40 pixels and a margin of 20 pixels on the top and bottom.
- The text alignment is centered within the placeholder.
- The icon inside the placeholder has a font size of 3rem and a margin bottom of 10 pixels.
- The text content inside the placeholder is styled with a color of #9ca3af and a font size of 1rem.
Real-Time Example of Factory Design Pattern in C#: Discounts in an E-commerce Application
Let's explore another real-world scenario showcasing the Factory Design Pattern in action. Picture an e-commerce platform where a diverse range of discounts are implemented on products, depending entirely on various situations.
using System;
namespace FactoryDesignPattern
{
//Define the Product Interface
public interface IDiscountStrategy
{
decimal ApplyDiscount(decimal price);
}
//Concrete Implementations for the Products
public class SeasonalDiscount: IDiscountStrategy
{
public decimal ApplyDiscount(decimal price)
{
return price * 0.90m; // 10% seasonal discount
}
}
public class ClearanceDiscount : IDiscountStrategy
{
public decimal ApplyDiscount(decimal price)
{
return price * 0.70m; // 30% clearance discount
}
}
public class MemberDiscount : IDiscountStrategy
{
public decimal ApplyDiscount(decimal price)
{
return price * 0.95m; // 5% member discount
}
}
//Factory Class to Produce the Products
public static class DiscountFactory
{
public static IDiscountStrategy CreateDiscountStrategy(string type)
{
switch (type.ToLower())
{
case "seasonal":
return new SeasonalDiscount();
case "clearance":
return new ClearanceDiscount();
case "member":
return new MemberDiscount();
default:
throw new ArgumentException("Invalid discount type");
}
}
}
// Testing the Factory Design Pattern
public class Program
{
public static void Main()
{
IDiscountStrategy discountStrategy;
decimal originalPrice = 100.0m;
discountStrategy = DiscountFactory.CreateDiscountStrategy("seasonal");
Console.WriteLine($"Seasonal Discounted Price: {discountStrategy.ApplyDiscount(originalPrice)}");
discountStrategy = DiscountFactory.CreateDiscountStrategy("clearance");
Console.WriteLine($"Clearance Discounted Price: {discountStrategy.ApplyDiscount(originalPrice)}");
discountStrategy = DiscountFactory.CreateDiscountStrategy("member");
Console.WriteLine($"Member Discounted Price: {discountStrategy.ApplyDiscount(originalPrice)}");
Console.ReadKey();
}
}
}
By following this setup, we are required to expand the DiscountFactory and develop a fresh discount strategy class whenever a new type of discount is included. The consumer code remains unchanged and doesn't need to understand the specific implementation details of each discount. This demonstrates the versatility of the Factory Design Pattern in creating objects and facilitates a distinct segregation of responsibilities. The output displayed upon executing the aforementioned code will be as follows.
Output:
The CSS code snippet below creates a styled placeholder diagram for a web page:
.placeholder-diagram { background: linear-gradient(135deg, #374151 0%, #1f2937 100%); border-radius: 12px; padding: 40px; margin: 20px 0; text-align: center; }
.placeholder-diagram .placeholder-icon { font-size: 3rem; margin-bottom: 10px; }
.placeholder-diagram .placeholder-text { color: #9ca3af; font-size: 1rem; }
Factory Design Patterns: Real-Time Applications in C#
The following are some situations or real-world uses for the Factory Design Pattern:
- User Interface Control Creation: The Factory pattern can be used to produce controls (such as buttons, text boxes, or custom components) dynamically based on user actions or setups in applications with complex user interfaces, like Windows forms or WPF applications.
- Database Access: Instead of hard-coding the database type in the business logic, a factory can be used to instantiate the right database connection and command objects in applications that communicate with several databases (such as SQL Server, Oracle, or MySQL).
- Logging Frameworks: Based on the application settings or environment, a factory can offer suitable logging objects (such as file loggers, database loggers, or cloud-based loggers) for applications that need logging functionality.
- Configuration Management: A factory can build configuration objects that are specific to the current environment and abstract the details from the rest of the application, which is useful when managing several configurations (such as development, staging, and production).
- Systems for Processing Payments: Based on the chosen payment method, a factory can instantiate the appropriate payment processing class in systems that must process payments through several gateways (such as PayPal, Stripe, or credit card).
- Game creation: Characters, weaponry, and other game pieces can be dynamically created using a factory pattern, depending on the current state of the game or user decisions.
- Plugin or Module Systems: For programs that support plugins or modules, the Factory pattern can instantiate the relevant plugin based on user input or configuration files.
- API Integration: A factory can supply the appropriate API client depending on the service needed when integrating with different external APIs (such as social media, weather, or geolocation services).
- When the production of an object (such as threads or database connections) is costly, object pooling allows a factory to manage and reuse these objects efficiently.
- Loose Coupling: The Factory Pattern separates the product's use and implementation. This implies that the client code need not know the details of how to make the product.
- Scalability and Flexibility: Adding new concrete items without modifying the client code is simple, making the application more scalable and versatile.
- Single Responsibility Principle: In accordance with the Single Responsibility Principle, the factory class contains the logic for creating the goods. Code becomes neater and more structured as a result.
- Open/Closed Principle: The Factory Pattern allows you to add new product categories without changing the factory or client code already in place, which is consistent with the Open/Closed Principle.
- Handle Complexity: This feature is helpful for developing things that require more than just simple instantiation because it allows you to manage and centralize complex creation logic.
- Authority over Instantiation: Factories have authority over the process and timing of object creation. In a factory, for example, you can use prototype, pool, and singleton patterns.
- Complexity: Adding a factory pattern to the code can make it more complicated, especially if an object generation function alone would be sufficient.
- Indirection: The code occasionally becomes more difficult to comprehend and debug due to the additional layer of abstraction, particularly for people who are not experienced with the pattern.
- Overhead: The extra method call and object formation overhead may be a disadvantage in applications where efficiency is a top priority.
- Dependency on Factories: When producing objects, the client code still relies on the factory, which can cause issues such as the overabundance of factory classes in complicated systems.
- Refactoring Existing Code: It can be difficult and necessitate extensive reworking to integrate the Factory Pattern into existing code that wasn't created with this pattern in mind.
- Requires Proper Design: Improper use or application of the Factory Pattern (e.g., utilizing an excessive number of factories or the wrong abstraction) can cause problems with maintenance and make future changes more challenging.
Advantages of Factory Design Pattern in C#:
Disadvantages of Factory Design Pattern in C#:
Conclusion:
In summary, the Factory Design Pattern proves to be a valuable asset for adaptable and sustainable object instantiation in C# development. This design pattern promotes the reuse of code and the ability to scale by segregating client code from concrete classes through the encapsulation of object creation within a factory class. Real-world scenarios benefit from the Factory Design Pattern, especially when designing diverse types of entities that may vary based on particular conditions. By incorporating this pattern, developers can create code that is more modular, extensible, and enhance the quality and manageability of their software endeavors.