The factory pattern
The factory pattern is one of the most widely used creational patterns. As its name suggests, it makes things, or more precisely, it creates objects. Its usefulness lies in the way it uses a common interface to separate logic from use. The best way to see how this works is simply to build one now. Open the project we began a page or two previously, or start a new one. Minimum and target SDK levels are not important for this exercise.
Tip
Selecting an API level of 21 or higher allows Android Studio to employ a technology known as hot-swapping. This avoids having to completely rebuild a project each time it is run and vastly speeds up the testing of an app. Even if you intend to finally target a lower platform, the time hot-swapping saves makes it well worth your while lowering this target once the app is as good as developed.
We are going to build a very simple example app that generates objects to represent the different types of bread our sandwich builder app might offer. To emphasize the pattern, we will keep it simple and have our objects return nothing more sophisticated than a string:
- Locate the
MainActivity.java
file in the project view. - Right-click it and create a
New | Java Class
of KindInterface calledBread
: - Complete the interface as follows:
public interface Bread { String name(); String calories(); }
- Create concrete classes of
Bread
, like so:public class Baguette implements Bread { @Override public String name() { return "Baguette"; } @Override public String calories() { return " : 65 kcal"; } } public class Roll implements Bread { @Override public String name() { return "Roll"; } @Override public String calories() { return " : 75 kcal"; } } public class Brioche implements Bread { @Override public String name() { return "Brioche"; } @Override public String calories() { return " : 85 kcal"; } }
- Next, create a new class called
BreadFactory
that looks like this:public class BreadFactory { public Bread getBread(String breadType) { if (breadType == "BRI") { return new Brioche(); } else if (breadType == "BAG") { return new Baguette(); } else if (breadType == "ROL") { return new Roll(); } return null; } }
UML diagrams
The key to understanding design patterns lies in understanding their structure and how component parts relate to each other. One of the best ways to view a pattern is pictorially, and the Unified Modeling Language (UML) class diagrams are a great way to accomplish this.
Consider the pattern we just created expressed diagrammatically, like so:
With our pattern in place, all that is required is to see it in action. For this demonstration, we will make use of the TextView in our layout that the template generated for us and the onCreate()
method that is called every time our main activity is started:
- Open the
activity_main.xml
file in Text mode. - Add an
id
to the text view, like so:<TextView android:id="@+id/text_view" android:layout_width="match_parent" android:layout_height="wrap_content" />
- Open the
MainActivity.java
file and edit theonCreate()
method to match the following code:@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView textView = (TextView) findViewById(R.id.text_view); BreadFactory breadFactory = new BreadFactory(); Bread bread = breadFactory.getBread("BAG"); textView.setText(new StringBuilder() .append(bread.name()) .toString()); }
Tip
Depending on how you have Android Studio set up, you may have to import the TextView widget:
import android.widget.TextView;
. Usually, the editor will prompt you and import the widget with a simple press of Alt + Enter.
You can now test the pattern on an emulator or real device:
This may appear at first glance as an incredibly long-winded way to achieve a very simple goal, but therein lies the beauty of patterns. The added layers of abstraction allow us to modify our classes without having to edit our activity and vice versa. This usefulness will become more apparent as we develop more complex objects and encounter situations that require more than a single factory.
The example we created here is too simple to really require any testing, but now is as good a time as any to explore how we test Android apps on both real and virtual devices, as well as how we can monitor performance and use debugging tools to test output without having to add unnecessary screen components.