Simple factory and Factory Method Design pattern difference
Asked Answered
C

3

6

I am learning design patterns newly & I am trying to understand the difference between Simple Factory & Factory Method Pattern. First I want to clear that , I tried reading lot of articles from Stack-overflow and other sites regarding the same but that doesn't helped me.

Here is my question: Lets consider I have a product hierarchy as shown below: enter image description here

I have written a simple Factory class as shown below

public class SimpleItemFactory {

    static Item getItem(String type) {
        if(type.equals("Cake")) {
            return new Cake();
        }else if(type.equals("Chocolate")) {
            return new Chocolate();
        }else {
            return null;
        }
    }
}

so now I have all the object creation in one place , so if tomorrow any changes occurs[Like constructor needs one parameter] we need to change in only one place. But it breaks OPEN CLOSED principle as if tomorrow we add more item we need to change getItem() methods if condition. So we go for Factory Method Pattern enter image description here

We create Factory class as shown below:

public abstract class ItemFactory {
    abstract Item getItem();
}

class CakeFactory extends ItemFactory {
    @Override
    Item getItem() {
        return new Cake();
    }
}

class ChocolateFactory extends ItemFactory {
    @Override
    Item getItem() {
        return new Chocolate();
    }
}


class Client{
    public static void main(String[] args) {

        Item chocolate = new ChocolateFactory().getItem();
        System.out.println(chocolate);
    }
 }

Now when the client want to add new Item called IceCream, they just create new Factory called IceCreamFactory and create IceCream from that as shown below:

class IceCreamFactory extends ItemFactory{

    @Override
    Item getItem() {
        return new IceCream();
    }

}

class Client{
    public static void main(String[] args) {
        Item iceCream = new IceCreamFactory().getItem();
        System.out.println(iceCream);

    }
}

Is my understanding correct? We satisfied Open closed principle here, but for each product(Item) we need one Factory class, does not it become manageable nightmare?

NOTE: Article I was referring https://www.codeproject.com/Articles/1135918/Factory-Patterns-Factory-Method-Pattern?msg=5380215#xx5380215xx

Cimmerian answered 30/9, 2017 at 8:53 Comment(1)
Simple Factory violates OCP. If you go for Simple Factory, you will end up with adding dozens of if-else's (in it's simplest form) and I think that it wouldn't be the best practice. I would rather choose Factory Method, in that caseSorites
D
5

Your understanding actually is correct, just you need to note that every design pattern is made to solve at least one single issue, sometimes it could bring with it some other complexities or side effects, which means there is no perfect design pattern that could solve every problems.

For learning purposes you apply design patterns one by one (that makes the real power of design patterns sames to be shy or hiding), however in a real world context design patterns are mixed together (or even you can invent a new one :p) for the purpose of creating something that satisfy most your needs and becomes near to ideal, you can meet for example the Builder pattern mixed with Factory pattern or Factory pattern with Strategy pattern, or even the three of them mixed together...

For your case here I propose for example to use Factory Method Pattern together with Simple Factory Pattern combined with Dependency Injection Pattern to create something totally beautiful and at the same time satisfying the Open closed principal.

class ItemFactoryContainer() {

    private Map<String, ItemFactory> factories = new HashMap<>();

    public void register(String name, ItemFactory factory) {
        factories.put(name, factory);
    }

    public ItemFactory getFactory(String name) {
        return factories.get(name);
    }
}

public class ItemFactoryTest {

    public static void main(String...args) {

        ItemFactoryContainer factoryContainer = new ItemFactoryContainer();

        factoryContainer.register("choclate", new ChocolateFactory ());
        factoryContainer.register("cake", new CakeFactory ());
        factoryContainer.register("iceCream", new IceCreamFactory  ());

        Chocolate choclate = factoryContainer.getFactory("choclate").getItem();
        Cake cake = factoryContainer.getFactory("cake").getItem();
    }
}
Disabuse answered 30/9, 2017 at 9:48 Comment(4)
Great , Your clearly mean Factory Method becomes difficult to manage some times. And one question in your example , don't you think tomorrow I want to add one more Item called Donuts , then in ItemFactoryTest I need to add/change factoryContainer to take Donuts factoryCimmerian
What i meant is some design patterns could bring some other side effects to solve a problem as increasing the number of classes hence increasing the size of the code source for example...Disabuse
Adding a Donuts Item is simple as factoryContainer.register("Donuts", new DonutsFactory ()); which is not violating the open/close principale (this is considered extension not modification actually), in a real world application probably u will manage the registration part using Spring framework via XML configuration.Disabuse
@MouadELFakir that is exactly modification, non extensionCornice
B
1

I think in your example a factory seem useless because your Cake and Chocolate constructors don't need any args.

A factory is useful when you want to construct a complex object with many args to respect the DIP (Dependency Inversion Principle) and avoid embarrassing couplings.

In addition, your example seems to violate the LSP (Liskov Substitution Principle). Cake and Chocolate classes have nothing in common, and as evidence, your abstract super class is named Item which is a very generic name.

For me, with complex uncommon objects, the choice of one factory by class is better, and effectively respect the OCP (Open Closed Principle).

Your first example can be useful to instantiate classes which inherits the same superclass, but using your classes as arg and reflection (using java.lang.reflect) to call the constructor not just using String evaluation. (Your IDE is not able to autocomplete that) and in case of no match don't return null, Throw an IllegalArgumentException, it's more correct.

Like this :

import java.lang.reflect.Constructor;

public class SimpleItemFactory {
    public <T extends Item> T createItem(Class<? extends Item> itemClass) {
        try {
            // construct your dependencies here
            // and inject them in the getConstructor
            Constructor<? extends Item> constructor = itemClass.getConstructor();
            return (T)constructor.newInstance();
        } catch (Exception e) {
            throw new IllegalArgumentException();
        }
    }
}

and use it like this :

class Client {
    public static void main(String[] args) {
        SimpleItemFactory factory = new SimpleItemFactory();
        IceCream iceCream = factory.createItem(IceCream.class);
        System.out.println(iceCream);
        Cake cake = factory.createItem(Cake.class);
        System.out.println(cake);
        Chocolate chocolate = factory.createItem(Chocolate.class);
        System.out.println(chocolate);
    }
}

If you have no dependencies you can implement the createItem() method as static directly in your abstract Item class.

See this book, it's a great resource : Head First Design Patterns

OCP is just Open for extension Close for modification Principle. If you need to open for modification your factory to add a new class (a new case to manage in a switch or a if), no, your factory is not "OCP".

You sould'nt calculate a tax in a constructor, a constructor should just... construct an object.. You could use a pattern like strategy to have many taxesCalculator and inject them into a constructor, with an IOC (Inversion of Control) mechanism like dependencies injection or in a simple way with.....a factory (it's acceptable), but not with a simple static method in your class.

I'm sorry for my bad english, it's difficult for me to answer to a complex question like this.

Blah answered 30/9, 2017 at 9:25 Comment(1)
Hi @françois-leporcq ,Sorry I am still not getting the point in your answer ,Is Factory Method Pattern follows open-Closed principle ? Lets consider the Item class has complex constructor logic like calculating tax in constructor ,then what should we need to do ? Go for factory method or simple Factory?Cimmerian
B
1

Answers (by @Mouad EL Fakir and @François LEPORCQ) are about the static factory. That is your first part of the code.

There are two more flavors namely Factory Method and Abstract Factory)

Second part of your code is using CakeFactory and ChocolateFactory, you are trying to use Factory Method (though your naming suggests Abstract Factory). The usage of factory method getXXX() here is incorrect. The purpose of these patterns is different. In your code you are not achieving anything but increasing number of classes. (though increased number of classes and parallel hierarchies is documented as cost associated with Factories)

I have answered a similar questions recently see if those answers are helpful

Role of Creator in Factory Pattern

What are the real benefits of using the Abstract Factory in the following example, instead of the factory method?

Bridgetbridgetown answered 1/10, 2017 at 1:1 Comment(2)
Can you explain in what is incorrect about the getXXX() method here?Cimmerian
@Cimmerian I have updated my answer to first question (Role of Creator in Factory Pattern) in response to similar request. Please read the updated part of the answer there it mentions use of getXXX() method. DO not wish to repeat it here.Bridgetbridgetown

© 2022 - 2024 — McMap. All rights reserved.