Code Refactoring 18

Abstract Factory PatternIn this tutorial I end my Code Refactoring Tutorial. It was a big one and between it and my Design Patterns Video Tutorial you should be able to do most anything.

To end this tutorial, I’ll cover the Abstract Factory Pattern again to make sure you understand it. It is often considered a hard to understand pattern, but I’m sure you’ll get it after this video. The code that follows will help explain anything you miss in the video.

If you like videos like this, it helps to tell Google+

Code From the Video

The Basics of the Abstract Design Pattern
1. There is a Monster abstract class
2. The difference between the Monsters is the Objects 
	stored in Attack and Range
3. An interface is used to represent both options
	available for Attack and Range
4. 2 classes implement that Attack & Range interfaces
5. There is a MonsterFactory interface. It represents 
	monsters with different Attack & Range types
6. If I want a Vampire with a MediumAttack & MediumRange
	a Vampire Factory is made that assigns those classes
	to the Vampire objects Attack & Range attributes
7. The only thing left is the builder that receives a string
	If the String is a Vampire we pass the Vampire Factory
	into the Vampire class Object.
8. The Vampire class object assigns the Attack & Range Objects
	as the attributes for Attack & Range for every Vampire
	Object that is created.

// This class defines attributes and capabilities 
// for each monster

public abstract class Monster {

	private String name;
	
	MonsterAttackPower attackPower;
	MonsterAttackRange attackRange;
	
	abstract void makeMonster();
	
	public void checkIfVictimIsInRange(){
		
		System.out.println(getName() + " checks if victim is " + attackRange);
		
	}
	
	public void attackTheVictim(){
		
		System.out.println(getName() + " attacks the victim for " + attackPower);
		
	}
	
	public String toString(){
		
		String infoOnMonster = getName() + " attacks anything " + attackRange + 
				" for " + attackPower;
		
		return infoOnMonster;
		
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}

// Now since I defined these interfaces, anything
// that implements this interface can replace toString

interface MonsterAttackPower{
	
	public String toString();
	
}

// These replace toString for MonsterAttackPower

class BasicAttack implements MonsterAttackPower{
	
	public String toString(){
		
		return "10 in damage";
		
	}
	
}

class MediumAttack implements MonsterAttackPower{
	
	public String toString(){
		
		return "20 in damage";
		
	}
	
}

// Now I do the same for MonsterAttackRange

interface MonsterAttackRange{
	
	public String toString();
	
}

class BasicRange implements MonsterAttackRange{
	
	public String toString(){
		
		return "5 away";
		
	}
	
}

class MediumRange implements MonsterAttackRange{
	
	public String toString(){
		
		return "10 away";
		
	}
	
}

// Here I define the attributes for each monster
// and the methods that will define them

interface MonsterFactory{
	
	public MonsterAttackPower assignAttackPower();
	public MonsterAttackRange assignAttackRange();
	
}

// This defines the attack and range to assign to
// each Zombie created

class ZombieFactory implements MonsterFactory{

	public MonsterAttackPower assignAttackPower() {
		
		return new BasicAttack();
		
	}

	public MonsterAttackRange assignAttackRange() {
		
		return new BasicRange();
		
	}
	
}

//This defines the attack and range to assign to
//each Vampire created

class VampireFactory implements MonsterFactory{

	public MonsterAttackPower assignAttackPower() {
		
		return new MediumAttack();
		
	}

	public MonsterAttackRange assignAttackRange() {
		
		return new MediumRange();
		
	}
	
}

// A factory is sent into this class that will assign
// the right objects for attack and range to the Zombie

class Zombie extends Monster{
	
	// The type of attack & range to assign are past
	// into the constructor
	
	MonsterFactory monsterFactory;
	
	public Zombie(MonsterFactory monsterFactory) {
		this.monsterFactory = monsterFactory;
	}

	void makeMonster() {
		
		System.out.println("Making a Zombie");
		
		// These are stored in the Monster class
		
		attackPower = monsterFactory.assignAttackPower();
		attackRange = monsterFactory.assignAttackRange();
		
	}
	
}

// The same is done for the vampire

class Vampire extends Monster{
	
	// The type of attack & range to assign are past
	// into the constructor
	
	MonsterFactory monsterFactory;
	
	public Vampire(MonsterFactory monsterFactory) {
		this.monsterFactory = monsterFactory;
	}

	void makeMonster() {
		
		System.out.println("Making a Vampire");
		
		// These are stored in the Monster class
		
		attackPower = monsterFactory.assignAttackPower();
		attackRange = monsterFactory.assignAttackRange();
		
	}
	
}

// Now that I have Monsters defined, their individual attacks
// & ranges and I have a factory for making them I have to
// create a way to order them.

abstract class MonsterBuilder {
	
	protected abstract Monster makeMonster(String typeOfMonster);
	
	public Monster orderAMonster(String typeOfMonster){
		
		Monster theMonster = makeMonster(typeOfMonster);
		
		// Test out the methods for the Monster
		
		theMonster.makeMonster();
		theMonster.checkIfVictimIsInRange();
		theMonster.attackTheVictim();
		
		return theMonster;
		
	}
	
}

class OrderAMonster extends MonsterBuilder{

	protected Monster makeMonster(String typeOfMonster) {
		
		Monster theMonster = null;
		
		if(typeOfMonster.equals("Zombie")){
			
			// Create the factory that assigns the right attributes
			// to each Zombie
			
			MonsterFactory monsterFactory = new ZombieFactory();
			
			// Create a Zombie Monster that stores the Objects
			// specific for each zombie so they can be assigned
			// to this monster
			
			theMonster = new Zombie(monsterFactory);
			
			theMonster.setName("Zombie Bob");
			
		} else
			
			if(typeOfMonster.equals("Vampire")){
				
				// Create the factory that assigns the right attributes
				// to each Vampire
				
				MonsterFactory monsterFactory = new VampireFactory();
				
				// Create a Vampire Monster that stores the Objects
				// specific for each Vampire so they can be assigned
				// to this monster
				
				theMonster = new Vampire(monsterFactory);
				
				theMonster.setName("Vampire Paul");
				
			}
		
		return theMonster;
		
	}
	
}

class MonsterMakerTest{
	
	public static void main(String[] args){
		
		// Create a way to order new monsters
		
		MonsterBuilder monsterBuilder = new OrderAMonster();
		
		Monster zombie = monsterBuilder.orderAMonster("Zombie");
		
		// Makes a call to the toString method
		
		System.out.println(zombie + "\n");
		
		Monster vampire = monsterBuilder.orderAMonster("Vampire");
		
		System.out.println(vampire + "\n");
		
	}
	
}

7 Responses to “Code Refactoring 18”

  1. Tadeu says:

    Hey Derek, you could try to reduce the speaking that make things seems to obvious when this is not to all? Is kind of frustrating =/

    • Derek Banas says:

      Please send me any questions that you have. I’m happy to answer them. The reason I do that is because positive reenforcement helps some people. I know it isn’t the easiest stuff 🙂

  2. Neil H. says:

    Hi Derek I have been following along with your Reflection and Refactoring tutorials and have learned a lot, thanks.

    As practice I tried to implement both in my programs and have a specific question.

    I use a factory to build my board game tokens and put them in an arraylist. It works perfectly. The token factory uses reflection:

    public GamePieces makeToken (int i) {
    try{
    Constructor constructor;
    constructor = Class.forName( “com.doka.boardgamezoo.”+getType(i) ).getConstructor(Integer.TYPE);
    return (GamePieces) constructor.newInstance(i);
    }

    (getType just returns “player”, “enemy”, “boss” etc).

    Now my problem: I moved the call to the factory into an abstract final (utility) class – with a private constructor – which I use only to set up the initial game. And now I get a InvocationTargetException NullPointer error. Can Reflection only be called from concrete classes? Is there any pointer you could give, as to where to look for the error, with only this to go on?

    The call to the factory is simple (and works in the Controller class):
    private void setTokensFromFactory(Resources res) {
    tokenSet = new ArrayList(numberTokens);
    TokenFactory factory = new TokenFactory();
    for (int i = 0; i < numberTokens; i++) {
    tokenSet.add(factory.makeToken(i));
    }
    }

    (And you may have noticed I am on Android – but I assume this does not matter….famous last words.

    Cheers for any direction you can give.
    Neil

    • Neil H. says:

      Sorry – I meant I moved the call to a final (utility) class with only static methods….can’t be final and abstract.

    • Derek Banas says:

      You have a NullPointerException. Don’t worry about the InvocationTargetException. This occurs when you try to access or modify an object set as null. You’ll find the line this occurs on in the error message. I hope that helps

      • Neil H. says:

        Yeah, I know the line – it is the newInstance that is causing the problem I think. But I still cannot find a reason why it works in the main Class but not when pulled out into a utility Class. All my variables have values, the constructor has the right name etc. I think it even works for i=0 but then falls over.

        I will chalk it up to “trying to run before I can walk” lol.

        Thanks for your time anyway.
        Neil

  3. Rahul says:

    Wow this really cleared things up for me. So can you explain how does singleton come into the picture for abstract classes? Which is the object that is only being created once that necessitates a Singleton?

Leave a Reply

Your email address will not be published.

Google+