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

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

  • сломались треугольные скобки в коде

  • Да, спасибо большое. Разломался хайлайтер, патчу буквально руками.

  • Я думаю, что Вы не правы. Давайте обсудим это.

  • Спасибо большое! Очень доходчиво и ясно объясняется! Всё-таки не зря старкрафт играл)))

  • Отличная статья, взял на заметку, т.к. планирую в будущем сделать онлайн игру. Но у начинающих джавистов, которые пытаются выучить шаблоны (это я про себя) – просто разрыв сознания случается от такого сложного кода. Так что пока добавлю в заметки до лучших времен.))

  • Алексей

    А нельзя ли здесь обойтись без класса CustomUnit, ведь мы и так имплеминтируем инт ерфейс IUnit?

  • Очень понравилась статья… пиши еще)))

Leave a reply:

Site Footer

Sliding Sidebar

About Me

About Me

Для кого этот блог?

Для тех кого интересуют современные интернет технологи, IT бизнес, стартапы, менеджмент, контроль качества, личная эффективность, мотивация. Здесь я буду писать о том, что в первую очередь будет интересно мне, о проблемах и решениях. О том что пригодилось мне, и возможно будет интересно Вам.

Что заставило меня создать его?

Желание совершенствоваться. Достигать успеха. Находить людей со схожими проблемами и задачами, вместе искать выходы и решения.

Немного о себе.

Мой первый серьезный опыт в IT это работа над desktop приложениями в компании «Эксперт-Софт». У истоков её стояли несколько амбициозных и талантливых молодых людей, с огнем в глазах и желанием работать «как майкрософт». То чем мы там занимались вполне могли бы сегодня назвать «стартапом». Рук было откровенно мало, поэтому приходилось заниматься всем: кодированием на Delphi, написанием скриптов на VBA, дизайном, вёрсткой и поддержкой вебсайта, работой над рекламной полиграфией, проектированием интерфейсов и БД. Работы было много, но запал был велик, команда очень разношерстная, гармонично дополняя друг-друга в решении нетривиальных задач. Благодаря тому что пришлось попробовать многое, постепенно вырисовалось понимание того чем хочется заниматься, и как. Софтверным программированием я был сыт по горло. Массы проблем десктопного софта в вебе просто не было, по определению. Зато был четкий фокус на дизайне, юзабилити, скорости. Поэтому когда пришла пора уходить из «Эксперт-Софт», я без всякого сожаления стал искать работу как разработчик для web. Поскольку городишко у нас не очень большой, выбор был практически предопределен. Так я стал работать в «Оникс-Системз», где и продолжаю работать поныне. За время работы в компании я как разработчик принимал участие в работе над несколькими десятками проектов. Несколько десятков проектов было сделано мною как фрилансером. Самым большим проектом в котором я сыграл роль менеджера, считаю свою семью. Также довольно большой проект мы сейчас поднимаем с командой разработчиков (на данный момент команда состоит из четырех php разработчиков, одного flex кодера и тестировщика). Отсюда, большой интерес к современным практикам и методологиям, разным подходам в управлении командой, повышению эффективности и качества работы. По мере сил, вдохновения и свободного времени, я буду писать об этом.

Если у Вас возникли какие-то вопросы ко мне лично, буду рад если Вы свяжетесь со мной:

e-mail:
skype: denis.sheremetov
Старый сайт, с музычкой и флешом

Прочая онлайновая деятельность: