Различные паттерны дизайна, представленные в этой главе, стремятся избежать использования RTTI, что может создать впечатление, будто RTTI является чем-то негативным (вспомните бедный goto
, который был так плохо воспринят, что его вообще не включили в Java). Однако это не всегда верно. Более точно можно сказать, что неправильное использование RTTI "вредно". Мы стараемся избегать использования RTTI потому, что её ошибочное применение может привести к снижению расширяемости системы. Нашей целью было обеспечить возможность свободного добавления новых типов в систему с минимальным воздействием на окружающий код. Поскольку RTTI часто используется неправильно (например, когда она используется для поиска всех типов в системе), это значительно снижает способность кода к расширению — при добавлении нового типа требуется найти все места, где используется RTTI. Даже если вы пропустите один такой случай, компилятор вам не поможет.Однако сам RTTI не создаёт автоматически неконсистентный код. Вспомним пример с автоматической очисткой мусора. На этот раз мы добавляем новый инструмент, который я называю TypeMap
. Он содержит Hashtable
(ассоциативный массив), содержащий несколько Vector
'ов, но интерфейс очень простой: можно добавить (add()
) новый объект, а также получить (get()
) Vector
, содержащий все объекты определённого типа. Ключевой особенностью этого паттерна является то, что TypeMap
динамически добавляет новые типы при встрече с ними. Таким образом, каждый раз, когда новый тип добавляется в систему (даже во время выполнения программы), он будет корректно обрабатываться.Наш пример основан на структуре типа Trash
внутри "пакета" (Package
) c16.Trash
(и использует тот же файл Trash.dat
).
//: DynaTrash.java
// Использование ассоциативного массива Vector и RTTI
// для автоматического сортирования мусора в
// векторах. Этот подход, несмотря на использование RTTI,
// является расширяемым.
package c16.dynatrash;
import c16.trash.*;
import java.util.*;
// Общий TypeMap работает в любой ситуации:
class TypeMap {
private Hashtable t = new Hashtable();
public void add(Object o) {
Class type = o.getClass();
if(t.containsKey(type))
((Vector)t.get(type)).addElement(o);
else {
Vector v = new Vector();
v.addElement(o);
t.put(type,v);
}
}
public Vector get(Class type) {
return (Vector)t.get(type);
}
public Enumeration keys() { return t.keys(); }
// Возвращает обработчик адаптерного класса для возможности
// обратного вызова из ParseTrash.fillBin():
public Fillable filler() {
// Анонимный внутренний класс:
return new Fillable() {
public void addTrash(Trash t) { add(t); }
};
}
}
```Пожалуйста, учтите, что использование `Hashtable`, `Vector` и `Enumeration` является устаревшим в современной Java. Рекомендую использовать более новые коллекции, такие как `HashMap`, `ArrayList` и `Iterator`. Хотя функционал очень мощный, но определение `TypeMap` довольно простое. Оно просто включает хэш-таблицу, а метод `add()` выполняет большую часть работы. При добавлении нового типа объект класса `Class` извлекается. Затем с помощью этого объекта проверяется, существует ли вектор (`Vector`), содержащий объекты данного типа, в хэш-таблице. Если такой вектор уже существует, он извлекается, и новый объект добавляется в него; если нет, то объект класса `Class` и новый пустой вектор добавляются как пара "ключ-значение".Метод `keys()` позволяет получить список всех объектов класса `Class`, а метод `get()` позволяет получить соответствующий вектор по объекту класса.
Метод `filler()` представляет интерес, так как он использует дизайн метода `ParseTrash.fillBin()`. Он может попытаться заполнить вектор, а также использовать метод `addTrash()` для заполнения любого объекта, который реализует интерфейс `Fillable`.
Для создания такого вызова метод `filter()` просто возвращает ссылку на объект, который реализует интерфейс `Fillable`, и передает эту ссылку в качестве параметра методу `fillBin()`, как показано ниже:
```java
ParseTrash.fillBin("Trash.dat", bin.filler());
```
Чтобы создать этот вызов, мы использовали "анонимный внутренний класс" (описанный в главе 7). Поскольку нам не требуется имя класса для реализации интерфейса `Fillable`, достаточно иметь ссылку на объект этого класса, использование анонимного внутреннего класса здесь вполне уместно.
Одной из особенностей этого дизайна является то, что хотя контроль над категориями не был предусмотрен, каждый раз при выполнении метода `fillBin()` объект класса `Trash` автоматически добавляется в контейнер `bin`.После просмотра предыдущих примеров большинство частей класса `DynaTrash` должны быть вам знакомыми. На этот раз вместо помещения новых объектов класса `Trash` в векторы типа `bin`, мы используем механизм внутренней классификации `TypeMap`. Процесс прохождения через `TypeMap` и операция со всеми отдельными векторами оказывается довольно простым делом.
Перебор ключей происходит следующим образом:```java
Перечисление ключей = бин.ключи();
пока(ключи.естьЕщеЭлементы()) {
Trash.суммаЗначения(бин.получить((Класс)ключи.следующийЭлемент()));
}
```
Как видно, добавление новых типов в систему никак не влияет на эти коды, а также не затрагивает код `TypeMap`. Это явно является наиболее полным решением проблемы. Хотя этот подход действительно сильно зависит от RTTI, стоит отметить, что каждый ключ-значение в хэш-таблице проверяет только один тип. Кроме того, при добавлении нового типа мы не оказываемся в ситуации "забывчивости" относительно добавления правильного кода в систему, так как вообще ничего добавлять не требуется.
```
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )