Abstract Factory: Provide an interface for creating families of related or dependent objects without specifying their concrete classes. – Design Patterns: Elements of Reusable Object-Oriented Software
The quote above is from the the Gang of Four book and describes the intent of the Abstract Factory design pattern.
The original book goes into a lot more detail about how you can use the pattern but for this post I will try to explain it in simple terms using the Unity game engine.
What is the Abstract Factory?
Let’s assume we have a scenario where we want to display adverts in our game. Our game runs on Android and iOS but there is no existing advertising API built into the Unity game engine. We have discovered a native solution called AdMob but it requires us to write some separate code to call the native plugin on each platform.
This is a potential use case for the Abstract Factory because we can detect what platform the game is running on at the start and then use the Abstract Factory to create the family of components required to display advertisements for that platform.
An Example Using Unity
The following image shows a class diagram of how we can use the Abstract Factory pattern to create our advertising API.
In the next steps we will create some basic interfaces and classes that match the design from the class diagram. I will not go into detail about the structure of the Unity project because you can check it out on GitHub.
AbstractAdvertFactory
The AbstractAdvertFactory
class in an abstract class that defines the abstract methods CreateBannerAdvert()
and CreateVideoAdvert()
, returning an IBannerAdvert
and IVideoAdvert
object respectively.
public abstract class AbstractAdvertFactory
{
public abstract IBannerAdvert CreateBannerAdvert();
public abstract IVideoAdvert CreateVideoAdvert();
}
iOSAdvertFactory
The concrete subclass iOSAdvertFactory
returns the objects specific to the iOS platform.
public class iOSAdvertFactory : AbstractAdvertFactory
{
public override IBannerAdvert CreateBannerAdvert()
{
return new iOSBannerAdvert();
}
public override IVideoAdvert CreateVideoAdvert()
{
return new iOSVideoAdvert();
}
}
AndroidAdvertFactory
The concrete subclass AndroidAdvertFactory
returns the objects specific to the Android platform.
public class AndroidAdvertFactory : AbstractAdvertFactory
{
public override IBannerAdvert CreateBannerAdvert()
{
return new AndroidBannerAdvert();
}
public override IVideoAdvert CreateVideoAdvert()
{
return new AndroidVideoAdvert();
}
}
If we needed to port our game to another platform we could create a new subclass for that platform. For example WindowsAdvertFactory
.
Using the Abstract Factory
The client program will instantiate the appropriate factory using some code like the following.
public class Client : MonoBehaviour
{
public string _whatToMake = "iOS";
private AbstractAdvertFactory _advertFactory;
// Use this for initialization
void Start()
{
switch (_whatToMake)
{
case "iOS":
_advertFactory = new iOSAdvertFactory();
break;
case "Android":
_advertFactory = new AndroidAdvertFactory();
break;
default:
throw new Exception("Unable to find advert factory implementation");
}
var bannerAdvert = _advertFactory.CreateBannerAdvert();
Debug.Log(bannerAdvert);
bannerAdvert.Show();
bannerAdvert.Hide();
var videoAdvert = _advertFactory.CreateVideoAdvert();
Debug.Log(videoAdvert);
videoAdvert.Play();
videoAdvert.Stop();
}
}
Attaching this script to a Unity game object will create the iOSAdvertFactory
at runtime. This is because of the iOS
string in the _whatToMake
field. Changing value to Android
will create the AndroidAdvertFactory
.
This is just an example and I’m sure you could come up with better ways of detecting what platform is in use. You could even combine this with the Singleton creation pattern to make a single entry point to creating the Factory and only ever creating it once.
Concrete Family Objects
One thing you will notice is I have missed out the interface declarations and implementation details for the returned family objects. You can see more details in AbstractFactory folder of the GitHub project.
All you need to know is that your platform specific calls into native code would go in the returned concrete objects. For example AndroidBannerAdvert
and iOSBannerAdvert
.
Conclusion
The good thing about the Abstract Factory pattern is that our client code has an identical set of method calls for each family object.
Just make sure you plan your design well in advance because the pattern can become a pain when you need to add new family objects. The reason for this is because you need to amend each existing factory class and create new family objects.
Further Reading
More detailed discussion on the Gang of Four design patterns can be found in the following publications:
- Gamma, E. et al. 1994. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley.
- Freeman, E. et al. 2004. Head First Design Patterns. O’Reilly Media.
- Bevis, T. 2012. C# Design Pattern Essentials. AbilityFirst.