Showing posts with label C# Tutorials. Show all posts
Showing posts with label C# Tutorials. Show all posts

When to use it Abstract Factory Design Pattern

When to use it Abstract Factory Design Pattern

 ðŸ‘‰ðŸ‘‰ðŸ‘‰ðŸ‘‰Differences between Abstract Factory and Factory Method Design Pattern

The Abstract Factory Design Pattern is particularly useful in situations where:

1. Need to Create Families of Related Objects

When your system needs to handle multiple families of related or dependent objects without specifying their concrete classes. For example, a user interface toolkit that supports different themes (e.g., dark mode, light mode) might require different sets of components (buttons, text boxes) for each theme.

2. Product Variations

When you have multiple product variations that need to work together. If you need to ensure that all products in a family are compatible with each other, the Abstract Factory helps by providing a way to create these products in a cohesive manner.

3. Object Creation is Decoupled from Use

When you want to decouple the creation of objects from their use. The Abstract Factory provides an interface for creating objects, which allows the client code to remain independent of the concrete classes being instantiated.

4. System is Configured to Work with Multiple Product Families

When your system needs to support multiple product families, and you want to switch between these families easily. For example, a system that needs to support both Windows and Mac platforms with different sets of components can use Abstract Factory to manage these variations.

5. Consistency Among Related Products

When you need to ensure consistency among related products. The Abstract Factory guarantees that a set of related products are created together, ensuring that they fit together and work as intended.

Example Scenarios

1. User Interface Libraries

If you are developing a UI library that needs to work across different platforms (e.g., Windows, Mac, Linux), you can use the Abstract Factory to create platform-specific components (buttons, text boxes, menus) while ensuring they are consistent with each other.

public interface IUIFactory
{
    IButton CreateButton();
    ITextBox CreateTextBox();
}

public class WindowsFactory : IUIFactory
{
    public IButton CreateButton() => new WindowsButton();
    public ITextBox CreateTextBox() => new WindowsTextBox();
}

public class MacFactory : IUIFactory
{
    public IButton CreateButton() => new MacButton();
    public ITextBox CreateTextBox() => new MacTextBox();
}

C#

2. Document Management Systems

For a document management system that supports multiple formats (e.g., PDF, Word), you can use Abstract Factory to create readers, editors, and savers for each document format.

public interface IDocumentFactory
{
    IReader CreateReader();
    IEditor CreateEditor();
    ISaver CreateSaver();
}

public class PdfDocumentFactory : IDocumentFactory
{
    public IReader CreateReader() => new PdfReader();
    public IEditor CreateEditor() => new PdfEditor();
    public ISaver CreateSaver() => new PdfSaver();
}

public class WordDocumentFactory : IDocumentFactory
{
    public IReader CreateReader() => new WordReader();
    public IEditor CreateEditor() => new WordEditor();
    public ISaver CreateSaver() => new WordSaver();
}

C#
3. Game Development

In game development,if you need to create different sets of game objects (e.g., enemies, weapons) for different levels or settings, you can use Abstract Factory to manage these variations.

public interface IGameFactory
{
    IEnemy CreateEnemy();
    IWeapon CreateWeapon();
}

public class EasyLevelFactory : IGameFactory
{
    public IEnemy CreateEnemy() => new EasyEnemy();
    public IWeapon CreateWeapon() => new BasicWeapon();
}

public class HardLevelFactory : IGameFactory
{
    public IEnemy CreateEnemy() => new HardEnemy();
    public IWeapon CreateWeapon() => new AdvancedWeapon();
}

C#

Summary

Use the Abstract Factory Design Pattern when:

  • You need to create families of related objects.
  • You want to ensure that objects from a family are used together.
  • Your system needs to support multiple product families and you want to keep your client code decoupled from concrete implementations.
  • You want to provide a consistent interface for creating related objects.

This pattern helps in managing complex object creation logic, ensuring consistency,

and providing flexibility in how objects are created and used. 


Differences between Abstract Factory and Factory Method Design Pattern

Differences between Abstract Factory and Factory Method Design Pattern

The Abstract Factory and Factory Method design patterns are both creational patterns used to create objects, but they differ in their structure and intent. Here are the key differences:

1. Intent

  • Factory Method: Defines an interface for creating a single product, but lets subclasses alter the type of product that will be created. The intention is to defer the instantiation of a class to subclasses.
  • Abstract Factory: Provides an interface for creating families of related or dependent products without specifying their concrete classes. The intention is to create a set of related products.

2. Structure

  • Factory Method: Typically involves a single method in a class to create an object. This pattern relies on inheritance: a base class defines the factory method, and derived classes implement the factory method to create specific products.

// Factory Method
public abstract class Creator
{
    public abstract IProduct FactoryMethod();
}

public class ConcreteCreator : Creator
{
    public override IProduct FactoryMethod()
    {
        return new ConcreteProduct();
    }
}

C#


Abstract Factory: Involves multiple factory methods grouped into a single interface to create families of related products. This pattern relies on object composition: a factory class is responsible for creating products, and concrete factory classes implement the creation methods.
public interface IAbstractFactory
{
    IProductA CreateProductA();
    IProductB CreateProductB();
}

public class ConcreteFactory : IAbstractFactory
{
    public IProductA CreateProductA()
    {
        return new ConcreteProductA();
    }

    public IProductB CreateProductB()
    {
        return new ConcreteProductB();
    }
}

C#


3. Scope

  • Factory Method: Deals with the creation of a single product. Each factory method is responsible for creating a single type of product.
  • Abstract Factory: Deals with the creation of families of related products. An abstract factory groups related factory methods together, so you can create various related products through a single interface.

4. Usage

  • Factory Method: Used when a class cannot anticipate the class of objects it must create. It's useful when a class wants its subclasses to specify the object to be created.
  • Abstract Factory: Used when a system should be independent of how its products are created and composed. It's useful when a system needs to support multiple families of products.

5. Example

Factory Method

// Product Interface
public interface IProduct
{
    void Operation();
}

// Concrete Product
public class ConcreteProduct : IProduct
{
    public void Operation()
    {
        Console.WriteLine("ConcreteProduct Operation");
    }
}

// Creator
public abstract class Creator
{
    public abstract IProduct FactoryMethod();

    public void AnOperation()
    {
        IProduct product = FactoryMethod();
        product.Operation();
    }
}

// Concrete Creator
public class ConcreteCreator : Creator
{
    public override IProduct FactoryMethod()
    {
        return new ConcreteProduct();
    }
}

// Usage
class Program
{
    static void Main(string[] args)
    {
        Creator creator = new ConcreteCreator();
        creator.AnOperation();
    }
}

C#

Abstract Factory

// Abstract Products
public interface IProductA
{
    void OperationA();
}

public interface IProductB
{
    void OperationB();
}

// Concrete Products
public class ConcreteProductA1 : IProductA
{
    public void OperationA()
    {
        Console.WriteLine("ConcreteProductA1 OperationA");
    }
}

public class ConcreteProductB1 : IProductB
{
    public void OperationB()
    {
        Console.WriteLine("ConcreteProductB1 OperationB");
    }
}

// Abstract Factory
public interface IAbstractFactory
{
    IProductA CreateProductA();
    IProductB CreateProductB();
}

// Concrete Factory
public class ConcreteFactory1 : IAbstractFactory
{
    public IProductA CreateProductA()
    {
        return new ConcreteProductA1();
    }

    public IProductB CreateProductB()
    {
        return new ConcreteProductB1();
    }
}

// Usage
class Program
{
    static void Main(string[] args)
    {
        IAbstractFactory factory = new ConcreteFactory1();
        IProductA productA = factory.CreateProductA();
        IProductB productB = factory.CreateProductB();

        productA.OperationA();
        productB.OperationB();
    }
}

C#

Summary

  • Factory Method is used for creating a single product and allows subclasses to determine the actual product to be created.
  • Abstract Factory is used for creating families of related products and provides a way to encapsulate a group of individual factories.

Each pattern has its own use case and choosing between them depends on the specific requirements of the software design.

👉👉👉When to use it Abstract Factory Design Pattern

What’s new in C# 13 | C# 13 new features

 

C# 13, introduced with .NET 9, brings several notable features and enhancements designed to improve the language's functionality and usability. Here are some of the key updates:




Params Collections: The params modifier can now be used with any recognized collection type, not just arrays. This includes types like System.Span<T>, System.ReadOnlySpan<T>, and collections that implement IEnumerable<T> and have an Add method.

The `params` modifier can now be used with various collection types, not just arrays.


using System;

using System.Collections.Generic;

public class Example {

public static void PrintNumbers(params List<int> numbers)

{
    foreach(var number in numbers)
    {
        Console.WriteLine(number);
    }

public static void Main()
{
    PrintNumbers(1, 2, 3, 4, 5);
}

}

C#

Explanation:

The PrintNumbers method takes a params List<int> numbers, allowing it to accept multiple integer arguments.

You can call PrintNumbers with individual integers, and they will be collected into a List<int>. 

New Lock Type and Semantics: A new System.Threading.Lock type has been introduced for better thread synchronization. The C# lock statement recognizes this new type and uses its improved API, providing more efficient thread management​.


using System;

using System.Threading;

public class Example {

 private static Lock _lock = new Lock();

 public static void Main() {

    using(_lock.EnterScope())
    {
      Console.WriteLine("Inside the lock.");
    }

  // The lock will automatically released here.
    }

}

C#

Explanation:

A new Lock type is used to handle thread synchronization.

The EnterScope method provides a ref struct that implements IDisposable, allowing the lock to be used within a using statement for automatic disposal and release.

C# 13 introduces a new way to handle locking (thread synchronization) called System.Threading.Lock. This new lock type offers better control and efficiency for managing access to shared resources in multi-threaded programs.

Old Way with lock Statement:

private static readonly object _lockObject = new object();

public void DoWork()
{
    lock (_lockObject)
    {
        // Critical section code
    }
}

C#

  • Uses the lock keyword with an object (_lockObject) to manage access.
  • The lock is explicitly controlled using the Monitor class under the hood.

New Way with System.Threading.Lock:


using System;
using System.Threading;

public class Example
{
    private static Lock _lock = new Lock();

    public static void Main()
    {
        using (_lock.EnterScope())
        {
            Console.WriteLine("Inside the lock.");
            // Critical section code
        }
        // The lock will automatically released here.
    }
}

    C#

    • Introduces a new Lock type from System.Threading.
    • Uses EnterScope method to acquire the lock, which returns a ref struct.
    • The ref struct supports the Dispose pattern, allowing you to use it with a using statement.
    • The lock is automatically released when the using block exits, making the code cleaner and less error-prone.

    Benefits:

    • Efficiency Provides more efficient thread synchronization.
    • Safety Reduces the risk of accidentally forgetting to release the lock, which can lead to deadlocks.
    • Readability The new syntax with using makes it clear when the lock is acquired and released.

    New Escape Sequence (\e): The \e escape sequence has been added to represent the ESCAPE character (Unicode U+001B). This provides a more readable and concise way to include this character in strings, as opposed to using `\u001b` or `\x1b`



    public class Example
    {
        public static void Main()
        {
            string escapeCharacter = "\e";
            Console.WriteLine($"This is an escape character: {escapeCharacter}");
        }

    }

    C#

    Explanation:

    The \e escape sequence is used to insert the ESCAPE character into a string, making the code more readable compared to using \u001b or \x1b.

    Method Group Natural Type Improvements: The process of determining the natural type for method groups has been optimized. The compiler now prunes inapplicable candidate methods earlier, improving performance and following a more logical overload resolution algorithm​.

    Improvements to method group type inference, optimizing overload resolution.

    When you pass a method as an argument (method group) to another method, C# has to figure out which exact overload (version of the method) to use. This process is known as method overload resolution. In C# 13, this process has been optimized to make the selection of the correct method more efficient and logical.

    using System;
    using System.Linq

    public class Example
    {
        public static void Main()
        {
            int[] numbers = { 1, 2, 3, 4, 5 };

            var result = numbers.Select((Func<int, bool>)IsEven).ToList();

            result.ForEach(Console.WriteLine);
        }

        public static bool IsEven(int number)
        {
            return number % 2 == 0;
        }

    }

    C#

    Explanation:

    The method group IsEven is explicitly cast to Func<int, bool>, allowing the Select method to use the correct overload.

    The compiler optimizes the resolution of method groups by pruning inapplicable candidates earlier

    Consider a scenario where you want to filter a list of numbers to get only the even numbers using a method group.


    using System;
    using System.Collections.Generic;
    using System.Linq;

    public class Example
    {
        public static void Main()
        {
            List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
           
            // Method group: Passing 'IsEven' method directly to 'Where'
            var evenNumbers = numbers.Where(IsEven).ToList();
           
            evenNumbers.ForEach(Console.WriteLine); // Output: 2, 4
        }

        public static bool IsEven(int number)
        {
            return number % 2 == 0;
        }
    }

    C#

    Explanation:
    1. Method Group: IsEven is the method group being passed to Where.
    2. Type Inference: C# needs to determine the type of the method group IsEven. The Where the method expects a delegate of type Func<int, bool>.
    3. Improved Resolution: In C# 13, the compiler more efficiently determines that IsEven matches Func<int, bool>, improving the performance and accuracy of the selection process.

    Benefits:

    • Performance The compiler now discards unsuitable methods earlier, speeding up the resolution process.
    • Accuracy Ensures the most appropriate method overload is selected, reducing potential errors.

    This optimization makes the code more efficient and maintains readability, especially in scenarios involving multiple overloads or complex method selections.

    Implicit Indexer Access in Object Initializers: The implicit "from the end" index operator (^) can now be used in object initializers. This allows for more flexible and concise array initializations within object initializers.

    Using the ^ operator in object initializers to access elements from the end of a collection.


    using System

    public class TimerRemaining
    {
        public int[] buffer = new int[10];

    public class Example
    {
        public static void Main()
        {
            var countdown = new TimerRemaining
            {
                buffer =
                {

                    [^1] = 0,

                    [^2] = 1,

                    [^3] = 2,

                    [^4] = 3,

                    [^5] = 4,

                    [^6] = 5,

                    [^7] = 6,

                    [^8] = 7,

                    [^9] = 8,

                    [^10] = 9
                }
            }; 

            foreach (var value in countdown.buffer)
            {
                Console.WriteLine(value);
            }

        }

    }

    C#

    Explanation:

    The ^ operator is used in the object initializer to index from the end of the array.

    This feature allows more concise and expressive array initializations directly within object initializers.

    In C# 13, you can use the ^ operator within object initializers to easily access elements from the end of a collection. This is useful for arrays and other indexable collections, making code more concise and readable.

    Before C# 13


    public class TimerRemaining
    {
        public int[] buffer = new int[10];
    }

    public class Example
    {
        public static void Main()
        {
            var countdown = new TimerRemaining();
            countdown.buffer[countdown.buffer.Length - 1] = 0;
            countdown.buffer[countdown.buffer.Length - 2] = 1;
            countdown.buffer[countdown.buffer.Length - 3] = 2;
            countdown.buffer[countdown.buffer.Length - 4] = 3;
            countdown.buffer[countdown.buffer.Length - 5] = 4;
            countdown.buffer[countdown.buffer.Length - 6] = 5;
            countdown.buffer[countdown.buffer.Length - 7] = 6;
            countdown.buffer[countdown.buffer.Length - 8] = 7;
            countdown.buffer[countdown.buffer.Length - 9] = 8;
            countdown.buffer[countdown.buffer.Length - 10] = 9;

            foreach (var value in countdown.buffer)
            {
                Console.WriteLine(value);
            }
        }
    }

    C#

    With C# 13

    public class TimerRemaining
    {
        public int[] buffer = new int[10];
    }

    public class Example
    {
        public static void Main()
        {
            var countdown = new TimerRemaining
            {
                buffer =
                {
                    [^1] = 0,
                    [^2] = 1,
                    [^3] = 2,
                    [^4] = 3,
                    [^5] = 4,
                    [^6] = 5,
                    [^7] = 6,
                    [^8] = 7,
                    [^9] = 8,
                    [^10] = 9
                }
            };

            foreach (var value in countdown.buffer)
            {
                Console.WriteLine(value);
            }
        }
    }

    C#


    Array Initialization:


    before C# 13 You had to manually calculate the indices from the end using Length - 1, Length - 2, and so on, which is verbose and error-prone.

    With C# 13 The ^ operator allows you to directly access elements from the end of the array. For example, [^1] accesses the last element, [^2] the second last, and so on.

    Benefits


    Readability Using the ^ operator makes the code shorter and more readable.
    Convenience It simplifies the initialization process by eliminating the need for manual index calculations.

    These updates make C# 13 a more powerful and efficient language, enhancing both the developer experience and the performance of C# applications. For more detailed information, you can refer to the official Microsoft documentation and other developer resources


    -------------------