ООП для чайников. Паттерны проектирования. Паттерн абстрактная фабрика (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

Site Footer

Sliding Sidebar

About Me

About Me

For whom this blog for?

For those who are interested in modern Internet technologies, IT business, startups, management, quality control, personal effectiveness, motivation. Here I write about what is interesting, about problems I faced and solutions I found. I hope it will be interesting to you either.

What motivates me to write?

The desire to improve, to study deeper topics that interest me. Find people with similar problems and tasks, together look for ways out and solutions.

Feel free to contact if you have anything to say to me

Old Flash site with my artistic works and misuc.