Android Design Patterns and Best Practice

4.7 (3 reviews total)
By Kyle Mew
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Design Patterns

About this book

Are you an Android developer with some experience under your belt? Are you wondering how the experts create efficient and good-looking apps? Then your wait will end with this book! We will teach you about different Android development patterns that will enable you to write clean code and make your app stand out from the crowd.

The book starts by introducing the Android development environment and exploring the support libraries. You will gradually explore the different design and layout patterns and get to know the best practices of how to use them together. Then you’ll then develop an application that will help you grasp activities, services, and broadcasts and their roles in Android development.

Moving on, you will add user-detecting classes and APIs such as gesture detection, touch screen listeners, and sensors to your app. You will also learn to adapt your app to run on tablets and other devices and platforms, including Android Wear, auto, and TV. Finally, you will see how to connect your app to social media and explore deployment patterns as well as the best publishing and monetizing practices.

The book will start by introducing the Android development environment and exploring the support libraries. You will gradually explore the different Design and layout patterns and learn the best practices on how to use them together. You will then develop an application that will help you grasp Activities, Services and Broadcasts and their roles in Android development. Moving on, you will add user detecting classes and APIs such as at gesture detection, touch screen listeners and sensors to our app. You will also learn to adapt your app to run on tablets and other devices and platforms, including Android Wear, Auto, and TV. Finally, you will learn to connect your app to social media and explore deployment patterns and best publishing and monetizing practices.

Publication date:
December 2016
Publisher
Packt
Pages
370
ISBN
9781786467218

 

Chapter 1. Design Patterns

Design patterns have long been considered some of the most reliable and useful approaches to solving common software design problems. Patterns provide general and reusable solutions to frequently occurring development issues, such as how to add functionality to an object without changing its structure or how best to construct complex objects.

There are several advantages to applying patterns, not least the way that this approach assists the developer in following best practices and how it simplifies the management of large projects. These benefits are achieved by providing overall software structures (patterns) that can be reused to solve similar problems. This is not to say that code can be simply cut and pasted from one project to another but that the concepts themselves can be used over and over again in many different situations.

There are a good many other benefits to applying programming patterns, all of which will be covered at some point in the book, but here are one or two that are worthy of mention now:

  • Patterns provide an efficient common language between developers working in teams. When a developer describes a structure as say, an adapter or a facade, other developers will understand what this means and will immediately recognize the structure and purpose of the code.

  • The added layers of abstraction that patterns provide make modifications and alterations to code that is already under development much easier. There are even patterns designed for these very situations.

  • Patterns can be applied at many scales, from the overall architectural structure of a project right down to the manufacturing of the most basic object.

  • The application of patterns can vastly reduce the amount of inline commentary and general documentation required, as patterns also act as their own description. The name of a class or interface alone can explain its purpose and place within a pattern.

The Android development platform lends itself nicely to the employment of patterns, as not only are applications created largely in Java, but the SDK contains many APIs that make use of patterns themselves, such as factory interfaces for creating objects and builders for constructing them. Simple patterns such as Singletons are even available as a template class type. In this book, we shall see not only how to put together our own, large-scale patterns but also how make use of these built-in structures to encourage best practice and simplify coding.

In this chapter, we will begin by taking a brief look at how the book as a whole will pan out, the patterns we will be using, the order in which we will approach them, and the demonstration app that we will be building to see how patterns can be applied in real-world situations. This will be followed by a quick examination of the SDK and which components will best assist us on our journey, in particular, the role that the support library provides, enabling us to develop for many platform versions at once. There is no better way to learn than actual experience, and so the rest of the chapter will be taken up by developing a very simple demonstration app and employing our first pattern, the factory pattern, and its associated abstract factory pattern.

In this chapter, you will learn the following:

  • How patterns are categorized and which patterns are covered here

  • The purpose of the book's demonstration app

  • How to target platform versions

  • What support libraries do

  • What a factory pattern is and how to construct one

  • How to follow a UML class diagram

  • How to test an app on both real and virtual devices

  • How to monitor an app during runtime

  • How to use simple debugging tools to test code

  •  What an abstract factory pattern is and how to use one

 

How this book works


The purpose of this book is to show how the application of design patterns can directly assist the development of Android applications. During the course of the book we will be concentrating on the development of a complete client-side mobile application, focusing particularly on when, why, and how patterns can and should be employed during Android development.

Historically, there has been a certain amount of disagreement as to what exactly constitutes a pattern. However, the 23 patterns laid out in the 1994 book Design Patterns by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, the so-called gang of four, are widely accepted as the definitive set and provide solutions to nearly all of the software engineering problems that we are likely to encounter, and it is for this reason that these patterns will form the backbone of the book. These patterns can be broken down into three categories:

  • Creational - Used for creating objects

  • Structural - Used for organizing groups of objects

  • Behavioral - Used for communication between objects

The practical nature of this book means that we will not be tackling these categories in the order they appear here; instead, we will explore individual patterns as and when they arise naturally during the development of our app, although this generally means starting with creating a structure first.

It would be difficult, clumsy, and unrealistic to incorporate all design patterns into a single application, so here will attempt to apply as many as might seem realistic. For those patterns we decide not to use directly, we will at least explore how we might have done so, and in every case give at least one practical example of how they are used.

Patterns are not written in stone, nor do they solve every possible problem, and towards the end of the book we will look at how, once we have a grasp of the subject, we can create our own patterns or adapt extant ones to suit the rare circumstances where none of the established patterns fit.

In short, patterns are not a set of rules but rather a series of well-worn paths leading from known problems to tested solutions. If you spot a shortcut along the way, then by all means use it. If you stick to it consistently, then you will have created a pattern of your own that is just as valid as the traditional ones that we will cover here.

The first few chapters of the book concentrate on UI design and introduce a few essential design patterns and how they work conceptually. From about Chapter 6, Activating Patterns, onward, we will start to apply these and other patterns to real-world examples, and to one application in particular. The final chapters concentrate on the final stages of development, such as adapting applications for differing devices, a task almost purpose-built for design patterns, reaching the widest possible market and how to monetize our apps.

Note

In case you are new to Android development, the instructions laid out in the first two or three chapters are given in detail. If you are already familiar with Android development, you will be able to skip these sections and concentrate on the patterns themselves.

Before we get stuck into our first patterns, it makes sense to take a closer look at the app we will be building during the course of the book, as well as the challenges and opportunities it presents.

 

What we will build


As mentioned earlier, throughout the course of this book, we will be building a small but complete Android application. It will be a good idea now to take a little look at what we will be building and why.

We will put ourselves in the position of an independent Android developer that has been approached by a potential client that runs a small business making and delivering fresh sandwiches to several local office buildings. There are several issues facing our client that they believe can be solved with a mobile app. To see what solutions an app might provide, we will break the situation down into three sections: the scenario, the problem, and the solution.

The scenario

The client runs a small but successful business making and then delivering fresh sandwiches to nearby office workers so that they can buy and eat them at their desks. The sandwiches are very good and, as a result of word-by-mouth advertising, are growing in popularity. There is a good opportunity for the business to expand, but there are some glaring inefficiencies in the business model that the client believes can be resolved with the use of a mobile app.

The problem

It is almost impossible for the client to anticipate demand. There are many occasions where too many of a particular sandwich are made, leading to wastage. Likewise, there are times where insufficient sandwich lines are prepared, leading to a loss in sales. Not only this, but the word-of-mouth advertising the customers provide limits the expansion of the business to a small geographical area. The client has no reliable way of knowing if it is worth investing in more staff, a motorbike to travel further afield, or even whether to open new kitchens in other parts of town.

The solution

A mobile app, provided free for all customers, not only solves these problems but makes available a whole new set of opportunities. Never mind that an app will solve the issues of unanticipated demand; we now have the chance to take this to a whole new level. Why just present the customer with a set menu when we can offer them the chance to construct their own personalized sandwich from a list of ingredients? Maybe they love the cheese and pickle sandwich our client already makes but fancy it with a slice or two of apple, or prefer mango chutney to pickle. Maybe they are vegetarian and prefer to filter out meat products from their choices. Maybe they have allergies. All of these needs can be met with a well-designed mobile app.

Furthermore, the geographical limitations of word-of-mouth advertising, and even local promotions such a as billboards or notices in local papers, gives no indication of just how successful a business might be on a larger stage. The use of social media, on the other hand, can give our client clear insights into current trends as well as spread the word to the widest possible audience.

Not only can our client now judge accurately the scope of their business but can also add entirely new features unique to the digital nature of modern life, such as the gamification of the app. Competitions, puzzles, and challenges can provide a whole new dimension to engaging customers and present a powerful technique to increasing revenue and market presence.

With the task ahead now a little clearer, we are now in a position to start coding. We will start with a very simple demonstration of the factory pattern, and on the way take a closer look at some of the features of the SDK that we will be finding useful along the way.

 

Targeting platform versions


To keep up with the latest technology, new versions of the Android platform are released frequently. As developers, this means we can incorporate the newest features and developments into our applications. The obvious drawback to this is the fact that only the very newest devices will be able to run this platform and these devices represent only a tiny proportion of the entire market. Take a look at this chart taken from the developer dashboard:

The dashboard can be found at developer.android.com/about/dashboards/index.html and contains this and other up-to-date information that is very useful when first planning a project.

As you can see, the vast majority of Android devices still run on older platforms. Fortunately, Android makes it possible for us to target these older devices while still being able to incorporate features from the most recent platform version. This is largely achieved through the use of the support library and by setting a minimum SDK level.

Deciding which platforms to target is one of the first decisions we will need to take, and although it is possible to change this at a later date, deciding early which features to incorporate and knowing how these will appear on older devices can greatly simplify the overall task.

To see how this is done, start a new Android Studio project, call it anything you choose, and select Phone and Tablet as the form factor and API 16 as the Minimum SDK.

From the list of templates, select Empty Activity and leave everything else as is.

Android Studio will automatically select the highest available SDK version as the target level. To see how this is applied, open the build.gradle (Module: app) file from the project pane and note the defaultConfig section, which will resemble the following code:

defaultConfig { 
    applicationId "com.example.kyle.factoryexample" 
    minSdkVersion 16 
    targetSdkVersion 25 
    versionCode 1 
    versionName "1.0" 
} 

This ensures that our project will compile correctly for this range of API levels, but if we were building an app that we intended to publish, then we would need to inform the Google Play store which devices to make our app available on. This can be done with the build.gradle modular file, like so:

minSdkVersion 21 
targetSdkVersion 24 

We would also need to edit AndroidManifest.xml file. For the example here, we would add the following uses-sdk element to the manifest node:

<uses-sdk 
    android:minSdkVersion="16" 
    android:targetSdkVersion="25" /> 

Once we have determined the range of platforms we wish to target, we can get on and see how the support library allows us to incorporate many of the latest features on many of the oldest devices.

 

The support library


When it comes to building backwards-compatible applications, the support library is undoubtedly our most powerful tool. It is in fact a series of individual code libraries that work by providing alternative classes and interfaces to those found in the standard APIs.

There are around 12 individual libraries and they do not only provide compatibility; they also include common UI components such as sliding drawers and floating action buttons that would otherwise have to be built from scratch. They can also simplify the process of developing for different screen sizes and shapes, as well as adding one or two miscellaneous functions.

Note

As we are developing with Android Studio, we should download the support repository rather than the support library as the repository is designed specifically for the studio, provides exactly the same functionality, and is more efficient.

In the example we are working on in this chapter, we will not be making any use of support libraries. The only one the project includes is the v7 appcompat library, which was added automatically for us when we started the project. We will be returning to support libraries often in the book, so for now, we can concentrate on applying our first pattern.

 

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:

  1. Locate the MainActivity.java file in the project view.

  2. Right-click it and create a New | Java Class of Kind Interface called Bread:

  3. Complete the interface as follows:

        public interface Bread { 
     
            String name(); 
            String calories(); 
        } 
    
  4. 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"; 
            } 
        } 
    
  5. 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:

  1. Open the activity_main.xml file in Text mode.

  2. 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" /> 
    
  3. Open the MainActivity.java file and edit the onCreate() 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.

 

Running and testing an app


There are an enormous number of Android devices available today, and they come in a huge variety of shapes and sizes. As developers, we want our applications to run on as many devices and form factors as possible, and we want to be able to do it with the minimum of coding. Fortunately, the Android platform is well suited to this challenge, allowing us to easily adapt layouts and to construct virtual devices to match any form factor we can imagine.

Tip

Google provide a very handy cloud-based app testing facility at firebase.google.com/docs/test-lab/

Obviously, virtual devices form an essential part of any testing environment, but that is not to say that is not very handy to simply plug in one of our own devices and test our apps on that. This is not only faster than any emulator, but as we shall see now, very simple to set up.

Connecting to a real device

As well as being faster than virtual devices, actual devices also allow us to test our apps in real-world situations.

Connecting a real device to our development environment requires two steps:

  1. Enable developer options on your handset. On some models, this can involve navigating to Settings | About phone and tapping on Build number seven times, which will then add Developer options to your settings. Use this to enable USB debugging and to Allow mock locations.

  2. It is more than likely that you will now be able to connect your device to your workstation via a USB or the WiFi plugin cable and for it to show up when you open Android Studio. If not, you may need to open the SDK Manager and install the Google USB driver from the Tools tab. On some rare occasions, you may have to download a USB driver from the device's manufacturer.

A real device can be very useful for quickly testing changes in the functionality of an app, but to develop how our app looks and behaves on a variety of screen shapes and sizes will mean that we will create some virtual devices.

Connecting to a virtual device

Android virtual devices (AVDs) allow developers to experiment freely with a variety of emulated hardware setups, but they are notoriously slow, can exhaust the resources of many computer systems, and lack many of the features present in actual devices. Despite these drawbacks, virtual devices are an essential part of an Android developers' toolkit, and by taking a few things into consideration, many of these hindrances can be minimized:

  • Strip your virtual device down to only the features your app is designed for. For example, if you are not building an app that takes photos, remove camera functionality from the emulator; one can always be added later.

  • Keep the memory and storage requirements of the AVD to a minimum. Another device can easily be created as and when an app needs it.

  • Only create AVDs with very recent API levels when you need to test for specifically new features.

  • Begin by testing on virtual devices with low screen resolution and density. These will run much faster and still allow you to test for different screen sizes and aspect ratios.

  • Try separating very resource demanding functions and testing them individually. For example, if your app utilizes large collections of high-definition images, you can save time by testing this functionality separately.

It is generally quicker to construct virtual devices to suit specific purposes rather than an all-purpose one to test all our apps on, and there is a growing number of third-party Android emulators available, such as Android-x86 and Genymotion, that are often faster and have more development features.

It is also worth noting that when testing for layouts only, Android Studio provides some powerful preview options that allow us to view our potential UIs on a wide number of form factors, SDK levels and themes, as can be seen in the next image:

For now, create a basic AVD to run and test the current project on. There is nothing really to test, but we are going to see how to monitor our app's behavior during runtime and how to use the debug monitor service to test output without having to use the device screen, which is not an attractive way to debug a project.

Monitoring devices

The following demonstration works equally well on either an emulated device or a real one, so select whichever is easiest for you. If you are creating an AVD, then it will not require a large or high density screen or a large memory:

  1. Open the project we just worked on.

  2. From the Tools | Android menu, enable ADB Integration.

  3. From the same menu, select Android Device Monitor, although this may well already be running.

  4. Now run the application on your connected device with the Android Monitor.

The Device Monitor is useful in several ways:

  • The Monitors tab can be used during runtime to view live system information such as how much memory or CPU time our app is using. This can be particularly helpful when we want to see what resources our apps are using when they are not running in the foreground.

  • The monitor can be set to collect a variety of data, such as method tracking and resource usage, and store these as files, which can be viewed in the Captures pane (which can usually be opened from the left-hand gutter).

  • Screenshots and videos of an app in action are very simple to capture.

  • LogCat is a particularly useful tool as it not only reports live on an app's behavior, but as we will see next, can also generate user-defined output.

Using a text view to test our factory pattern is a convenient but clumsy way to test our code for now, but once we start developing sophisticated layouts, it would soon become very inconvenient. A far more elegant solution is to use debug tools that can be viewed without affecting our UIs. The rest of this exercise demonstrates how to do this:

  1. Open the MainActivity.java file.

  2. Declare the following constant:

    private static final String DEBUG_TAG = "tag"; 
    
  3. Again, you may have to confirm the importing of android.util.Log;.

  4. Replace the line in the onCreate() method that sets the text of the text view with the following line:

    Log.d(DEBUG_TAG, bread); 
    
  5. Open the Device Monitor again. This can be done with Alt + 6.

  6. From the dropdown in the top-right of the monitor, select Edit Filter Configuration.

  7. Complete the resultant dialog, as seen here:

Running the app and testing our factory demo should produce an output in the logcat monitor similar to the one seen here:

05-24 13:25:52.484 17896-17896/? D/tag: Brioche
05-24 13:36:31.214 17896-17896/? D/tag: Baguette
05-24 13:42:45.180 17896-17896/? D/tag: Roll

Tip

You can, of course, still use System.out.println() if you like, and it will print out in the ADB monitor, but you will have to search for it among the other output.

Having seen how we can test our apps on both real and virtual devices and how we can use debug and monitoring tools to interrogate our apps during runtime, we can now move on to a more realistic situation involving more than a single factory and an output more sophisticated than a two-word string.

 

The abstract factory pattern


When making a sandwich, bread is only our first and most basic ingredient; we obviously need some kind of filling. In programming terms, this could mean simply building another interface like Bread but calling it Filling and providing it with its own associated factory. Equally, we could create a global interface called Ingredient and have both Bread and Filling as examples of this. Either way, we would have to do a fair bit of re-coding elsewhere.

The design pattern paradigm offers the abstract factory pattern as perhaps the most adaptable solution to this dilemma. An abstract factory is simply a factory that creates other factories. The added layer of abstraction that this requires is amply paid off when we consider how little the top-level control code in our main activity needs to be altered, if at all. Being able to modify low-level structures without affecting those preceding constitutes one of the major reasons for applying design patterns, and when applied to complex architectures, this flexibility can shave many weeks off development time and allow more room for experimentation than other approaches.

Working with more than one factory

The similarities between this next project and the last are striking, as they should be; one of the best things about patterns is that we can reuse structures. You can either edit the previous example or start one from scratch. Here, we will be starting a new project; hopefully that will help make the pattern itself clearer.

The abstract factory works in a slightly different way to our previous example. Here, our activity makes uses of a factory generator, which in turn makes use of an abstract factory class that handles the task of deciding which actual factory to call, and hence which concrete class to create.

As before we will not concern ourselves with the actual mechanics of input and output, but rather concentrate on the pattern's structure. Before continuing, start a new Android Studio project. Call it whatever you choose, set the minimum API level as low as you like, and use the Blank Activity template:

  1. We begin, just as we did before, by creating the interface; only this time, we will need two of them: one for the bread and one for the filling. They should look like this:

    public interface Bread { 
     
        String name(); 
        String calories(); 
    } 
     
    public interface Filling { 
     
        String name(); 
        String calories(); 
    } 
    
  2. As before, create concrete examples of these interfaces. Here, to save space, we will just create two of each. They are all almost identical, so here is just one:

    public class Baguette implements Bread { 
     
        @Override 
        public String name() { 
            return "Baguette"; 
        } 
     
        @Override 
        public String calories() { 
            return " : 65 kcal"; 
        } 
    } 
    
  3. Create another Bread called Brioche and two fillings called Cheese and Tomato.

  4. Next, create a class that can call on each type of factory:

    public abstract class AbstractFactory { 
     
        abstract Bread getBread(String bread); 
        abstract Filling getFilling(String filling); 
    } 
    
  5. Now create the factories themselves. First, BreadFactory:

    public class BreadFactory extends AbstractFactory { 
     
        @Override 
        Bread getBread(String bread) { 
     
            if (bread == null) { 
                return null; 
            } 
     
            if (bread == "BAG") { 
                return new Baguette(); 
            } else if (bread == "BRI") { 
                return new Brioche(); 
            } 
     
            return null; 
        } 
     
        @Override 
        Filling getFilling(String filling) { 
            return null; 
        } 
    } 
    
  6. And then, FillingFactory:

    public class FillingFactory extends AbstractFactory { 
     
        @Override 
        Filling getFilling(String filling) { 
     
            if (filling == null) { 
                return null; 
            } 
     
            if (filling == "CHE") { 
                return new Cheese(); 
            } else if (filling == "TOM") { 
                return new Tomato(); 
            } 
     
            return null; 
        } 
     
        @Override 
        Bread getBread(String bread) { 
            return null; 
        } 
    } 
    
  7. Finally, add the factory generator class itself:

    public class FactoryGenerator { 
     
        public static AbstractFactory getFactory(String factory) { 
     
            if (factory == null) { 
                return null; 
            } 
     
            if (factory == "BRE") { 
                return new BreadFactory(); 
            } else if (factory == "FIL") { 
                return new FillingFactory(); 
            } 
     
            return null; 
        } 
    } 
    
  8. We can test our code just as before, with a debug tag, like so:

    AbstractFactory fillingFactory = FactoryGenerator.getFactory("FIL"); 
    Filling filling = fillingFactory.getFilling("CHE"); 
    Log.d(DEBUG_TAG, filling.name()+" : "+filling.calories()); 
     
    AbstractFactory breadFactory = FactoryGenerator.getFactory("BRE"); 
    Bread bread = breadFactory.getBread("BRI"); 
    Log.d(DEBUG_TAG, bread.name()+" : "+bread.calories()); 
    

When tested, this should produce the following output in the Android monitor:

com.example.kyle.abstractfactory D/tag: Cheese :  : 155 kcal
com.example.kyle.abstractfactory D/tag: Brioche :  : 85 kcal

By the time we reach the end of the book, each ingredient will be a complex object in its own right, with associated imagery and descriptive text, price, calorific value, and more. This is when adhering to patterns will really pay off, but a very simple example like the one here is a great way to demonstrate how creational patterns such as the abstract factory allow us to make changes to our products without affecting client code or deployment.

As before, our understanding of the pattern can be enhanced with a visual representation:

Imagine we wanted to include soft drinks in our menu. These are neither bread nor filling, and we would need to introduce a whole new type of object. The pattern of how to add this is already laid out. We will need a new interface that would be identical to the others, only called Drink; it would utilize the same name() and calories() methods, and concrete classes such as IcedTea could be implemented along exactly the same lines as above, for example:

public class IcedTeaimplements Drink { 
 
    @Override 
    public String name() { 
        return "Iced tea"; 
    } 
 
    @Override 
    public String calories() { 
        return " : 110 kcal"; 
    } 
} 

We would need to extend our abstract factory with something like this:

abstract Drink getDrink(String drinkType); 

We also, of course, need to implement a DrinkFactory class, but this too would have the same structure as the other factories.

In other words, we can add, delete, change, and generally muck around with the nuts and bolts of a project, without ever really having to bother with how these changes are perceived by the higher-level logic of our software.

The factory pattern is one of the most frequently used of all patterns. It can and should be used in many situations. However, like all patterns, it can be overused or underused, if not thought about carefully. When considering the overall architecture of a project, there are, as we shall see, many other patterns at our disposal.

 

Summary


We have covered quite a lot, considering that this is an introductory chapter. We've built examples of two of the best known and most useful design patterns and hopefully seen why they might be of use to us.

We began by looking at what patterns are and why we might use them in an Android setting. This was helped by taking a look at the development tools at our disposal, and how and why we can and should target specific platform versions and form factors.

We then applied this knowledge to create two very simple apps that employed basic factory patterns and saw how we can test and then retrieve data from an app running on any device, be it real or virtual.

This puts us in a great situation to take a look at other patterns and consider which to use when building a fully working app. This is something we will look at more closely in the next chapter, where we will introduce the builder pattern and how Android layouts are produced.

About the Author

  • Kyle Mew

    Kyle Mew has been programming since the early '80s and has written for several technology websites. Also, he has written three radio plays and four other books on Android development.

    Browse publications by this author

Latest Reviews

(3 reviews total)
The Packt website is well setup a.d easy to navigate.
Muito bom tecnicamente e interessante para novatos.
So many useful patterns to build a mcommerce app. This book is for an intermediate to advanced Android developer.
Book Title
Unlock this book and the full library for FREE
Start free trial