design-patternsumlfactory-pattern

Clarifying UML class diagram of Factory Method design pattern


I was learning factory method design pattern and found following class diagram in a tutorial. I understand product and concreteProduct part but Creator and ConcreteCreator part look little vague to me. I appreciate if someone clarifies the UML diagram. Thanks. enter image description here


Solution

  • The examples in this answer are written in C#. The examples are really basic. Basic enough so that somebody without knowledge of C# understands the intention of the code.

    When talking about abstract and concrete types in context of the design pattern, we are never referring to a particular language model. For example, in some programming languages abstract types are realized by abstract classes and interfaces, while not all languages know interfaces.
    Design patterns are always language agnostic: they must be postulated programming language independent in order to qualify as a design pattern.
    For that reason, an abstract type is an object descriptor that can't be instantiated - it's the abstraction of the instantiated object.
    A concrete type is the instantiated object based on the object's abstraction.
    OO languages are usually based on this model: an object description type and the actual object in memory (the instance).

    The Factory Method pattern describes a way to encapsulate and delegate instantiation of a type (type creation): it's a creational design pattern.
    For most languages that means that the client can create instances of an anonymous product without calling the constructor.
    The factory method is usually prefixed with Create... e.g. Creator.CreateSpecializedObject() and defined as abstract.

    The purpose of this pattern is to delegate instantiation and initialization of the consumed object instance from the caller or consumer (the superclass or base class) away to a more specialized subclass to avoid a strong coupling to the consumed object (product).
    The strong coupling is eliminated because the caller no longer has to explicitly invoke a constructor for an explicit type. Instead, the created concrete type can remain anonymous.

    In other words, Factory Method allows a base class to create instances of subclasses without knowing the derived type explicitly.

    The participants of the Factory Method pattern are:

    1. The Creator, usually an abstract base class that defines and uses the abstract factory method in its base functionality:

      C#
      
      // 1. The abstract Creator
      abstract class Creator
      {
        // Define abstract factory method 
        // that returns an abstract Product instance
        protected abstract Product CreateProduct()
      
        // The base functionality that depends on the abstract product type
        public void DoSomethingWithAbstractProduct()
        {
          // Get an instance of a subclass of Product by invoking the factory method 
          // (and not the constructor)
          Product anonymousProduct = CreateProduct();
        }
      }
      
    2. The Product, usually an abstract base class for the types that the abstract Creator uses (depends on) to implement the base functionality:

      C#
      
      // The abstract Product
      abstract class Product
      { }
      
    3. The ConcreteProduct, the realization (inheritor) of the abstract Product and the actual (anonymous) instance that the abstract Creator is operating on:

      C#
      
      // The concrete Product that inherits from the abstract Product
      class ConcreteProduct : Product
      { }
      
    4. The ConcreteCreator, the realization (inheritor) of the abstract Creator. The ConcreteCreator must override the abstract factory method to return a ConcreteProduct for the Creator. The abstract Creator then uses this instance to realize the base functionality. Because the factory method's return type is the abstract Product, the abstract Creator never knows the concrete type of the ConcreteProduct --> abstract Creator uses the result of the factory method anonymously.

      C#
      
      // The concrete Creator that implements the abstract Creator
      class ConcreteCreator : Creator
      {
        // Implement the abstract factory method 
        // that returns an abstract Product instance
        protected override Product CreateProduct()
        {
          // The caller of this factory method never knows that the 
          // returned instance is of type ConcreteProduct. 
          // Because of the method's return type the instance is 
          // only known as Product.
          return new ConcretProduct();
        }
      }
      

    Since the factory method is supposed to return the abstract base type of the produced instance, the caller (the superclass aka base class) can create and consume product instances in advance and without knowing the concrete implementation. The consumer only depends and operates on abstractions. In advance means that the base class can implement functionality that uses the abstract product without having to care how the product is created and of what concrete type it is.

    Factory Method allows to implement a default behavior for a type that requires to operate on instances of an abstract type. The specialized concrete implementations of this abstract type are then provided by the inheritors, which have to override/implement the abstract factory method to return this type.

    Scroll down to skip the UML diagram discussion and find a brief example implementation of this pattern...


    UML diagram discussion

    In UML model language, class members like fields are called Attribute and methods are called Operation. In UML, everything has a meaning e.g., symbols, stroke style of lines, arrow design and font formatting like italic letters.

    The diagram says: enter image description here

    a) We have an abstract type Product.
    We know it is abstract, because the class name is written in italic letters.

    b) There is a concrete class ConcreteProduct.
    We know it is concrete i.e. an implementation, because the class name is written in normal letters.

    c) The arrow that points from ConcreteProduct to Product describes that ConcreteProduct inherits Product (Generalization).
    We know it, because a hollow arrow always symbolizes inheritance and always points from the subclass (inheriting type) to the superclass (base type, the generalization of the subclass).

    This arrow expresses: "{type_B} is subclass of {type_A}"
    or "{type_B} ๐Ÿ – {type_A}".

    When the abstract type would be an interface, you would've replaced the arrow with a dashed hollow arrow (with the same orientation).

    The relationship between a class ClassA and interface is called Realization.
    The relationship between a class ClassA and its parent class (superclass) is called Generalization.

    d) Then we have another abstract class Creator, that declares the public operation (method) FactoryMethod() and another public operation AnOperation() as a contract, that every non-abstract inheritor must implement.
    We know both methods are public because of the prefix +, which symbolizes public visibility (for both attributes and operations).

    e) According to the attached arrow we can tell that theConcreteCreator inherits from Creator and therefore is required to implement the in the contract Creator declared public or virtual attributes and operations.

    f) Furthermore the ConcreteCreator has a dependency to the type ConcreteProduct, because it instantiates and returns this type. We know it is a dependency because dependencies are symbolized by the dashed solid arrow. This arrow always points from the depending object to the object it depends on.

    It expresses "{type_X} needs/knows/uses {type_Y}"
    or "{type_X} โค‘ {type_Y}".

    g) The two boxes that look like paper sheets are used to annotate the elements of the diagram. In this case it informs about the return value of the operation.

    Alternatively (and preferably) you would add the return value to the signature definition using the colon :. The colon introduces the type of return value, which would be void in case the operation returns nothing.
    The following signature describes an operation that takes no argument and returns an instance of type Product: +FactoryMethod(void):Product

    The visibility of virtual attributes or methods is identified by their member name being written in italic letters, while other access modifiers (visibilities) are identified by a symbol that precedes the name of the attribute or operation (+: public, -: private, #: protected, ~: internal or package).


    You cannot create instances of abstract types (abstract classes and interfaces), only of concrete types. If the inheritor of an abstract type is not abstract itself, it must provide an implementation of all abstract members that are not declared as virtual.

    Abstract types are a contract or a guarantee, that all inheritors (either abstract themself or concrete implementations) will have the members defined according to that contract. Therefore, we know that every type that inherits from Creator e.g., ConcreteCreator must have a method FactoryMethod() implemented, because Creatorhas defined the FactoryMethod() as abstract.

    In advanced programming, you only use abstract types or interface types as member types (method and instance/class variables) or local variable declarations. Instead of referencing a local instance variable or class member or parameter field using the derived type e.g. ConcreteCreator, you use the least specialized superclass or interface that offers the required functionality e.g. Creator.
    You typically use it like this:

    // Instead of: 
    // ConcreteCreator concreteCreator = new ConcreteCreator();
    
    // Use the abstract base type (or interface) to declare the variable:
    Creator creator = new ConcreteCreator();
    
    // Because 'Creator' defines a contract, 
    // we know we can always invoke a method called 'FactoryMethod()' 
    // on every type that inherits from 'Creator'
    // and that the return value is guaranteed 
    // to be an instance of type 'Product'
    Product product = creator.FactoryMethod();
    

    The implemented factory method ConcreteCreator.FactoryMethod() returns a ConcreteProduct to satisfy the contract defined by the abstract Creator type. This contract defines a return value of type Product. ConcreteProduct is assignable to Product because 'ConcreteProduct' inherits Product.

    Factory Method Example Implementation

    // Abstract creator.
    // This abstract class provides a default behavior 
    // to pick up and transport people using a vehicle.
    // The actual transportation vehicle is 
    // created by the inheritor.
    abstract class Driver
    {
      // The abstract factory method that the inheritor has to implement.
      protected abstract Vehicle CreateVehicle();
    
      protected List<Persons> PickUpPersons(List<Destination> destinations)
      {    
        List<Person> result = new List<Person>();
    
        // Use the factory method to create more derived types of Vehicle
        // that are not known yet. These more derived types 
        // actually implement the abstract factory method to return an appropriate instance
        Vehicle vehicle = CreateVehicle();
        foreach (Destination destination in destinations)
        {
          Location location = vehicle.Move(destination);
          Person pickedUpPerson = location.GetPerson();
          result.Add(pickedUpPerson);
        }
        return result;
      }
    }
    
    // Concrete creator
    class BusDriver extends Driver
    {
      // Override and provide the instance that derives from Vehicle
      protected override Vehicle CreateVehicle()
      {
        // Bus implements Vehicle    
        Vehicle bus = new Bus();
        bus.Refuel(new Diesel(100));
        return bus;
      }
    
      public decimal StartJob()
      {
        List<Destination> destinations = GetBusStations();
    
        // Call to base.PickUpPersons invokes the base functionality 
        // provided by the Driver base class (this is where 
        // the factory method is invoked)
        List<Person> persons = PickUpPersons(destinations);
        decimal pay = CollectMoney(persons);
        return pay;
      }
    }
    
    // Concrete creator
    class TaxiDriver extends Driver
    {
      // Override and provide the instance that derives from Vehicle
      protected override Vehicle CreateVehicle()
      {
        // Taxi implements Vehicle 
        Vehicle taxi = new Taxi();
        taxi.Refuel(new Gasoline(50));
        Clean(taxi);
        return taxi;
      }
    
      public decimal StartJob()
      {
        List<Destination> destinations = AwaitCaller();
    
        // Call to base.PickUpPersons invokes the base functionality 
        // provided by the Driver base class (this is where 
        // the factory method is invoked)
        List<Person> persons = PickUpPersons(destinations);
        decimal pay = CollectMoney(persons);
        return pay;
      }
    }
    

    Usage

    class TransportationCompany
    {
      public void RunBusiness()
      {
        Driver busDriver = new BusDriver();
        decimal cash = busDriver.StartJob();
    
        Driver taxiDriver = new TaxiDriver();
        decimal moreCash = taxiDriver.StartJob();
      }
    }
    

    Remarks

    There is another alternate version of the Factory Method pattern (although this is not the official GoF Factory Method design pattern). This alternate version uses a static factory method defined by a class to create instances of itself. You typically call this factory method, instead of the constructor. This is a convenient way to create a preconfigured type instance.
    In many cases, the constructor itself is defined as non-public (but this is not a requirement). For example, a hidden constructor makes sense if there is only a number of valid instance configurations.

    For example, consider the following FlatTv class that extends Product and represents a flat TV for different international markets that must use the correct language. Let's assume the FlatTv sells to an English, German and Chinese market. And the FlatTv defines attributes like control buttons that are labeled in the individual languages and a manual for that particular language.

    Instead of configuring the FlatTv instance manually and cumbersome:

    // Configure the FlatTv instance manually
    Manual englishManual = new EnglishManual();
    Product englishProduct = new FlatTv(englishManual);
    englishProduct.Language = Language.English;
    
    Manual germanManual = new GermanManual();
    Product germanProduct = new FlatTv(germanManual);
    germanProduct.Language = Language.German;
    
    Manual chineseManual = new ChineseManual();
    Product chineseProduct = new FlatTv(chineseManual);
    chineseProduct.Language = Language.Chinese;
    

    We can provide a more convenient API to avoid all the cumbersome client side configuration:

    Product englishProduct = FlatTv.CreateEnglishMarketProduct();
    Product chineseProduct = FlatTv.CreateChineseMarketProduct();
    Product germanProduct = FlatTv.CreateGermanMarketProduct();
    

    Implementation Example

    class FlatTv : Product
    {
      public static Product CreateEnglishMarketProduct()
      {
        // Initialize and configure the new instance
        Manual englishManual = new EnglishManual();
        Product newEnglishFlatTv = new FlatTv(englishManual);
        newEnglishFlatTv.Language = Language.English;
        
        // Return the configured instance
        return newEnglishFlatTv;
      }
    
      public static Product CreateGermanMarketProduct()
      {
        // Initialize and configure the new instance
        Manual germanManual = new GermanManual();
        Product newGermanFlatTv = new FlatTv(germanManual);
        newGermanFlatTv.Language = Language.German;
        
        // Return the configured instance
        return newGermanFlatTv;
      }
    
      public static Product CreateChineseMarketProduct()
      {
        // Initialize and configure the new instance
        Manual chineseManual = new ChineseManual();
        Product newChineseFlatTv = new FlatTv(chineseManual);
        newChineseFlatTv.Language = Language.Chinese;
        
        // Return the configured instance
        return newChineseFlatTv;
      }
    
      // For example, if the number of configurations are restricted 
      // we can hide the constructor to enforce creation 
      // using the factory methods. For example, we don't allow 
      // an English instance with a German manual - this would make an invalid FlatTv (an unhappy customer/ buggy experience).
      private FlatTv(Manual manmal)
      { ... }
    }
    

    Usage

    class Program
    {
      public static Main()
      {
        /* Get a new and differently preconfigured FlatTv instances
           without having to worry about parameters and other details
        */
    
        Product englishMarketProduct = FlatTv.CreateEnglishMarketProduct();
        Product germanMarketProduct = FlatTv.CreateGermanMarketProduct();
        Product chineseMarketProduct = FlatTv.CreateChineseMarketProduct();
      }
    }