Преимущество плавающего веса в связке с другими шаблонами проектирования в том, что это способ улучшить производительность. Это общий идеал - просто создавать объект для каждого элемента вашей системы, но некоторые задачи генерируют запредельное число объектов, что в результате значительно замедлит выполнение или может стать причиной нехватки памяти.
Плавающий вес решает эту проблему путем снижения числа объектов. Чтобы сделать это вы создаете расширенные данные в объекте, так что вы можете представить, что имеете больше объектов, чем это есть на самом деле. Однако, это внесет сложность в интерфейсы, использующие такие объекты, поскольку вы должны передавать дополнительную информацию в вызываемый метод, чтобы сказать методу, как найти расширенную информацию.
В качестве очень простого примера рассмотрим объект DataPoint, который хранит int, float, и id, содержащий номер объекта. Предположим, вам нужно создать миллион таких объектов, а затем манипулировать ими подобным образом:
//: flyweight:ManyObjects.java
class DataPoint {
private static int count = 0;
private int id = count++;
private int i;
private float f;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public float getF() {
return f;
}
public void setF(float f) {
this.f = f;
}
public String toString() {
return "id: " + id + ", i = " + i + ", f = " + f;
}
}
public class ManyObjects {
static final int size = 1000000;
public static void main(String[] args) {
DataPoint[] array = new DataPoint[size];
for (int i = 0; i < array.length; i++)
array[i] = new DataPoint();
for (int i = 0; i < array.length; i++) {
DataPoint dp = array[i];
dp.setI(dp.getI() + 1);
dp.setF(47.0f);
}
System.out.println(array[size - 1]);
}
} // /:~
В зависимости от вашего компьютера, эта программа может выполняться несколько секунд. Более сложные объекты и более запутанные операции могут стать причиной увеличения накладных расходов и этот метод станет неприемлемым. Для решения проблемы DataPoint можно снизить количество объектов с миллиона до одного объекта, введя хранитель расширенных данных в DataPoint:
//: flyweight:FlyWeightObjects.java
class ExternalizedData {
static final int size = 5000000;
static int[] id = new int[size];
static int[] i = new int[size];
static float[] f = new float[size];
static {
for (int i = 0; i < size; i++)
id[i] = i;
}
}
class FlyPoint {
private FlyPoint() {
}
public static int getI(int obnum) {
return ExternalizedData.i[obnum];
}
public static void setI(int obnum, int i) {
ExternalizedData.i[obnum] = i;
}
public static float getF(int obnum) {
return ExternalizedData.f[obnum];
}
public static void setF(int obnum, float f) {
ExternalizedData.f[obnum] = f;
}
public static String str(int obnum) {
return "id: " + ExternalizedData.id[obnum] + ", i = "
+ ExternalizedData.i[obnum] + ", f = "
+ ExternalizedData.f[obnum];
}
}
public class FlyWeightObjects {
public static void main(String[] args) {
for (int i = 0; i < ExternalizedData.size; i++) {
FlyPoint.setI(i, FlyPoint.getI(i) + 1);
FlyPoint.setF(i, 47.0f);
}
System.out.println(FlyPoint.str(ExternalizedData.size - 1));
}
} // /:~
Так как все данные теперь хранятся в ExternalizedData, каждый вызов метода FlyPoint должен включать индекс для ExternalizedData. Чтобы быть последовательным, и чтобы напомнить читателю схожесть с явным указателем this при вызове метода, "this индекс" передается в метод в качестве первого аргумента.
Естественно, здесь стоит повторить относительно преждевременной оптимизации. "Сначала заставьте это работать, затем сделайте это быстрее - если это заработало". Также, профайлер - это инструмент, который используется для обнаружения узких мест, а не для гадания.
← | Упражнение | Декоратор (Decorator): Слишком много классов | → |