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

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

Site Footer

Sliding Sidebar

About Me

About Me

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

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

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

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

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

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

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

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

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