Strategy Pattern and Factory Pattern with Dota2 Heroes

In this post, I will introduce the strategy pattern and factory patterns using Dota2 Heroes as examples. Dota2 is a multiplayer online battle arena (MOBA) video game, a sequel to Defense of the Ancients (DotA), played in matches between two teams of 5 players. Each player independently controls a powerful character, known as a “hero”, with unique abilities and roles.

See code at Github repo (https://github.com/yangju2011/design-patterns).

Strategy Pattern

Definition

  • Defines a family of algorithms, encapsulates each one, and makes them interchangeable.
  • Lets the algorithms vary independently from clients that use it.

Code example

In this example, we will build a Character simulator of Dota2 Characters. Dota 2 is played in matches between two teams of 5 players. Each player independently controls a powerful character, known as a “hero”, with unique abilities and roles. There are other characters in Dota2 such as couriers that transport items from shops to heroes, and shop sellers from whom heroes can buy items. Each character (hero, courier, seller) exhibits different behaviors and attributes.

alt text

Character is an abstract class that has 2 behaviors to be initialized: MoveBehavior and AttackBebavior. It also 6 methods: move, attack, display, disable, setMoveBehavior, setAttackBehavior.

There are various concrete Character classes: SvenCharacter, SniperCharacter, CrystalMaidenCharacter, CourierCharacter, SellerCharacter that extends Character. These concrete classes have different behaviors.

Note that MoveBehavior and AttackBebavior are interface which encapsulates different types of behaviors.

 

Run CharacterSimulator.java.

Output:

Factory Pattern

Definition

Simple Factory: not actually a design pattern, but more of a programming idiom. It encapsulates the create method for object creation.

Factory Method: defines an interface for creating an object, and lets subclasses decide which class to instantiate.

Abstract Factory: provides an interface for creating families of related or dependent objects without specifying their concrete class

1. HeroSelector with Simple Factory

alt textIn this example, we want to build a HeroSelector that takes a selection of hero name in String, and print out some information about a selected hero.

A straightforward implementation is HeroSelector which contains a selectHero method:


This method includes conditional statement to instantiate various Hero objects:

There is also additional code to print out behaviors of a selected hero:

If we have new hero names, we will need to modify the conditional statements in HeroSelector, while the code about hero behaviors remains unchanged.

As the code shows, we know what is varying (object creation with new) and what isn’t (printing hero behaviors), so it is probably time to encapsulate the varying part.

We can use Factory to encapsulate the details of object creation, and move the conditional statements to a separate class SimpleHeroFactory, which contains createHero method.

The HeroSelectorWithSimpleFactory gets SimpleHeroFactory passed to its constructor:

The selectHero method uses the factory to create a concrete hero object:

createHero can be defined as a static method, as in SimpleHeroFactoryStatic. With a static method, we do not need to instantiate an object to use the create method as shown in HeroSelectorWithSimpleFactoryStatic.

By encapsulating the createHero method in SimpleHeroFactory, we decouple object creation from how an object is used. In this example, the object Hero created from SimpleHeroFactory is used in selectHero. There can be other classes that use the factory to get Hero for its properties and behaviors. By using Factory, we have only one place to make modifications when the implementation of createHero changes.

Output example:

2. HeroSelector with Factory Method

alt textSay we have two types of clients (users): paid subscription – Dota Plus, and unpaid – Regular. The two clients will create slighly different Hero objects when selecting a heroName. For example, Dota Plus may have additional tags [Dota Plus] for hero’s name and abilities.

Regular Crystal Maiden:

Dota Plus Crystal Maiden:

In this case, we make createHero an abstract method in original HeroSelector client, and then create HeroSelector subclasses for each different types of clients with their correponding createHero implementation. This is called the Factory Method pattern.

alt text

Factory method lets the subclass decide which class to instantiate at run time. The Creator class can be written without knowledge of the actual Product that will be created.

Output example:

It is easy to accommodate new changes when there is a new implementation for the factory method.

In heroFactoryMethodV2, we have a new client BattlePassHeroSelector which creates new types of Heros with the [Battle Pass] tag.

Battle Pass Crystal Maiden:

To add this new type of HeroSelector, we keep all the code in heroFactoryMethod unchanged, and only add a few new Hero classes and a new concrete implementation of the factory method:

3. HeroSelector with Abstract Factory

alt textNote that a Hero may have arbitrarily defined properties such as position. But in reality, we may want to ensure consistency in a Hero’s properties. For example, we may only allow the client to select among a set of predefined values for positionweapon and boots.

In this example, we are building a Hero with different roles: Carry and Support. Each role uses one set of HeroBuild that include family of objects such as PositionBootsWeapon, and a list of Item.

We are going to build a factory interface to create our HeroBuild. This interface creates families of related or dependent pbjects without specifying their concrete classes. This is called the Abstract Factory pattern.

We then implement createMethod in concrete HeroBuildFactory classes for Carry and Support:

The concrete HeroBuildFactory uses Factory Method to instantiate a family of objects for each HeroBuild.

Output example:

It is easy to accommodate new changes when there is a new concrete factory. In heroAbstractFactoryV2, we have a new factory for OffLaneHeroBuild. We create a new family of products for this factory. Everything else in the original abstract factory is not affected by this new addition of factory.

Note

When we use Factory Method or Abstract Factory pattern, we end up creating a lot more classes and files to encapsulate what varies, program to interface, and make our classes open for extension but closed for modification – all of these are OO principles. In practice, we need to weigh the benefits of clean code and the added work of creating and maintaining various classes.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.