Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Factory method and where to choose which factory is to be used

I have a project involving a webshop for different kinds of products. From what I understand, if you have multiple classes that inherit from one base class, the factory design pattern is the way to go. I only have trouble deciding where to put the logic for actually deciding which factory to use.

I have created classes and factory classes for the different kinds of products like so.

public class Product
{
    public int ID { get; protected set; }
    public string Name { get; set; }
    public ProductType Type { get; private set; }

    public Product(int id)
    {
        ID = id;
        Populate();
    }

    public virtual void CompleteOrder()
    {
        //SendMailToSupplier();
    }

    private void Populate() 
    {
        //database stuff including setting the type
    }
}

public class DigitalProduct : Product
{
    public DigitalAsset ProductAsset { get; private set; }

    public DigitalProduct(int id, DigitalAsset asset) : base(id)
    {
        ProductAsset = asset;
    }

    public override void CompleteOrder()
    {
        base.CompleteOrder();
        //SendAssetToUser();
    }
}

public class PrintProduct : Product
{
    public PrintInformation Information { get; private set; }

    public PrintProduct(int id, PrintInformation information) : base(id)
    {
        Information = information;
    }

    public override void CompleteOrder()
    {
        base.CompleteOrder();
        //PreparePrintingFlle();
    }
}

public abstract class ProductFactory
{
    public abstract Product CreateProduct(int id);
}

public class GenericProductFactory : ProductFactory
{
    public override Product CreateProduct(int id)
    {
        return new Product(id);
    }
}

public class DigitalProductFactory : ProductFactory
{
    public override Product CreateProduct(int id)
    {
        DigitalAsset asset = GetDigitalAsset(id);
        return new DigitalProduct(id, asset);
    }   

    private DigitalAsset GetDigitalAsset(int id)
    {
        DigitalAsset asset = new DigitalAsset();
        //IO stuff
        return asset;
    }
}

public class PrintProductProductFactory : ProductFactory
{
    public override Product CreateProduct(int id)
    {
        PrintInformation information = GetPrintInformation(id);
        return new PrintProduct(id,information);
    }

    private PrintInformation GetPrintInformation(int id)
    {
        PrintInformation information = new PrintInformation();
        //database stuff
        return information;
    }
}

Now when an order is completed an event is triggered.

public void OrderCompleted(int orderId, List<int> productIds);

So here I have a list of int's from which I want to make product object so I can call the CompleteOrder function on each of them. The problem is to know the type of the product I need to get the product type from the database which is filled in the populate function.

What I could do is create a function public ProductFactory GetFactory(int id) in the ProductFactory. But then the factory is can not be an abstract class.

Another option would be creating a function public static Product GetProduct(int id) in the product class. Which then first figures out which factory to use and the returns the created product.

But both of this options feel weird. Am I missing something? Or is one of these the actual way to go?

Cheers.

like image 802
Shingala94 Avatar asked Jan 20 '26 15:01

Shingala94


1 Answers

So skip the Populate function in the Product class. That is the duties of the factory to do. And I would go with one factory class to create all types of products. (The factory can if needed call other classes to help create the product)

Something like:

public class ProductFactory
{
     public Product GetProductById(int id)
     {
          var dbProduct = FetchFromDb(id);
          switch(dbProduct.Type)
          {
              case "Print"
                 return CreatePrintProduct(dbProduct);
              case "Digital"
                 return CreateDigitalProduct(dbProduct);
          }
     }


     private DigitalProduct CreateDigitalProduct(DbEntity dbProduct)
     {
         var product = new DigitalProduct(dbProduct.Id);
         //Initialize the product

         return product;
     }

     //You might also want a batch function to avoid calling the database for each product.
     public IEnumerable<Product> GetProductByIds(IEnumerable<int> ids)
     {
          var dbProducts = FetchFromDb(ids);
          ...
     }
}
like image 54
Magnus Avatar answered Jan 22 '26 03:01

Magnus



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!