Flyweight 模式是结构设计模式之一,因为该模式提供了减少对象数量从而改进应用程序所需对象结构的方法。当我们需要创建大量相似的对象(比如 10 5 )时,使用享元模式。享元对象的一个重要特性是它们是不可变的。这意味着它们一旦被构造就不能被修改。
为什么我们关心程序中的对象数量?
- 更少的对象减少了内存使用量,它设法让我们远离与内存相关的错误,如Java.lang.OutOfMemoryError。
-
尽管在Java创建对象确实很快,但我们仍然可以通过共享对象来减少程序的执行时间。
在享元模式中,我们使用 HashMap 存储对已创建对象的引用,每个对象都与一个键相关联。现在,当客户想要创建一个对象时,他只需要传递一个与之关联的键,如果该对象已经创建,我们只需获取对该对象的引用,否则它会创建一个新对象,然后将其引用返回给客户端.
内在和外在状态
为了理解内在和外在状态,让我们考虑一个例子。
假设在一个文本编辑器,当我们输入一个字符,创建字符类的一个对象,字符类的属性{名称,字体,大小}。我们不需要每次客户端输入一个字符都创建一个对象,因为字母 ‘B’ 与另一个 ‘B’ 没有区别。如果客户端再次输入“B”,我们只需返回我们之前已经创建的对象。现在所有这些都是内在状态(名称、字体、大小),因为它们可以在不同的对象之间共享,因为它们彼此相似。
现在我们向字符类添加更多属性,它们是行和列。它们指定字符在文档中的位置。现在即使对于相同的字符,这些属性也不会相似,因为没有两个字符在文档中具有相同的位置,这些状态被称为外在状态,并且它们不能在对象之间共享。
实施:我们在反恐精英游戏中实施恐怖分子和反恐精英的创建。因此,我们有2类一个对于T errorist(T)和其它对C ounterŤerrorist(CT)。每当玩家要求武器时,我们都会为他分配所要求的武器。在任务中,恐怖分子的任务是放置炸弹,而反恐怖分子的任务是散布炸弹。
为什么在这个例子中使用享元设计模式?这里我们使用 Fly Weight 设计模式,因为这里我们需要减少玩家的对象数量。现在我们有 n 个玩家在玩 CS 1.6,如果我们不遵循 Fly Weight 设计模式,那么我们将不得不创建 n 个对象,每个玩家一个。但是现在我们只需要创建 2 个对象,一个用于恐怖分子,另一个用于反恐怖分子,我们将在需要时一次又一次地重复使用。
内在状态:这里的“任务”是两种类型玩家的内在状态,因为这对于 T/CT 来说总是相同的。我们可以有一些其他状态,比如它们的颜色或任何其他属性,这些属性对于它们各自的 Terrorists/Counter Terrorists 类中的所有 Terrorists/Counter Terrorists 都相似。
外在状态:武器是外在状态,因为每个玩家都可以携带他/她选择的任何武器。武器需要由客户端本身作为参数传递。
类图:
// A Java program to demonstrate working of
// FlyWeight Pattern with example of Counter
// Strike Game
import java.util.Random;
import java.util.HashMap;
// A common interface for all players
interface Player
{
public void assignWeapon(String weapon);
public void mission();
}
// Terrorist must have weapon and mission
class Terrorist implements Player
{
// Intrinsic Attribute
private final String TASK;
// Extrinsic Attribute
private String weapon;
public Terrorist()
{
TASK = "PLANT A BOMB";
}
public void assignWeapon(String weapon)
{
// Assign a weapon
this.weapon = weapon;
}
public void mission()
{
//Work on the Mission
System.out.println("Terrorist with weapon "
+ weapon + "|" + " Task is " + TASK);
}
}
// CounterTerrorist must have weapon and mission
class CounterTerrorist implements Player
{
// Intrinsic Attribute
private final String TASK;
// Extrinsic Attribute
private String weapon;
public CounterTerrorist()
{
TASK = "DIFFUSE BOMB";
}
public void assignWeapon(String weapon)
{
this.weapon = weapon;
}
public void mission()
{
System.out.println("Counter Terrorist with weapon "
+ weapon + "|" + " Task is " + TASK);
}
}
// Class used to get a player using HashMap (Returns
// an existing player if a player of given type exists.
// Else creates a new player and returns it.
class PlayerFactory
{
/* HashMap stores the reference to the object
of Terrorist(TS) or CounterTerrorist(CT). */
private static HashMap hm =
new HashMap();
// Method to get a player
public static Player getPlayer(String type)
{
Player p = null;
/* If an object for TS or CT has already been
created simply return its reference */
if (hm.containsKey(type))
p = hm.get(type);
else
{
/* create an object of TS/CT */
switch(type)
{
case "Terrorist":
System.out.println("Terrorist Created");
p = new Terrorist();
break;
case "CounterTerrorist":
System.out.println("Counter Terrorist Created");
p = new CounterTerrorist();
break;
default :
System.out.println("Unreachable code!");
}
// Once created insert it into the HashMap
hm.put(type, p);
}
return p;
}
}
// Driver class
public class CounterStrike
{
// All player types and weapon (used by getRandPlayerType()
// and getRandWeapon()
private static String[] playerType =
{"Terrorist", "CounterTerrorist"};
private static String[] weapons =
{"AK-47", "Maverick", "Gut Knife", "Desert Eagle"};
// Driver code
public static void main(String args[])
{
/* Assume that we have a total of 10 players
in the game. */
for (int i = 0; i < 10; i++)
{
/* getPlayer() is called simply using the class
name since the method is a static one */
Player p = PlayerFactory.getPlayer(getRandPlayerType());
/* Assign a weapon chosen randomly uniformly
from the weapon array */
p.assignWeapon(getRandWeapon());
// Send this player on a mission
p.mission();
}
}
// Utility methods to get a random player type and
// weapon
public static String getRandPlayerType()
{
Random r = new Random();
// Will return an integer between [0,2)
int randInt = r.nextInt(playerType.length);
// return the player stored at index 'randInt'
return playerType[randInt];
}
public static String getRandWeapon()
{
Random r = new Random();
// Will return an integer between [0,5)
int randInt = r.nextInt(weapons.length);
// Return the weapon stored at index 'randInt'
return weapons[randInt];
}
}
输出:
Counter Terrorist Created
Counter Terrorist with weapon Gut Knife| Task is DIFFUSE BOMB
Counter Terrorist with weapon Desert Eagle| Task is DIFFUSE BOMB
Terrorist Created
Terrorist with weapon AK-47| Task is PLANT A BOMB
Terrorist with weapon Gut Knife| Task is PLANT A BOMB
Terrorist with weapon Gut Knife| Task is PLANT A BOMB
Terrorist with weapon Desert Eagle| Task is PLANT A BOMB
Terrorist with weapon AK-47| Task is PLANT A BOMB
Counter Terrorist with weapon Desert Eagle| Task is DIFFUSE BOMB
Counter Terrorist with weapon Gut Knife| Task is DIFFUSE BOMB
Counter Terrorist with weapon Desert Eagle| Task is DIFFUSE BOMB
参考:
- 可重用的面向对象软件的要素(四人组)
- https://en.wikipedia.org/wiki/Flyweight_pattern