И снова здравствуйте. В преддверии старта базового и продвинутого курсов по Android-разработке мы подготовили для вас еще один интересный перевод.
Внедрение зависимостей требует от нас разделять операторы new и логику приложения. Это разделение подталкивает вас к использованию фабрик в вашем коде, которые отвечают за связывание вашего приложения. Однако, нежели писать фабрики, мы лучше будем использовать автоматическое внедрение зависимостей, такое как GUICE, которое бы взяло связывание на себя. Но действительно ли внедрение зависимостей может спасти нас от всех операторов new?
Давайте рассмотрим две крайности. Скажем, у вас есть класс MusicPlayer, который должен заполучить AudioDevice. Здесь мы хотим использовать внедрение зависимости и запросить AudioDevice в конструкторе MusicPlayer. Это позволит нам добавить дружественный к тестированию AudioDevice, который мы можем использовать, чтобы утверждать, что из нашего MusicPlayer выходит правильный звук. Если бы мы использовали оператор new для создания экземпляра BuiltInSpeakerAudioDevice, то у нас были бы некоторые трудности с тестированием. Итак, давайте называть такие объекты, как AudioDevice или MusicPlayer «Injectable». Injectable — это объекты, которые вы будете запрашивать в конструкторах и ожидать, что фреймворк для внедрения зависимостей вам их предоставит.
Теперь к другой крайности. Предположим, у вас есть примитив «int», но вы хотите автоупаковать его в «Integer», самое простое — вызвать new Integer (5), и дело с концом. Но если внедрение зависимостей является новым «new», почему мы вызываем new in-line? Повредит ли это нашему тестированию? Оказывается, что фреймворки для внедрения зависимостей не могут дать вам Integer, который вам нужен, поскольку они не понимают, о каком конкретно Integer идет речь. Это несколько игрушечный пример, поэтому давайте рассмотрим что-то более сложное.
Допустим, пользователь ввел адрес электронной почты в поле для логина, и вам нужно вызвать new Email(«a@b.com»
). Можно оставить так, или же мы должны запросить Email в нашем конструкторе? Опять же, фреймворк для внедрения зависимостей не может предоставить вам Email, поскольку сначала нужно получить String, в котором находится электронное письмо. А String-ов на выбор очень много. Как вы можете заметить, существует множество объектов, которые фреймворк внедрения зависимостей никогда не сможет предоставить. Давайте назовать их «Newable», так как вы будете вынуждены вызывать для них new вручную.
Во-первых, давайте установим некоторые основные правила. Injectable класс может запрашивать другие Injectable в своем конструкторе. (Иногда я называю Injectable как Service Object, но этот термин перегружен.) Injectable, как правило, имеют интерфейсы, так как есть вероятность, что нам придется заменить их реализацией, удобной для тестирования. Тем не менее, Injectable никогда не может запросить не-Injectable (Newable) в своем конструкторе. Это потому, что фреймворк для внедрения зависимостей не знает, как создать Newable. Вот несколько примеров классов, которые я ожидал бы получить от своего фреймворка для внедрения зависимостей: CreditCardProcessor, MusicPlayer, MailSender, OfflineQueue. Точно так же Newable могут запрашивать другие Newable в своем конструкторе, но не Injectable (иногда я называю Newable как Value Object, но опять же, этот термин перегружен). Некоторые примеры Newable: Email, MailMessage, User, CreditCard, Song. Если вы будете следовать этим разграничениям, ваш код будет легок в тестировании и работе с ним. Если же вы нарушите эти правила, ваш код будет сложно тестировать.
Давайте разберемся на примере MusicPlayer и Song
class Song {
Song(String name, byte[] content);
}
class MusicPlayer {
@Injectable
MusicPlayer(AudioDevice device);
play(Song song);
}
class Song {
String name;
byte[] content;
Song(String name, byte[] content);
}
class MusicPlayer {
AudioDevice device;
Song song;
@Injectable
MusicPlayer(AudioDevice device, Song song);
play();
}
class MusicPlayer {
AudioDevice device;
@Injectable
MusicPlayer(AudioDevice device);
}
class Song {
String name;
byte[] content;
MusicPlayer palyer;
Song(String name, byte[] content, MusicPlayer player);
play();
}
class SongReader {
MusicPlayer player
@Injectable
SongReader(MusicPlayer player) {
this.player = player;
}
Song read(File file) {
return new Song(file.getName(),
readBytes(file),
player);
}
}
class Song {
Song(String name, byte[] content);
boolean isPlayable(MusicPlayer player);
}
К сожалению, не доступен сервер mySQL