ООП для чайников. Паттерны проектирования. Паттерн абстрактная фабрика (Abstract factory)

Сегодня мы рассмотрим паттерн абстрактная фабрика, или фабрика, как его часто называют. Этот паттерн относится к группе порождающих и решает проблему создания группы объектов. Классический пример использования фабрики для предоставления приложению элементов интерфейса в зависимости от платформы. Например, приложение на linux будет создавать при помощи фабрики кнопки, поля и прочие элементы через фабрику, которая в свою очередь, по запросу будет возвращать элементы XWindows, а приложение на windows будет возвращать элементы win32. Таким образом программе становится не важна платформа, платформо-зависимая логика ляжет на плечи фабрики.

Паттерн абстрактная фабрика должен использоваться тогда когда:

  • Конкретный вариант требуемого поведения системы дают не отдельные объекты, а четко выраженное семейство связанных объектов
  • Объекты одного семейства должны использоваться вместе
  • Система должна кофигурироваться одним из семейств объектов

Вот как это выглядит на диаграмме:

В моем примере фабрики мы имеем три вида игровых юнитов Protos, Terran, Zerg (должно быть близко любому кто играл в StarCraft), и некоторое количество игровых юнитов (ботов). Боты создают солдатов нужного типа в зависимости от количества ресурсов добытых работягами, а также боты могут сражаться друг с другом теми юнитами, что были созданы.
Вот код примера, несколько многословный, в силу специфичности синтаксиса явы:

// IUnit.java
package patterns.factory.unit;
 
public interface IUnit {
 
	public int getGroundWeapon();
	public int getAirWeapon();
	public int getShield();
	public int getPrice();
	public String toString();
 
}
 
// CustomUnit.java
package patterns.factory.unit;
 
public class CustomUnit implements IUnit {
 
	@Override
	public int getAirWeapon() {
		return 0;
	}
 
	@Override
	public int getGroundWeapon() {
		return 0;
	}
 
	@Override
	public int getPrice() {
		return 0;
	}
 
	@Override
	public int getShield() {
		return 0;
	}
 
	public String toString() {
		return "a:" + getAirWeapon() + " g:" + getGroundWeapon() + " s:" + getShield() + " p:" + getPrice();
	}
 
}
 
// ProtosSoldier.java
package patterns.factory.unit;
 
public class ProtosSoldier extends CustomUnit implements IUnit {
 
	@Override
	public int getAirWeapon() {
		return 10;
	}
 
	@Override
	public int getGroundWeapon() {
		return 12;
	}
 
	@Override
	public int getPrice() {
		return 100;
	}
 
	@Override
	public int getShield() {
		return 10;
	}
 
}
 
// Terransoldier.java
package patterns.factory.unit;
 
public class TerranSoldier extends CustomUnit implements IUnit {
 
	@Override
	public int getPrice() {
		return 35;
	}
 
	@Override
	public int getAirWeapon() {
		return 5;
	}
 
	@Override
	public int getGroundWeapon() {
		return 8;
	}
 
	@Override
	public int getShield() {
		return 3;
	}
 
}
 
// ZergSoldier.java
package patterns.factory.unit;
 
public class ZergSoldier extends CustomUnit implements IUnit {
 
	@Override
	public int getAirWeapon() {
		return 0;
	}
 
	@Override
	public int getGroundWeapon() {
		return 6;
	}
 
	@Override
	public int getPrice() {
		return 25;
	}
 
	@Override
	public int getShield() {
		return 5;
	}
 
}
 
// IArmyFactory.java
package patterns.factory.army;
 
import patterns.factory.unit.IUnit;
 
public interface IArmyFactory {
 
	public IUnit createSoldier();
 
}
 
// ProtosArmy.java
package patterns.factory.army;
 
import patterns.factory.unit.IUnit;
import patterns.factory.unit.ProtosSoldier;
 
public class ProtosArmy implements IArmyFactory {
 
	@Override
	public IUnit createSoldier() {
		return new ProtosSoldier();
	}
 
}
 
// TerranArmy.java
package patterns.factory.army;
 
import patterns.factory.unit.IUnit;
import patterns.factory.unit.TerranSoldier;
 
public class TerranArmy implements IArmyFactory {
 
	@Override
	public IUnit createSoldier() {
		return new TerranSoldier();
	}
 
}
 
// ZergArmy.java
package patterns.factory.army;
 
import patterns.factory.unit.IUnit;
import patterns.factory.unit.ZergSoldier;
 
public class ZergArmy implements IArmyFactory {
 
	@Override
	public IUnit createSoldier() {
		return new ZergSoldier();
	}
 
}
 
// BotClient.java
package patterns.factory.client;
 
import java.util.ArrayList;
import java.util.Iterator;
 
import patterns.factory.army.IArmyFactory;
import patterns.factory.unit.IUnit;
 
public class BotClient {
 
	private ArrayList <IUnit> units;
	private IArmyFactory factory;
 
	public BotClient(IArmyFactory army) {
		factory = army;
		units = new ArrayList <IUnit> ();
	}
 
	public void createForMoney(int money) {
		Boolean hasMoney = false;
		units.clear();
		do {
			IUnit unit = factory.createSoldier();
			hasMoney = money - unit.getPrice() > 0;
			if(hasMoney){
				units.add(unit);
				money -= unit.getPrice();
			}
		} while(hasMoney);
	}
 
	public int getGroundWeapon() {
		int total = 0;
		Iterator  iter = units.iterator();
	    while (iter.hasNext()) {
	    	total += ((IUnit)iter.next()).getGroundWeapon();
	    }
	    return total;
	}
 
	public int getShield() {
		int total = 0;
		Iterator  iter = units.iterator();
	    while (iter.hasNext()) {
	    	total += ((IUnit)iter.next()).getShield();
	    }
	    return total;
	}
 
	public int unitsCount() {
		return units.size();
	}
 
	public Boolean battle(BotClient client) {
		System.out.println("Units: " + unitsCount() + " vs " + client.unitsCount());
		System.out.println("Attack: " + getGroundWeapon() + " vs " + client.getGroundWeapon());
		System.out.println("Shield: " + getShield() + " vs " + client.getShield());
		float damage = (float)client.getGroundWeapon() / (float)getShield();
		System.out.println("Damage: " + damage);
		float enemyDamage = (float)getGroundWeapon() / (float)client.getShield();
		System.out.println("Enemy damage: " + enemyDamage);
		return enemyDamage < damage;
	}
 
}
 
// TestApp.java
package patterns.factory;
 
import patterns.factory.army.ProtosArmy;
import patterns.factory.army.TerranArmy;
import patterns.factory.army.ZergArmy;
import patterns.factory.client.BotClient;
 
public class TestApp {
 
	public static void main(String[] args) {
		BotClient bot1 = new BotClient(new ProtosArmy());
		BotClient bot2 = new BotClient(new TerranArmy());
		BotClient bot3 = new BotClient(new ZergArmy());
		bot1.createForMoney(400);
		bot2.createForMoney(400);
		bot3.createForMoney(400);
		System.out.println(bot1.battle(bot2));
		System.out.println(bot1.battle(bot3));
	}
 
}

Результат работы примера:

Units: 3 vs 11
Attack: 36 vs 88
Shield: 30 vs 33
Damage: 2.9333334
Enemy damage: 1.0909091
false
Units: 3 vs 15
Attack: 36 vs 90
Shield: 30 vs 75
Damage: 3.0
Enemy damage: 0.48
false

Comments

comments


Bookmark and Share