Мои Конспекты
Главная | Обратная связь


Автомобили
Астрономия
Биология
География
Дом и сад
Другие языки
Другое
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Металлургия
Механика
Образование
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Туризм
Физика
Философия
Финансы
Химия
Черчение
Экология
Экономика
Электроника

Рассмотрите возможность замены конструкторов статическими методами генерации.



 

Обычно для того, чтобы клиент мог получать экземпляр класса, ему предоставля­ется открытый (pubIic) конструктор. Есть и другой, менее известный прием, который должен быть в арсенале любого программиста. Класс может иметь открытый статический метод генерации (static factory method), который является статическим мето­дом, возвращающим экземпляр класса. Пример такого метода возьмем из класса Boolean (являющего оболочкой для простого типа boolean). Приведенный ниже статический метод генерации, который был добавлен в версию 1.4, преобразует значение boolean в ссылку на объект Boolean:

public static Boolean valueOf(boolean Ь) {

Return (Ь? Boolean. TRUE: Boolean. FALSE);

}

Статические методы генерации могут быть предоставлены клиентам класса не только вместо конструкторов, но и в дополнение к ним. Замена открытого конструктора статическим методом генерации имеет свои достоинства и недостатки.

 

 

Первое преимущество статического метода генерации состоит в том, что, в отличие от конструкторов, он имеет название. В то время как параметры кон­структора сами по себе не дают описания возвращаемого объекта, статический метод генерации с хорошо подобранным названием может упростить работу с классом и, как следствие, сделать соответствующий программный код клиента более понятным. На­пример, конструктор Biglnteger(int, int, Random), который возвращает Biglnteger, являющийся, вероятно, простым числом (prime), лучше было бы представить как статический метод генерации с названием Biglnteger.ргоЬаЫеРrime. (В конечном счете этот статический метод был добавлен в версию 1.4.)

Класс может иметь только один конструктор с заданной сигнатурой. Известно, что программисты обходят данное ограничение, создавая конструкторы, чьи списки параметров отличаются лишь порядком следования типов. Это плохая идея. Человек, использующий подобный API, не сможет запомнить, для чего нужен один конструк­тор, а для чего другой, и, в конце концов, по ошибке вызовет не тот конструктор. Т е, кто читает' программный код, в котором при меняются такие конструкторы, не смогут понять, что же он делает, если не будут сверяться с сопроводительной документацией

на этот класс.

Поскольку статические методы генерации имеют имена, к ним не относится

ограничение конструкторов, запрещающее иметь в классе более одного метода с заданной сигнатурой. Соответственно в ситуациях, когда очевидно, что в классе должно быть несколько конструкторов с одной и той же сигнатурой, следует рассмотреть возмож­ность замены одного или нескольких конструкторов статическими методами генерации. Тщательно выбранные названия будут подчеркивать их различия.

Второе преимущество статических методов генерации заключается в том, что, в отличие от конструкторов, они не обязаны при каждом вызове создавать новый объект. Это позволяет использовать для неизменяемого класса (статья 13) предварительно созданный экземпляр либо кэшировать экземпляры класса по мере их создания, а затем раздавать их повторно, избегая создания ненужных дублирующих объектов. Подобный прием иллюстрирует метод Boolean. valueOf(boolean): он не создает объектов. Эта методика способна значительно повысить производительность программы, если часто возникает необходимость в создании одинаковых объектов, особенно в тех случаях, когда создание объектов требует больших затрат.

Способность статических методов генерации возвращать при повторных вызовах тот же самый объект можно использовать и для того, чтобы в любой момент времени; четко контролировать, какие экземпляры объекта еще существуют. На это есть две причины. Во-первых, это позволяет гарантировать, что некий класс является синглто­ном (статья 2). Во-вторых, это дает возможность убедиться в том, что у неизменяемо­го класса не появилось двух одинаковых экземпляров: a.equals(b) тогда и только тогда, когда а==Ь. Если класс предоставляет' такую гарантию, его клиенты могут использовать оператор == вместо метода equals(Object), что приводит к существен­ному повышению производительности программы. Подобную оптимизацию реализует шаблон перечисления типов, описанный в статье 21, частично ее реализует также метод String.intern.

 

 

Третье преимущество статического метода генерации заключается в том, что, в отличие от конструктора, он может вернуть объект, который соответствует не только заявленному типу возвращаемого значения, но и любому его подтипу. Это предоставляет вам значительную гибкость в выборе класса для возвращаемого объекта. Например, интерфейс АР! может возвращать объект, не декларируя его класс как pubIic. Сокрытие реализации классов может привести к созданию очень компактного API. Этот прием идеально подходит для конструкций, построенных на интерфейсах, где интерфейсы для статических методов генерации задают собственный тип возвращаемого значения.

Например, архитектура Collections Framework имеет двадцать полезных реализа­ций интерфейсов коллекции: неизменяемые коллекции, синхронизированные коллек­ции и т. д. С помощью статических методов генерации большинство этих реализаций сводится в единственный класс jауа.util.Collections, для которого невозможно со­здать экземпляр. Все классы, соответствующие возвращаемым объектами, не, являются открытыми.

АРI Collections Framework имеет гораздо меньшие размеры, чем это было бы, если бы в нем были представлены двадцать отдельных открытых классов для всех воз­можных реализаций. Сокращен не только объем этого API, но и его "концептуальная нагрузка". Пользователь знает, что возвращаемый объект имеет в точности тот API, который указан в соответствующем интерфейсе, и ему нет нужды читать дополнительные документы по этому классу. Более того, применение статического метода генерации дает клиенту право обращаться к возвращаемому объекту, используя его собственный интерфейс, а не интерфейс класса реализации, что обычно является хоро­шим приемом (статья 34).

Скрытым может быть не только класс объекта, возвращаемого открытым стати­ческим методом генерации. Сам класс может меняться от вызова к вызову в зависи­мости от того, какие значения параметров передаются статическому методу генерации. Это может быть любой класс, который является подтипом по отношению к возвращаемому типу, заявленному в интерфейсе. Класс возвращаемого объекта может также меняться от версии к версии, что повышает удобство сопровождения программы.

В момент написания класса, содержащего статический метод генерации, класс, соответствующий возвращаемому объекту, может даже не существовать. Подобные гибкие статические методы генерации лежат в основе систем с предоставлением услуг (service provider framework), например ]ауа Cryptography Extension ОСЕ). Система с предоставлением услуг - это такая система, в которой поставщик может создавать различные реализации интерфейса API, доступные пользователям этой системы. Чтобы сделать эти реализации доступными для применения, предусмотрен механизм регистрации (register). Клиенты могут пользоваться указанным API, не беспокоясь о том, с какой из его реализаций они имеют дело.

В Упомянутой системе ]CE системный администратор регистрирует класс реали­зации, редактируя хорошо известный файл Properties: делает в нем запись, которая с13лзыветT некий ключ-строку с именем соответствующего класса. Клиенты же используют статический метод генерации, который получает этот ключ в качестве параметра.

 

 

По схеме, восстановленной из файла Properties, статический метод генерации нахо­дит объект Class, а затем создает экземпляр соответствующего класса с помощью метода Class. newlnstance.' Этот прием демонстрируется в следующем фрагменте:

import java.util.*;

// Эскиз системы с предоставлением услуг

public abstract class Foo {

// Ставит ключ типа String в соответствие объекту Class

private static Map implementations = null;

// При первом вызове инициализирует карту соответствия

private static synchronized void initMapIfNecessary() {

if (implementations == null) {

implementations = new HashMap();

// 3агружает названия классов и ключи из файла Properties,

// транслирует названия в объекты Class, используя

// Class. forName, и сохраняет их соответствие ключам

// ...

}

}

public static Foo getInstance(String key) {

initMapIfNecessary();

Class c = (Class) implementations.get(key);

if (c == null)

return new DefaultFoo();

try {

return (Foo) c.newInstance();

} catch (Exception e) {

return new DefaultFoo();

} }

public static void main(String[] args) {

System.out.println(getInstance("NonexistentFoo"));

} }

class DefaultFoo extends Foo {

}

Основной недостаток статических методов генерации заключается в том, что классы, не имеющие открытых или защищенных конструкторов, не могут иметь подклассов. Это же касается классов, которые возвращаются открытыми статически­ми методами генерации, но сами открытыми не являются. Например, в архитектуре Collections Framework невозможно создать подкласс ни для одного из классов реализа­ции. Сомнительно, что в такой маскировке может быть благо, поскольку поощряет программистов использовать не наследование, а композицию (статья 14).

Второй недостаток статических методов генерации состоит в том, что их трудно отличить от других статических методов. В документации АРI они не вы­деляются так, как это делается для конструкторов. Более того, статические методы генерации представляют собой отклонение от нормы. Поэтому иногда из документации к классу сложно понять, как создать экземпляр класса, в котором вместо конструкторов

 

 

 

клиенту предоставлены статические· методы генерации. Указанный недостаток может быть смягчен, если придерживаться стандартных соглашений, касающихся именова­ния. Эти соглашения продолжают совершенствоваться, но два названия статических методов генерации стали уже общепринятыми:

· valueOf' - возвращает экземпляр, который имеет то же значение,

что и его параметры. Статические методы генерации с таким названием фактически являются операторами преобразования типов.

· getlnstance - возвращает экземпляр, который описан параметрами, однако говорить о том, что он будет иметь то же значение, нельзя.

В случае с синглтоном этот метод возвращает единственный экземпляр данного класса. Это название является общепринятым в системах

с предоставлением услуг.

Подведем итоги. И статические методы генерации, и открытые конструкторы имеют свою область применения. Имеет смысл изучить их достоинства и недостатки. Прежде чем создавать конструкторы, рассмотрите возможность использования стати­ческих методов генерации, поскольку последние часто оказываются лучше. Если вы проанализировали обе возможности и не нашли достаточных доводов в чью-либо пользу, вероятно, лучше всего создать конструктор, хотя бы потому, что этот подход является нормой.