Terug naar overzicht

Design Patterns – Factory Pattern

Door Geert Van Hoef

.NET Core .NET Standard

Nov 2020

In the previous blog post we started our journey through the design pattern network with the Strategy Design Pattern. In this blog post, we are going to talk about another widely used design pattern that falls under the creational design patterns category, the Factory Design Pattern. Remember that creational design patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code. This can be achieved in many different ways. One of them is by applying one or more variants of the factory design pattern.

Factory Pattern

As I mentioned above, this pattern belongs to the creational design pattern category and there are multiple variants of this design pattern. The most known are “Simple Factory”, “Factory Method” and “Abstract Factory”. What they all have in common is that they define an interface for creating an object, but let subclasses decide which class to instantiate. These patterns can be recognized by one or more creation methods which create objects from concrete classes but return them as objects of abstract type or interface.

In this blog post we’ll have a look at the following topics:

  • Definition

  • Architecture

  • Code sample

  • References

1. Definition

The purpose of a factory is to have only one point of contact that returns us an instance of something that meets certain conditions, for example implements an interface. The Factory Pattern does this by “defining an interface for creating an object, but let sub-classes decide which class to instantiate”.

Let’s take a closer look at the two different things mentioned here;

  • “A factory defines an interface for creating an object”… A factory is an object that creates objects without the use of a constructor. It is used to replace this class’s constructor, abstract the process of object generation so that the object can be instantiated at run-time.

  • “Sub-classes decide which class to instantiate”… This pattern lets a class postpone instantiation to sub-classes. These sub-classes can create objects of different types.

Factory classes are often implemented because they allow the project to follow the SOLID principles more closely. In particular, the interface segregation and dependency inversion principles.
Factories and interfaces allow for a lot more long term flexibility. It allows for a more decoupled – and therefore more testable – design. Here is a non-exhaustive list of why you might go down this path:

  • It allows you to introduce an Inversion of Control (IoC) container easily

  • It makes your code more testable as you can mock interfaces

  • It gives you a lot more flexibility when it comes time to change the application (i.e. you can create new implementations without changing the dependent code)

2. Architecture

To clarify the architecture of the factory design pattern, I will use an example from the xamarin forms context. With xamarin forms it’s common to have a shared project in addition to your platform specific projects. In this example, data is loaded from an xml file and adjustments to this data can also be saved. The xml files are located in their OS specific place on disc. Both Android and iOS have their own QuoteLoader class. The intention is that regardless of the OS used, we can work with this data in our shared project. To solve this problem properly we use the Factory Pattern.

First, we’ll define an IQuoteLoader interface that we will implement in both our QuoteLoader classes. Next, we’ll create our QuoteLoaderFactory class that returns an object of type IQuoteLoader. You can also see the QuoteManager class as a management place for everything that has to do with quotes. This class uses the Factory to obtain an object of type IQuoteLoader.

Participants

  1. IQuoteLoader interface: This defines the interface of objects the factory method creates.

  2. QouteLoader classes: These are the classes that implements the IQuoteLoader interface.

  3. QuoteLoaderFactory class: This static class returns a method to create an instance of an IQuoteLoader.

3. Code Sample

First, we create a common interface that needs to be implemented by each concrete product class. One method to load all quotes from the resources and one method to save them.

// This interface needs to be implemented by each concrete product class
public interface IQuoteLoader {
  IEnumerable<GreatQuoteViewModel> Load();
  void Save(IEnumerable<GreatQuoteViewModel> quotes);
}

Each OS has his own QuoteLoader class. These “concrete product” classes implement the above IQuoteLoader interface. The only difference between Android and iOS is the location where they store internal files.

// Android QuoteLoader class where XML data is stored on Android specific file location
public class QuoteLoader : IQuoteLoader {
  const string FileName = "quotes.xml";

  public IEnumerable<GreatQuoteViewModel> Load() {
    …
    string filename = Path.Combine(
      Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), FileName);
    …
  }
  public void Save(IEnumerable<GreatQuoteViewModel> quotes) {
    filename = Path.Combine(
      Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), FileName);
      …
    doc.Save(filename);
  }
}
// iOS QuoteLoader class where XML data is stored on iOS specific file location
public class QuoteLoader : IQuoteLoader {
  const string FileName = "quotes.xml";
  public IEnumerable<GreatQuoteViewModel> Load() {
    …
    string filename = Path.Combine(
      Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
      "..", "Library", FileName);
    …
  }
  public void Save(IEnumerable<GreatQuoteViewModel> quotes) {
    filename = Path.Combine(
      Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
      "..", "Library", FileName);
      …
    doc.Save(filename);
  }
}

Next, we create a static factory class which represent the factory we’ll use to create our platform-specific implementation of IQuoteLoader. This class contains a single static property named Create that’s of type Func. Later, we’ll set this property to create a new IQuoteLoader.

public static class QuoteLoaderFactory { 
  // This must be assigned to a method which creates a new quote loader.
  public static Func Create { get; set; }
}

Now we can set this property in the OnCreate() method of the MainActivity class(Android) and in the FinishedLauching() method of the AppDelegate class(iOS).

// Android
protected override void OnCreate(Bundle savedInstanceState)
{ 
  …
  QuoteLoaderFactory.Create = () => new QuoteLoader();
  …
}

// iOS
public override bool FinishedLaunching(UIApplication uiApplication, NSDictionary launchOptions)
{
  QuoteLoaderFactory.Create = () => new QuoteLoader();
  …
}

The intention here is that we want to manage everything related to quotes in the shared code project of our application. To do so, we create a QuoteManager class which contains a property to expose a single copy of this class (singleton pattern!). We can use the built-in Lazy type to implement this. The next thing is that we also need an IQuoteLoader object. We achieve this by using the QuoteLoaderFactory.Create delegate. After that we can load our quotes from the IQuoteLoader object by calling the Load() method.

public class QuoteManager 
{
  static readonly Lazy<QuoteManager> instance = new Lazy<QuoteManager>(() => new 
  QuoteManager());

  private IQuoteLoader loader;

  public static QuoteManager Instance { get => instance.Value; }

  public IList<GreatQuoteViewModel> Quotes { get; set; }

  private QuoteManager() 
  {
    loader = QuoteLoaderFactory.Create();
    Quotes = new ObservableCollection<GreatQuoteViewModel>(loader.Load());
  }

  public void Save() {
    loader.Save(Quotes);
  }
}

The next step is to use the new shared QuoteManager class and assign the factory. We also want to save quotes from the MainViewModel, that’s why QuoteManager is a dependency on this viewmodel.

public class MainViewModel : BaseViewModel {

  private readonly QuoteManager quoteManager;

  public MainViewModel() 
  {
    quoteManager = QuoteManager.Instance;
    Quotes = quoteManager.Quotes as ObservableCollection<GreatQuoteViewModel>;
  }

  public ObservableCollection<GreatQuoteViewModel> Quotes { get; set; }

  public GreatQuoteViewModel ItemSelected { get; set; }

  public void SaveQuotes() 
  {
    quoteManager.Save();
  }
}

References

Application Logging – “That warm fuzzy blanket for when production doesn’t behave” Thumb

Door Steven Hillaert

Nov 2023

Application Logging – “That warm fuzzy blanket for when production doesn’t behave”

Whenever I see a codebase that has logs, I feel safe. Because when things start to break, I know I’ll have data to help me fix it.

Enabling (as) the next generation software developers Thumb

Door Ruben Verheyen

Oct 2022

Enabling (as) the next generation software developers

Wij zijn allemaal AllPhi. Ieder van ons maakt deel uit van het grote geheel en kan vanuit z’n eigen positie z’n steentje bijdragen. En wanneer we er bewust ...

AllPhi Culture
.NET MAUI, a bright and shiny new bit of technology Thumb

Door Mathias Peene

Aug 2022

.NET MAUI, a bright and shiny new bit of technology

What’s my next experience with MAUI? I’m probably going to continue playing around with the MonkeyFinder application, just to get to know the framework ...

.NET Core .NET Standard
Cache primary btn default asset Cache primary btn hover asset Cache white btn default asset Cache white btn hover asset