Started reading Effective Java (3rd edition) by Joshua Bloch

Started reading Effective Java (3rd edition) by Joshua Bloch

- 7 mins

Effective Java

I realized how little did I know about Java and software design and design patterns in general. I have a substantial base of knowledge so far but I am planning on building a much better and higher level acquaintance of the programming language of the web and programming in general.

My Github repo

I am implementing some patterns and learning useful skills in the meantime. The following link leads to my repo. I am sharing some code snippets here as well.

First snippets

The builder pattern

Let’s make a pizza! :D The builder pattern is recommended when there are 4 or more parameters in a class. It is antipattern to write a constructor with 1,2,3,4,…, N values. It is much simpler to create a builder that gives back an object of the class while setting the required parameters.

An advanced example can be a generic Pizza class with a Builder class inside it. It is considered useful to create the builder class outside the class we want to build but we are not doing so in this example:

package qbeer.github.io;

import java.util.EnumSet;
import java.util.Objects;
import java.util.Set;

public abstract class Pizza {

    public enum Topping {HAM, MUSHROOM, MOZZARELLA, ANCHOVY, CORN, PINEAPPLE};
    final Set<Topping> toppings;

    abstract static class Builder<T extends Builder<T>> {
        
        EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
        public T addTopping(Topping topping) {
            toppings.add(Objects.requireNonNull(topping));
            return self();
        }
        abstract Pizza build();
        protected abstract T self(); // protected so it must be implemented in each class accourdingly

    }

    public Pizza(Builder<?> builder){
        toppings = builder.toppings.clone();
    }

}

We should go through this class line by line. Each Pizza implementation will have a set of toppings and a constructor with a builder parameter that will use the topping set of the builder. The builder class is abstract static since it needs an implementation on its own in each class that extends the Pizza class. This builder as all builders must implement the parameters of the class it will eventually build. By default, one can add toppings to a pizza and the returned type T must correspond to the implemented Builder of that class, so that’s why it is generalized. An abstract build() method is going to build each pizza subclass and a self() function must return the implemented Builder not the one here, therefore, we can’t just implement return this; here.

Moving on to a HungarianStylePizza we shall extend this class above.

package qbeer.github.io;

import java.util.Objects;

public class HungarianPizza extends Pizza {

    private final boolean isWithPaprika;
    private final boolean isWithSourCream;
    public enum Thickness {THIN, MEDIUM, THICK}
    private final Thickness thickness;

    public static class Builder extends Pizza.Builder<Builder> {

        private boolean isWithPaprika = false;
        private boolean isWithSourCream = false;
        private Thickness thickness = Thickness.THICK;

        Builder withPaprika() {
            this.isWithPaprika = true;
            return this;
        }

        Builder withSourCream() {
            this.isWithSourCream = true;
            return this;
        }

        Builder thickness(Thickness thickness) {
            this.thickness = Objects.requireNonNull(thickness);
            return this;
        }

        @Override HungarianPizza build() {
            return new HungarianPizza(this);
        }

        @Override protected Builder self() {
            return this;
        }

    }

    private HungarianPizza(Builder builder) {
        super(builder);
        this.isWithPaprika = builder.isWithPaprika;
        this.isWithSourCream = builder.isWithSourCream;
        this.thickness = builder.thickness;
    }

    @Override public String toString() {
        return "HungarianPizza=[withPaprika=" + isWithPaprika + ",withSourCream=" + isWithSourCream +
                ",thickness=" + thickness.toString() + ",toppings=" + toppings.toString() + "]";
    }
}

This subclass of the Pizza class extends it by adding an enum Thickness to it and boolean isWithSourCream, isWithPaprika parameters. The parameters are private final since they are set in the constructor by the builder and not modified in the future. Therefore we are not in need of getter and setter functions.

The builder extends the Pizza.Builder<T extends Builder<T>> class where T equals the builder class of this subclass. So for the self() method, it is returned. It implements the fields of the class that it builds and sets default values to some of the parameters. The build() method is overridden by returning a HungarianPizza better known as kenyérlángos. The seld() method should return this since we would like to acquire the builder method for this class and not any other.

In the private constructor of the extended pizza class, the super(builder); class must be called first and after that, we can set the remaining fields as well with our extended builder class. I override the toString() method as well to be able to better see the result I get by building a Hungarian style pizza.

package qbeer.github.io;

public class Main {

    public static void main(String[] args) {

        HungarianPizza hungarianPizza1 = new HungarianPizza.Builder()
                .addTopping(Pizza.Topping.HAM)
                .addTopping(Pizza.Topping.MUSHROOM)
                .withPaprika()
                .withSourCream()
                .thickness(HungarianPizza.Thickness.MEDIUM)
                .build();

        HungarianPizza hungarianPizza2 = new HungarianPizza.Builder()
                .addTopping(Pizza.Topping.MOZZARELLA)
                .addTopping(Pizza.Topping.ANCHOVY)
                .withPaprika()
                .build();

        System.out.println(hungarianPizza1);
        System.out.println(hungarianPizza2);

    }
}

This way it is way easier to build a pizza with a bunch of extra toppings and optional toppings such as paprika or sour cream. We are able to decide the thickness and later on, we can implement a size variable as well. This way the class and the builder should be modified together. The output of this example is:

HungarianPizza=[withPaprika=true,withSourCream=true,thickness=MEDIUM,toppings=[HAM, MUSHROOM]]
HungarianPizza=[withPaprika=true,withSourCream=false,thickness=THICK,toppings=[MOZZARELLA, ANCHOVY]]

This is the first blog post in this series, I hope I can get through this book fast and efficiently.

@Regards, Alex

Alex Olar

Alex Olar

Christian, foodie, physicist, tech enthusiast

comments powered by Disqus
rss facebook twitter github gitlab youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora