Abstract Factory - Design Pattern Explanation

Design patterns like "Decorator", "Singleton" are common and be easily recognized. The "Builder" pattern is also recognizable whenever you use Java's StringBuilder class.

Some patterns which are commonly used by frameworks are not easily recognizable unless you are a framework author. Recently, I spent time trying to recognize and understand Abstract Factory design pattern.

In this post, we will look at "Abstract Factory" and explain it with an example. I consulted multiple resources, and ultimately to gain confidence, I had to rely upon The Gang of Four [1] design patterns book.

This post is intended as refresher for a reader who has read the Gang of Four chapter on Abstract Factory. This post presents an example which the reader can relate to in the modern world.

Abstract Factory is a Factory for Factories. To understand this, you will first have to understand the Factory Design pattern, which encapsulates creation of objects. Factory pattern is recognized when instead of using new SomeClass() we call SomeClass.createObject() static method. The advantage is SomeClass is independent of your code, it could be supplied as a dependency from someone else and you simply use the factory. The person controlling the factory can modify the object creation process.

For example, SomeClass.createObject() in version1, can be return new SomeClass(arg1) and in version2, it can change to return new SomeClass(arg1, arg2) with you as the caller, invoking the object creation entirely as SomeClass.createObject() unaffected by the change made by the creator of SomeClass.

Factory pattern is easy to understand. The next step comes in dealing with Abstract Factory.

Intent

Abstract Factory provides an interface for creating families of related or dependent objects without specifying the concrete classes.

Canonical Design

https://dl.dropbox.com/s/3o1opat3zd7c569/Screenshot%202016-07-11%2023.04.02.png

Factory is a class that defers the instantiation of the object to the subclass.Factory encapsulates the decision-making that figures out which specific subclass to instantiate. There are three different kinds of Factory patterns observable with respect to object instantiation.

Simple Factory.

The client directly uses a static method of a subclass, to instantiate an object.

Factory Pattern

The client uses a Factory class to create an object. A Factory, is a class that defers the instantiation of an object to the subclasses.Factory method creates only one product

Abstract Factory

Abstract Factory is a constructional design pattern that is used to create a family of related products.

Abstract Factory is applicable when the

  • System should be configured with one of multiple families of Products.

  • The family of related product objects is designed to to used together and we need to enforce this constraint.

Design Problem

In this problem, we are trying to design a "Operating System Installer" for Unix family of Operating Systems. We know there are two popular variants of Unix, there popular operating system with Linux kernel and related application stack, and there is BSD systems.

Each Operating System will consists of components like

  1. Bootloader

  2. Kernel

  3. Shell

  4. DisplayManager

  5. WindowManager

  6. Applications

The installer will have to abstract those components and help the client create an Unix operating system choice.

Correspondence with Canonical Design

https://dl.dropbox.com/s/wmnawrv6h3rxx4u/Screenshot%202016-07-12%2002.15.48.png

Let's look at each of these in some detail.

Product Interfaces

Starting with products, these are:

  • Bootloader

  • Kernel

  • Shell

  • DisplayManager

  • WindowManager

  • BaseApplications

We will have Interfaces for the products.

java/abstractfactory/IBootLoader.java (Source)

public interface IBootLoader {
    /**
     * Boot up the System Image.
     */
    void bootUp();
}

java/abstractfactory/IKernel.java (Source)

public interface IKernel {

    /**
     * Load the kernel on top of the system image.
     */
    void loadKernel();
}

java/abstractfactory/IShell.java (Source)

public interface IShell {
    void loadShell();
}

java/abstractfactory/IDisplayManager.java (Source)

public interface IDisplayManager {
    void installDisplayManager();
}

java/abstractfactory/IWindowManager.java (Source)

public interface IWindowManager {
    void installWindowManager();
}

java/abstractfactory/IBaseApplications.java (Source)

public interface IBaseApplications {
    void installApplications();
}

Concrete Products

Each of these can create many difference concrete products. For the different concrete products like

  • Bootloader

    • BSDBootLoader

    • LinuxBootLoader

  • Kernel

    • BSDKernel

    • Linux

  • Shell

    • BASH

    • CShell

  • DisplayManager

    • X11

    • WayLand

  • WindowManager

    • Gnome

    • KDE

  • BaseApplications

    • SystemVUnix

    • GNUApplications

    • ProprietaryApps

Let's denote these concrete products in code that can be instantiated.

java/abstractfactory/BSDBootLoader.java (Source)

public class BSDBootLoader implements IBootLoader{
    /**
     * Boot up the System Image.
     */
    @Override
    public void bootUp() {
        System.out.println("Booting: " + this.getClass().getSimpleName());

    }
}

java/abstractfactory/BSDKernel.java (Source)

public class BSDKernel implements IKernel {
    /**
     * Load the kernel on top of the system image.
     */
    @Override
    public void loadKernel() {
        System.out.println("Loading: " + this.getClass().getSimpleName());

    }
}

java/abstractfactory/Bash.java (Source)

public class Bash implements IShell {

    @Override
    public void loadShell() {
        System.out.println("Loading: " + this.getClass().getSimpleName());

    }
}

java/abstractfactory/CShell.java (Source)

public class CShell implements IShell {
    @Override
    public void loadShell() {
        System.out.println("Loading: " + this.getClass().getSimpleName());

    }
}

java/abstractfactory/GNUApplications.java (Source)

public class GNUApplications implements IBaseApplications{
    @Override
    public void installApplications() {
        System.out.println("Installing: " + this.getClass().getSimpleName());
    }
}

java/abstractfactory/Gnome.java (Source)

public class Gnome implements IWindowManager {
    @Override
    public void installWindowManager() {
        System.out.println("Installing: " + this.getClass().getSimpleName());

    }
}

java/abstractfactory/KDE.java (Source)

public class KDE implements IWindowManager {
    @Override
    public void installWindowManager() {
        System.out.println("Installing: " + this.getClass().getSimpleName());
    }
}

java/abstractfactory/Linux.java (Source)

public class Linux implements IKernel{
    /**
     * Load the kernel on top of the system image.
     */
    @Override
    public void loadKernel() {
        System.out.println("Loading: " + this.getClass().getSimpleName());

    }
}

java/abstractfactory/LinuxBootLoader.java (Source)

public class LinuxBootLoader implements IBootLoader {
    @Override
    public void bootUp() {
        System.out.println("Booting: " + this.getClass().getSimpleName());
    }
}

java/abstractfactory/ProprietaryApps.java (Source)

public class ProprietaryApps implements IBaseApplications{
    @Override
    public void installApplications() {
        System.out.println("Installing: " + this.getClass().getSimpleName());
    }
}

java/abstractfactory/SystemVUnix.java (Source)

public class SystemVUnix implements IBaseApplications{
    @Override
    public void installApplications() {
        System.out.println("Installing: " + this.getClass().getSimpleName());
    }
}

java/abstractfactory/WayLand.java (Source)

public class WayLand implements IDisplayManager{

    @Override
    public void installDisplayManager() {
        System.out.println("Installing: " + this.getClass().getSimpleName());
    }

}

java/abstractfactory/X11.java (Source)

public class X11 implements IDisplayManager{
    @Override
    public void installDisplayManager() {
        System.out.println("Installing: " + this.getClass().getSimpleName());
    }
}

Factories

The products are created by Factories

  • BSDFactory

  • LinuxFactory

  • UbuntuFactory

java/abstractfactory/BSDFactory.java (Source)

public class BSDFactory implements IUnixFactory {
    @Override
    public IBootLoader installBootLoader() {
        return new BSDBootLoader();
    }

    @Override
    public IKernel installKernel() {
        return new BSDKernel();
    }

    @Override
    public IShell installShell() {
        return new CShell();
    }

    @Override
    public IDisplayManager installDisplayManager() {
        return new X11();
    }

    @Override
    public IWindowManager installWindowManager() {
        return new KDE();
    }

    @Override
    public IBaseApplications installApps() {
        return new SystemVUnix();
    }
}

java/abstractfactory/LinuxFactory.java (Source)

public class LinuxFactory implements IUnixFactory {
    @Override
    public IBootLoader installBootLoader() {
        return new LinuxBootLoader();
    }

    @Override
    public IKernel installKernel() {
        return new Linux();
    }

    @Override
    public IShell installShell() {
        return new Bash();
    }

    @Override
    public IDisplayManager installDisplayManager() {
        return new X11();
    }

    @Override
    public IWindowManager installWindowManager() {
        return new Gnome();
    }

    @Override
    public IBaseApplications installApps() {
        return new GNUApplications();
    }
}

java/abstractfactory/UbuntuFactory.java (Source)

public class UbuntuFactory extends LinuxFactory {
    @Override
    public IDisplayManager installDisplayManager() {
        return new X11();
    }

    @Override
    public IBaseApplications installApps() {
        return new ProprietaryApps();
    }
}

Abstract Factory

The factories will implement an abstraction provided by the Abstract Factory

java/abstractfactory/IUnixFactory.java (Source)

public interface IUnixFactory {
    IBootLoader installBootLoader();
    IKernel installKernel();
    IShell installShell();
    IDisplayManager  installDisplayManager();
    IWindowManager installWindowManager();
    IBaseApplications installApps();
}

Client

The design is best understood from the view of the client which uses the Abstract Factory to the create the products.

java/abstractfactory/OperatingSystem.java (Source)

public class OperatingSystem {
    IUnixFactory unixFactory;

    public OperatingSystem(IUnixFactory unixFactory) {
        this.unixFactory = unixFactory;
    }

    /**
     * installerClient uses only the interfaces declared by AbstractFactory (IUnixFactory) and AbstractProduct
     * (IBootLoader, IKernel, IShell, IDisplayManager, IWindowManager, IBaseApplications) classes.
     */
    public void installerClient() {


        IBootLoader bootLoader = unixFactory.installBootLoader();
        IKernel kernel = unixFactory.installKernel();
        IShell shell = unixFactory.installShell();
        IDisplayManager displayManager = unixFactory.installDisplayManager();
        IWindowManager windowManager = unixFactory.installWindowManager();
        IBaseApplications applications = unixFactory.installApps();

        bootLoader.bootUp();
        kernel.loadKernel();
        shell.loadShell();
        displayManager.installDisplayManager();
        windowManager.installWindowManager();
        applications.installApplications();

    }

    /**
     * client will not know the
     * products the type of bootloader, kernel, shell, display, window manager or applications.
     * That is encapsulated in factory used by the client.
     *
     */
    private static void factoryClient(IUnixFactory factory) {
        OperatingSystem operatingSystem = new OperatingSystem(factory);
        operatingSystem.installerClient();
    }

    public static void main(String[] args) {
        IUnixFactory factory;

        factory = new LinuxFactory();
        factoryClient(factory);

        factory = new BSDFactory();
        factoryClient(factory);

        factory = new UbuntuFactory();
        factoryClient(factory);
    }

}

The execution looks like this.

Booting: LinuxBootLoader
Loading: Linux
Loading: Bash
Installing: X11
Installing: Gnome
Installing: GNUApplications

Booting: BSDBootLoader
Loading: BSDKernel
Loading: CShell
Installing: X11
Installing: KDE
Installing: SystemVUnix

Booting: LinuxBootLoader
Loading: Linux
Loading: Bash
Installing: X11
Installing: Gnome
Installing: ProprietaryApps

Tabulated Correspondence

Mapping of the code with various elements in the design helps us to appreciate this pattern.

https://dl.dropbox.com/s/ahu9pj89qtt7pos/Screenshot%202016-07-27%2008.57.29.png

Hope this was useful. If you have any comments on this article, please add your thoughts in the comments section of this article.

Thank you for reading!

A Week Of Violence - July 9, 2016

Police shooting of the black men were disturbing. There are videos on the internet, I suggest that you don't watch them, it just does not help. The men were shot for no-good reason and if I have take an alternate stance, it is probably because the police officer had fear and hatred towards the other person that he shot them.

There are no excuses for the police officers who shot the black men. They should be jailed for their life term for this act.

Later, the protest in Dallas to condemn the shooting, some people, who carried guns, which is legal in US, shot at the officers and killed 5 of them. This is equally brutal. In addition, police used a robot to kill the suspect too.

I had to recollect "Gandhi's message of peace" that many Indians have been thought since childhood. It seems to me that folks who cite, "Second Amendment" will really not understand either Gandhi's or Christ's message to mankind.

Movie Review: No Country For Old Men

My Rating: 4/5

There is a decent chance that you must have heard about this movie. I had heard the name too, had a vague idea from reading synopsis what I would expect. But I was totally wrong. After a point, I said to myself, it is no longer good vs. evil story.

It has a very thin storyline but has an excellent performance from the villain, gripping scenes that will keep you absorbed.

I won't the read book, but I enjoyed watching the movie.

Mesosphere Ahoy!

I joined @mesosphere on June 20, 2016. It's exciting as I get to work on some interesting technology in Datacenter Operating System. The container technology and orchestration world is in rage right now. Mesosphere is suitably positioned in that sphere. :)

The most interesting thing for me will be learn the distributed systems concepts well and contributed to this space. Plus, I discovered that our infrastructure tools are completely in Python3. Yay for that!

Documenting projects can reveal code/ design bugs.

Stumbled upon this LWN.net entry written by a Linux man-pages contributor. The main point of this article was, documenting software projects quite early on can reveal important design and code bugs. This presented three examples of how a feature in inotify call was introduced, but was found not working while documenting it. Similar example was given for splice and timerfd calls

Read the full article here: http://lwn.net/Articles/247788/

The Martian Way - Novella

Enjoyed reading "The Martian Way" story by Asimov. Takes place in a future setting where there are dwellers in mars, and Earth has decided to ration the supply of water to mars. Martians get worried, but a group, adventurous enough, decide to get the water from "elsewhere" in the galaxy.

Still thinking like an earthling?

Get out of your rut, open your mind—there’s a whole universe waiting. It’s waiting for people who aren’t afraid of thinking in new ways, of doing new things.

Can you imagine…

…mining the skies for water?

…building a new world beneath the surface of a strange planet?

…making pets out of alien explorers?

…putting your life in the hands of a teen-aged computer?

If you can, you’re well on your way to becoming a member of the space generation—you’re thinking The Martian Way.

Also, asimovreviews.net review for the martian way

There will be code

There is a wired story which predicted that advancements in artificial intelligence will result in programs getting trained rather than being written. This is keeping in line with machine learning advancements popular these days where the number of people who are doing "machine learning" need not learn "how to write the machine learning algorithms", instead they just need to learn "how to use machine learning algorithms" on data. They should be efficient at using "algorithms".

That was total alien concept to me until I saw a whole category and industry flourish with that concept. Given the background, the wired articles prediction was that programs will be trained instead of being written.

I found an alternate argument, from a credible and respectable source Clean Code By: Robert C. Martin It goes like this.

There Will Be Code

One might argue that a book about code is somehow behind the times—that code is no longer the issue; that we should be concerned about models and requirements instead. Indeed some have suggested that we are close to the end of code. That soon all code will be generated instead of written. That programmers simply won’t be needed because business people will generate programs from specifications.

Nonsense! We will never be rid of code, because code represents the details of the requirements. At some level those details cannot be ignored or abstracted; they have to be specified. And specifying requirements in such detail that a machine can execute them is programming. Such a specification is code.

I expect that the level of abstraction of our languages will continue to increase. I also expect that the number of domain-specific languages will continue to grow. This will be a good thing. But it will not eliminate code. Indeed, all the specifications written in these higher level and domain-specific language will be code! It will still need to be rigorous, accurate, and so formal and detailed that a machine can understand and execute it.

The folks who think that code will one day disappear are like mathematicians who hope one day to discover a mathematics that does not have to be formal. They are hoping that one day we will discover a way to create machines that can do what we want rather than what we say. These machines will have to be able to understand us so well that they can translate vaguely specified needs into perfectly executing programs that precisely meet those needs.

This will never happen. Not even humans, with all their intuition and creativity, have been able to create successful systems from the vague feelings of their customers. Indeed, if the discipline of requirements specification has taught us anything, it is that well-specified requirements are as formal as code and can act as executable tests of that code!

Remember that code is really the language in which we ultimately express the requirements. We may create languages that are closer to the requirements. We may create tools that help us parse and assemble those requirements into formal structures. But we will never eliminate necessary precision—so there will always be code.

The Man Who Knew Infinity

"An equation for me has no meaning, unless it represents a thought of God." - Srinivasa Ramanujan

"The man who knew infinity" is the story of Srinivasa Ramanujan. This phrase, associated with Ramanujan should be familiar to many due to a very popular book with the same title. There is a motion picture directed by Matt Brown based upon this book.

I watched his movie on a Saturday at Berkeley. I was excited to listen to the QA with the director, and Mathematicians such as Richard Borcheds , Olga Holz and Ken Ribet following the screening.

In India, we have been exposed to Ramanujan quite early in our lives. I had stories in school book about great Indian mathematicians like Aryabhata, Brahmangupta, and Bhaskaracharya. Indian Satellites to Space launched by ISRO bear their names too. Along with those personalities, we have Srinivasa Ramanujan and whom we know for his invention of Magic Squares, and Ramanujan Number, the smallest number expressible as sum of two cubes.

\begin{equation*} 1729 = 1^3 + {12}^3 = 9^3 + 10^3 \end{equation*}

Beyond that, I had not known much about the significance of this number or Ramanujan's work.

Years later, when taking online courses, I had come to know about Hardy and Ramanujan's research in Number Theory being at the core of secure communication and cryptography. Do you use Credit Cards online and feel confident that someone will not maliciously take your money? All this is was because of the research in number theory.

There is also famous anecdote associated with Ramanujan, known to many from South India, Ramanujan prayed to a goddess, and she gave inspiration for his work. He mentions that his theorems had come up like a dream, a boon granted by the Goddess, and he would write formulae in his book. That's how he invented those theorems.

If it happened like that, imagine how excited young students from Madurai will be, where there a temple every hundred feet. :) It needs to be clarified that, Ramanujan was a genius and he also worked very hard on his subjects.

A better understanding of this phenonmemon, in general, is now known. The style of discovery is called " diffused mode learning", wherein after an intense work on challenging problem, the solution suddenly comes up during a restful time.

All these are portrayed well in this movie. The relationship between Hardy and Ramanujan, the scientific culture at Trinity College, London revealed in real detail. The movie has a significant portion dealing with how Hardy mentors Ramanujan, and strives to bring his work to the modern world.

In the question and answer session that followed, two questions were of interest to me.

How do mathematicians study Ramanujan's work when he has not left many formula proofs with his equation?

Richard Borcheds, who is an accomplished mathematician, replied that Ramanujan's work was published in the form of series of notebooks. He left behind three notebooks containing almost 3000 theorems, virtually all without proof. The reason he could have done that is perhaps he grew up in a time when the paper was very expensive for him and he wanted to be economical.

(It did not answer the question, but provided a good perspective).

Question 2: In the movie, Ramanujan is shown to be desisting writing formal proofs. Is that true?

Richard Borcheds shared that, it is bit exaggerated in the movie. Ramanujan always knew the proof of his work and could state it if he wanted to. But he usually did not.

I enjoyed watching this movie and listening to the perspectives associated with the genius from kumbakonam.

Oxford Comma

The Oxford comma is the final comma in a list of items mentioned in a sentence. In this statement:

I like computer science, maths, and programming.

The comma after the word "maths" and before the conjunction "and" is considered the Oxford comma. Other commonly used terms for this style are the serial comma, series comma, or Harvard comma.

Before writing this essay, I had never added a comma to the final element of a conjunction. This topic piqued my interest, and I started researching this entity.

The term "Oxford comma" got its name from the Oxford University Press style guide. It was recommended by the Oxford style guide even when many British journals and publications did not recommend it. My first conscious encounter with it was during a diagnostic test. I was unable to correct a sentence by placing a serial comma. I had never considered a statement with a list of items wrong if it did not have a serial comma. We always try to infer the meaning from the context. But in fact, as we will see shortly, it may not always be possible. The reason to include the Oxford comma is to reduce ambiguity in a sentence.

The reason we often do not notice the lack of an Oxford comma is that the Associated Press style guide, which many newspapers follow, does not require its use. Thus, it is often left out. Let's examine an ambiguity that arises when the Oxford comma is omitted.

To my parents, Alicia and Steve Jobs.

There is ambiguity about the writer's parentage here because "Alicia and Steve Jobs" can be read as being in apposition to "my parents," leading the reader to believe that the writer claims Alicia and Steve Jobs are his parents. Placing the Oxford comma after "Alicia" resolves the ambiguity:

To my parents, Alicia, and Steve Jobs.

This sentence clearly articulates that the writer is referring to three separate entities in the dedication. However, there are cases where the inclusion of the Oxford comma can create ambiguity. For example, consider this statement:

To my father, Steve Jobs, and Alicia.

The serial comma after "my father" creates ambiguity about the writer's father because it uses punctuation identical to that used for an appositive phrase, suggesting that Steve Jobs is the writer's father. It is unclear whether there are three people (1. my father; 2. Steve Jobs; and 3. Alicia) or only two (1. Steve Jobs and 2. Alicia, with Steve Jobs being the writer's father). A common way to disambiguate this sentence is to refer to each entity explicitly. For example:

To my father, to Steve Jobs, and to Alicia.

Thus, the placement of the comma can significantly affect the meaning of an entire sentence. By appropriately choosing words and consistently using the Oxford comma, we can write less ambiguous phrases. Additionally, the Oxford comma often matches the spoken cadence of sentences better.