So let's talk about creational design patterns

Design patterns are present probably in all the interviews for software development related positions, sometimes even on the junior level.

|

Design patterns are present probably in all the interviews for software development related positions, sometimes even on the junior level. I spent a lot of time trying to determine and understand design patterns from all required perspectives. My conclusion after couple years of observation is that we need to know below checkpoints to be able to find, create and understand code which is using design patterns. I sometimes ask questions about design patterns on interviews and they are always related to below points. So what do we need to know about a design patterns in my opinion?

  • Main purpose of the pattern – by that I understand what I will gain if I decide to use that pattern in my solutions?
  • Code structure (Basic concepts like for example: which language elements am I going to use: interfaces, abstract classes, final variables - these are probably the most popular in terms of design patterns but not the only ones being used obviously).
  • Example of use case in well know standards, frameworks or libraries.
  • What is the group which particular pattern belongs to and if possible how to compare that pattern with alternatives at the same group?

I am going to go through 5 core creational design patterns now and try to find the answers for the questions above.

1. SINGLETON:

  • Singleton is the easiest and the most popular design pattern at all in my opinion.
    The pattern allows to create just single instance of an object in context of application, thanks for that we may save memory and restrict number of available objects on our application. There are also a lot of people saying that Singleton is actually anti-pattern what is partially true because the pattern is leading us in totally opposite direction that Objected Oriented Programing paradigm.
  • Core element of Singleton Pattern is class with private constructor (will not allow to instantiate object outside of the class). We have also to create construction method and keep singleton object as a static member of a class. It may look more or less like in the quick example below:
public class Singleton {

    private static Singleton singleton = new Singleton();

    private Singleton(){}

    public static Singleton getInstance(){
        return singleton;
    }

    public void sayHelloWorld(){
        System.out.println("Hello World!");
    }

    public static void main(String[] args) {
        Singleton object = Singleton.getInstance();
        object.sayHelloWorld();
    }
}

Console output:

Hello World!
  • Singletons are opposite of prototypes and this is probably the best way we can compare them with other creational patterns. Prototype will return a new instance each time, while Singleton will always return the same one.

 2. FACTORY

  • Factory is another creational design pattern. We should treat Factory in the same way like it is in real life. We have factories everywhere so T-Shirt factory in China is producing T-Shirts, Coca Cola Factory in Niepolomice is producing of course Coca Cola etc. In our code we are doing the same mapping but in abstract way.
  • There are only two things required to create useful code by following Factory Pattern. All we need is product and a place to produce that product – in short factory for that product. Let me give you the easiest example in code with Smartphones:

So here is a product, Smartphone:

public class Smartphone {

    public Smartphone(){
        System.out.println("Smartphone created!");
        memoryAssembly();
    }

    public Smartphone memoryAssembly(){
        System.out.println("memory assembled");
        return this;
    }
}

Now we need a place to produce it, Smartphones Factory: 
public class SmartphoneFactory {

    public static Smartphone getSmartphone(){
        return new Smartphone();
    }
}

Now we need a place to produce it, Smartphones Factory:

public class SmartphoneFactory {

    public static Smartphone getSmartphone(){
        return new Smartphone();
    }
}

Ok, we built factory, we know how to build the product. All remaining about it is to turn on such Factory:
public class Main {
    public static void main(String[] args){
        SmartphoneFactory.getSmartphone();
    }
}

Console output:

Smartphone created!
memory assembled
 
  • Factory pattern may be really often compared with Abstract Factory or Factory Method.
    Abstract Factory pattern will be described as the next one in my article so I will try to compare them both later. Comparison of Factory Pattern and Factory Method: Factory creates objects without exposing the instantiation logic to the client and Factory Method Defines an interface for creating an object, but let the subclasses decide which class to instantiate, so not only Smartphones would be available from such Factory Method.

3. ABSTRACT FACTORY:

  • Abstract Factory is pretty similar pattern like Factory Pattern. The only difference is another abstraction layer which we need to include into classes structure. Let’s get back to T-Shirt Factory. If we want to create Abstract Factory with shirts we should add another layer of abstraction. As a core I would suggest abstract Shirt factory. We will extend that with another three, more detailed, Factories. T-Shirt Factory as a first, Sleeveless Factory as a second one, Long Sleeves Factory as a third. They will all produce shirts, but every one of them in a specific way.
  •  Let’s start with the Product, our product would be Smartphone, and three different Smartphones brands:
public interface Smartphone {
    void sayHello();
}

public class Samsung implements Smartphone {
    
    @Override
    public void sayHello() {
        System.out.println("Hello from Samsung!");
    }
}

public class Huawei implements Smartphone {


    @Override
    public void sayHello() {
        System.out.println("Hello from Huawei!");
    }
}

public class Apple implements Smartphone {

    @Override
    public void sayHello() {
        System.out.println("Hello from Apple!");
    }
}

There are many different product so we had to create couple of factories for them:

public interface SmartPhoneFactory {
    Smartphone getInstance();
}

public class SamsungFactory implements SmartPhoneFactory {
    @Override
    public Smartphone getInstance() {
        return new Samsung();
    }
}

public class AppleFactory implements SmartPhoneFactory {
    @Override
    public Smartphone getInstance() {
        return new Apple();
    }
}

public class HuaweiFactory implements SmartPhoneFactory {

    @Override
    public Smartphone getInstance() {
        return new Huawei();
    }
}

Now let’s add another abstraction layer. We will use detailed Factory on demand, so we need to create abstract factory which will serve exact Factory for us:

public class AbstractSmartphoneFactory {

    public static SmartPhoneFactory getFactory(String name){
        if(name.equals("Samsung")){
            return new SamsungFactory();
        }else if(name.equals("Apply")){
            return new AppleFactory();
        }else if(name.equals("Huawei")){
            return new HuaweiFactory();
        }
        else{
            throw new RuntimeException("Type not supported");
        }
    }
}

Let’s run that program to see how it works:

public static void main(String[] args){
    SmartPhoneFactory huaweiFactory = AbstractSmartphoneFacto-ry.getFactory("Huawei");
    Smartphone smartphone = huaweiFactory.getInstance();
    smartphone.sayHello();
}

Console output:

Hello from Huawei!
  • Abstract Factory is often compared with Factory. I noticed that a lot of programmers have some problem with that difference which is in fact pretty simple. Abstract Factory pattern works like standard Factory but usually its adding some abstraction layer and groups couple of Factories which share the same assembly methods and just customize them in order to create them in more specific way.

4. BUILDER:

  •  Builder is design pattern which behaves in a little different way. The main purpose of builder is prevention of telescoping constructor what is actually well known as anti-pattern. I found once good car example. Could you imagine car being created at once at construction time? So the air condition is prepared together with wheels and engine? It will not work, the mess behind it will kill every car factory. The same in software development. We have builder pattern which should be able to operate on objects. Comparing that to the car it should first assembly core steel part of a car, then probably plugin the engine, wheels as a next method and as the last one it may be air condition or something like that.
  • Let’s go to McDonalds to check how builder pattern works. We usually go there to eat some sets, right? Every set in McDonalds contains Fries, that’s for sure ;) some drink, and usually sandwich (or Burger, call it as you want). I’ve created couple of classes representing that:
public class Item {
    protected String name;

    public Item(String name){
        this.name = name;
    }

    protected String getName(){
        return this.name;
    }
}

public class Sandwich extends Item {

    public Sandwich(String name){
        super(name);
    }
}

public class Drink extends Item {

    public Drink(String name) {
        super(name);
    }
}

We can definitely create Sets with above:

public class McDonaldsSet {

    private List<Item> items = new ArrayList<Item>();

    public McDonaldsSet(){
        items.add(new Item("Fries"));
    }

    public void addItemToSet(Item item){
        items.add(item);
    }

    public void check(){
        System.out.println("This is McDonalds Set:");
        for (Item item : items){
            System.out.println("Element inside McDonald Set: " + item.getName());
        }
    }
}

Now let’s check builder for such Sets:

public class McDonaldsSetBuilder {

    public McDonaldsSet prepareBigMac(){
        McDonaldsSet set = new McDonaldsSet();
        set.addItemToSet(new Drink("Coke"));
        set.addItemToSet(new Sandwich("BigMac"));
        return set;
    }

    public McDonaldsSet prepare2ForU(){
        McDonaldsSet set = new McDonaldsSet();
        set.addItemToSet(new Drink("Fanta"));
        set.addItemToSet(new Sandwich("ChickenBurger"));
        return set;
    }
}

Let’s check how such program will work, it’s pretty easy. We can see there is not big constructors, not a lot of variables we just use objective oriented programming to achieve beautiful Builder.

public class Main {

    public static void main(String... args) {
        McDonaldsSetBuilder builder = new McDonaldsSetBuilder();
        McDonaldsSet twoForYou = builder.prepare2ForU();
        twoForYou.check();
        McDonaldsSet bigMac = builder.prepareBigMac();
        bigMac.check();

    }
}

Console output:

This is McDonalds Set:

Element inside McDonald Set: Fries

Element inside McDonald Set: Coke

Element inside McDonald Set: ChickenBurger

This is McDonalds Set:

Element inside McDonald Set: Fries

Element inside McDonald Set: Coke

Element inside McDonald Set: BigMac
  • Builder pattern is really frequently compared with fluent interface. The biggest difference is that fluent interface can apply multiple properties to an object by connecting them with dots.
    And in terms of builder it orchestrates the building of something. That is, if you are building a car, the Builder would make sure that the steps from assembly the core steel to the time the car is leaving the factory are executed in the right order with the right data by the right builder.

5. PROTOTYPE

  • Prototype – one more time I will try to explain it with cars. Cars are always getting prototyped before they are getting into production lines. This is obvious. Prototypes let us to understand how to construct such car, we can easily change some parts of prototype, apply our new ideas without a big costs etc. It helps to understand construction issues & concerns and generally simplifies whole production process. The same in software. Prototype pattern is used when the cost of creating an object is too big and it is easier and faster to copy an existing instance than created a new instance.
  • Prototype design pattern will be always really close to the Clonable interface, let’s take a look on the example with Cars:

As First I would like to introduce Car class which is providing clone method.

public class Car implements Cloneable {

    private String name;
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void drive(){
        System.out.println("Wow you drive " + this.name);
    }

    public Object clone() {
        Object clone = null;
        try {
            clone = super.clone();
        }
        catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
}

Let’s try to register some prototypes then:

public class CarPrototypes {

    private static Map<String, Car> carMap  = new HashMap<String, Car>();

    public static Car getCar(String carName) {
        Car cachedCar = carMap.get(carName);
        return (Car)cachedCar.clone();
    }

    public static void loadCache() {
        Car ferrari = new Car();
        ferrari.setName("Ferrari");
        carMap.put(ferrari.getName(),ferrari);

        Car lamborgini = new Car();
        lamborgini.setName("Lamborghini");
        carMap.put(lamborgini.getName(), lamborgini);

        Car masterati = new Car();
        masterati.setName("Maserati");
        carMap.put(masterati.getName(), masterati);
    }
}

As a final effect let’s kick off with such program:

public class Main {

    public static void main(String[] args){
        CarPrototypes.loadCache();
        Car car = CarPrototypes.getCar("Maserati");
        car.drive();
        Car car2 = CarPrototypes.getCar("Ferrari");
        car2.drive();
    }
}

Console output:

Wow you drive Maserati

Wow you drive Ferrari
  • As I said about Singleton, Prototype is opposite design Pattern. Moreover prototype is not creating any inheritance structure like for example Abstract Factory Pattern does. 

Micha

Michal is the passionate software engineer with large experience in fields of design and implementation of enterprise applications. He did some as coder, technical recruiter and software trainer. Self-development soul. Usually convincing people around that standard technology stack is not the worst for most of the use cases. After all of that mix of football and only good music.

michal.mytnik@j-labs.pl

Did you like this article?

So let's talk about creational design patterns