#java #oop #singleton
#java #ооп #синглтон
Вопрос:
Я пишу небольшой веб-сервер, и для этого требуется файл конфигурации со всевозможными параметрами: сколько потоков запускать, какой класс обрабатывает каждое расширение файла, какой файл отображать по умолчанию и так далее, и тому подобное. Чтобы представить это, я преобразовываю конфигурационный файл в Configuration
объект, который содержит все эти настройки, а основной класс содержит этот объект.
Однако данные конфигурации требуются практически на каждом уровне сервера — классы внутри классов внутри классов…
Мой вопрос в том, какую наилучшую практику здесь использовать? Должен ли я указывать конфигурацию в качестве параметра для многих-многих классов и передавать ее туда и обратно? Должен ли я сделать это синглтоном? Есть ли другое решение, которого я не вижу?
Ответ №1:
Используйте Guice! Guice — это платформа для внедрения зависимостей, которая эффективно заменяет использование вами ключевого слова new. Вы определяете привязки ваших объектов в модуле, подобном этому:
bind(Configuration.class).to(MySpecificConfiguration.class);
bind(WebServer.class).to(MySpecificWebServerImplementation.class);
И затем, вместо того, чтобы создавать что-то новое WebServer
напрямую, вы просто просите Guice сделать это за вас:
WebServer ws = Guice.createInjector(yourModule).getInstance(WebServer.class);
Которая волшебным образом будет создана MySpecificWebServerImplementation
для вас. If MySpecificWebServerImplementation
определяется следующим образом:
public class MySpecificWebServerImplementation {
@Inject
public MySpecificWebServerImplementation(Configuration configuration) {
}
}
Тогда MySpecificWebServerImplementation
конфигурация будет автоматически предоставлена, и вам не придется беспокоиться о ее передаче.
Комментарии:
1. 1 для этого решения это несколько более современный подход DI по сравнению с шаблоном Singleton старой школы
Ответ №2:
Передача экземпляра Configuration
по кругу добавляет много ненужного беспорядка в ваш код. Хорошей идеей будет использовать Singleton или добавить какой-нибудь класс Manager, который предоставляет все эти общие ресурсы, такие как данные конфигурации.
Если вы выберете singleton, рассмотрите возможность реализации шаблона enum:
public enum Config{ DATA;
private int value1;
private int value2;
private int value3;
public load(File configFile) {
// some magic to store the values
}
public int getValue1() {return value1;}
public int getValue2() {return value2;}
public int getValue3() {return value3;}
}
Вы можете использовать ее везде в своем коде:
int property = Config.DATA.getValue1();
Комментарии:
1. Привязка файла к каждому классу и эффективное усложнение его тестирования. Используйте перечисления по назначению для определения констант, но откажитесь от синглтона. Класс ведения журнала является исключением. Объект конфигурации является идеальным примером решения для внедрения зависимостей.
Ответ №3:
Хотя Singletons
по разным причинам многие профессионалы больше не одобряют Java, я думаю, что все еще есть хороший вариант использования Ssingleton`.
Однако — спросите себя, действительно ли требования веб-сервера требуют единой общей конфигурации для всех аспектов сервера. И если да — насколько вероятно, что они скоро изменятся? Например, допустим, на вашем сервере размещены 3 веб-сайта. И теперь вас просят установить ограничения на пропускную способность для одного из них — как вы это настроите?
Я действительно за хороший синглтон — хотя вы должны учитывать всю проблему потокобезопасности при его инициализации в многопоточной среде (Хотя, похоже, у вас не возникнет этой проблемы, поскольку у вас не будет нескольких потоков перед его инициализацией.
Имейте в виду, что синглтоны живут внутри a ClassLoader
, и если у вас есть несколько ClassLoader
файлов (например, для каждого .war
загружаемого вами), у вас будет несколько копий этого « Singleton
«.
Рассмотрите все аспекты вашего сервера, и если это действительно просто — A Singleton
избавит вас от большой головной боли — и сделает код намного более удобным для сопровождения и чтения. Как уже упоминалось в одном или двух ответах — внедрение зависимостей является хорошей альтернативой Singleton
s — они помогают вам упростить ваши модули.
Одно замечание о JUnit, я понимаю (еще не пробовал), что Mockit
может позволить вам заменить фактическую реализацию во время тестового запуска, так что вы все равно можете создавать разные тестовые примеры JUnit для разных сценариев, не изменяя свой Singleton
, чтобы его можно было тестировать.
Комментарии:
1. не могли бы вы уточнить свое утверждение о том, что «Многие профессионалы по разным причинам больше не предпочитают синглтоны»? Я не уверен, что понимаю вас…
2. @stupahsmaht — Многие разработчики ссылаются на a
Singleton
как наantipattern
, что означает то, чего вам не следует делать. Если вы загуглите «singleton antipattern», вы можете найти множество ссылок на него во многих блогах.
Ответ №4:
Сделать ее одноэлементной кажется разумным решением. Я думаю, было бы ошибкой передавать конфигурацию в качестве параметра конструкторам других классов или вызовам методов, поскольку это приведет к загрязнению аргументов метода и сделает их излишне сложными.
Если вы все-таки решите создать singleton, то я бы рекомендовал разработать механизм, с помощью которого вы можете вводить новые экземпляры конфигурации в класс singleton, чтобы singleton не затруднял выполнение ваших модульных тестов.
Ответ №5:
Если вы делаете это на Java, я бы разместил всю конфигурацию в Spring, а затем либо использовал контекст приложения для поиска настроек, либо просто внедрил их в ваши компоненты. Spring превосходен в такого рода вещах.
Ответ №6:
Мы установили ваши свойства как системные свойства. Доступ к ним осуществляется SystemProperties
классом.
Этот класс final
имеет закрытый конструктор, доступ к свойствам осуществляется только через статические методы, поэтому технически это синглтон. Она сама содержится в commons.jar каждый другой проект зависит от.
Таким образом, нет необходимости передавать что-либо, поскольку настройки доступны непосредственно там, где они вам нужны.
Комментарии:
1. Это может прерваться, как только на сервере установлено более одного веб-приложения.
2. @Аарон Дигулла — конечно. Она может даже сломаться сама по себе, если кто-то присвоит свойству неправильное значение. Единственное, что обеспечивает безопасность, — это довольно длинные и специфические имена свойств. В нашем приложении есть много видов свойств, и подход к системным свойствам применяется к наиболее динамичным из них. Затем есть свойства, развернутые как связанный XML, некоторые свойства в манифесте, а также некоторые пропозиции, жестко закодированные в конечных классах как общедоступные статические элементы. И, ИМХО, есть веские причины использовать каждый из подходов. Просто не для всего
Ответ №7:
Я бы определенно сделал класс-держатель конфигурации одноэлементным! Это классический вариант использования для одноэлементного экземпляра.
Ответ №8:
Конфигурации нет, но хорошо иметь один одноэлементный объект ServerManager
. Слишком много глобально доступных объектов немного сбивают с толку. Конфигурация должна принадлежать «компоненту»:
ServerManager.getServerManager.getConfigurationComponent(Configuration.class);
ServerManager должен создать и прочитать объект конфигурации во время запуска. Сохраните и закройте ее перед выходом.
Если вы используете ее более одного раза в каком-либо классе, сохраните ее как частное конечное поле.
Ответ №9:
Я часто этим пользовался. Я делаю несколько установок продуктов, и они не всегда находятся в одном и том же месте. Однако установка, интеграция и внедрение продукта обычно одинаковы. Итак, чтобы компенсировать это, я пишу класс свойств конфигурации, который просматривает файл config.properties. Сначала вы должны зайти и изменить файл свойств в соответствии с местоположениями, значениями и т.д. Того, что вам нужно заранее. Затем скомпилируйте класс с классом config и перенесите это в приложение.
Пример ниже. файл config.properties
import_dir=c:\temp\test\import
export_dir=c:\temp\test\export
system_dir=c:\temp\test\system
username=mainman
role=admin
domain=
server=
hostname=
и т.д…
Затем я создал класс внутри моего класса.
public static class Config {
private static Properties defaultProps = new Properties();
static {
try {
FileInputStream in = new FileInputStream("config.properties");
defaultProps.load(in);
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getProperty(String key) {
return defaultProps.getProperty(key);
}
}
Я уверен, что вы также могли бы создать отдельный класс, но это тоже работает.
Затем внутри моего метода или в основном классе я получаю нужные мне значения и присваиваю их всему, что мне нужно. Затем я могу передать их соответствующему методу, который в них нуждается.
domain = Config.getProperty("domain");
server = Config.getProperty("server");
hostname = Config.getProperty("hostname");
Это довольно круто!
Я также использую это на нескольких ОС.