假设存在一个复杂的应用程序,我们存储和检索一组应用程序设置。
应用程序设置用于许多应用程序类,有两种方法可以解决此任务。
第一个。将ApplicationSettings声明为singleton。
public class ApplicationSettings {
private static final ApplicationSettings instance = new ApplicationSettings();
public static ApplicationSettings getInstance() {
return instance;
}
// ... public methods to set and retrieve settings information, save settings, etc.
private ApplicationSettings() {
// loading the application settings in the private constructor
}
}
我们有一个Application
类(以及许多其他类),我们使用应用程序设置。
public class Application {
public Application() {
// .... initialization code
}
public void doSomething() {
ApplicationSettings applicationSettings = ApplicationSettings.getInstance();
someMethod1(applicationSettings.getSetting(ApplicationSettings.SETTING_SOME_KEY1));
someMethod2(applicationSettings.getSetting(ApplicationSettings.SETTING_SOME_KEY2));
// etc
}
}
看起来很方便,但单胎的使用有一些严重的缺点,例如,单身类很难测试。使用单例类会降低类的灵活性和可管理性。
作为一种选择,为了避免使用单例,我们将ApplicationSettings
声明为常规类,而不是单例。并将ApplicationSettings
实例作为参数传递给使用ApplicationSettings
进行内部活动的类的构造函数。
第二种方式:
public interface ApplicationSettingsInterface {
// ... public methods to set and retrieve settings information, save settings, etc.
}
public class ApplicationSettings implements ApplicationSettingsInterface {
private ApplicationSettings() {
// loading the application settings in the private constructor
}
// ... public methods to set and retrieve settings information, save settings, etc.
}
public class Application {
private final ApplicationSettingsInterface applicationSettings;
public Application(ApplicationSettingsInterface applicationSettings) {
this.applicationSettings = applicationSettings;
// .... initialization code
}
public void doSomething() {
someMethod1(applicationSettings.getSetting(ApplicationSettings.SETTING_SOME_KEY1));
someMethod2(applicationSettings.getSetting(ApplicationSettings.SETTING_SOME_KEY2));
// etc
}
}
我相信第二种方式比单身人士的第一种方式更灵活,产生更易于管理的代码。
我的问题:第二种方式假设我们需要将对应用程序设置的引用存储到使用app的每个类实例中。设置和获取应用程序。设置对象作为构造函数参数。
是否可以将这些应用程序设置引用存储到许多对象实例中,或者有更好的第三种方法来处理此任务?
谢谢。
答案 0 :(得分:3)
第一种方式(单身/工厂)有严重的缺点。单身人士难以测试,单身人士的使用会导致紧密耦合到你的系统。当组件紧密耦合时,很难将不同的组件相互组合,这会显着降低整个产品的灵活性。
第二种方式,当我们将ApplicationSettings
作为参数传递给构造函数时,它有自己的缺点,当我们有复杂的继承树时,它会进入阶段。
想象一下,我们有课程:GrandParentOfSomeObject
- > SomeObject
- > ChildSomeObject
SomeObject
课程根本不需要ApplicationSettings
。但是ChildSomeObject
需要访问应用程序设置。
在这种情况下,我们需要将ApplicationSettings
对象引用传递给SomeObject
构造函数,即使SomeObject
不需要此对象,也需要传递它。稍后提供给ChildSomeObject
。
这种方法会污染具有不必要功能的代码,使其不那么优雅和灵活。
第三种首选方法是使用dependency injection方法,在需要时注入应用程序设置对象。
我使用Google Guice来证明这一点。
下面是我的评论代码。
SettingsInterface.java
- 表示应用程序设置功能的界面。
package com.mycompany.settingstest;
public interface SettingsInterface {
int getFirstSetting();
void setFirstSetting(int value);
String getSecondSetting();
void setSecondSetting(String value);
boolean getThirdSetting();
void setThirdSetting(boolean value);
}
ApplicationSettings.java
- SettingsInterface
类的具体实现。
package com.mycompany.settingstest;
import javax.inject.Singleton;
@Singleton
public class ApplicationSettings implements SettingsInterface{
private int firstSetting = -1234567890;
private String secondSetting = "some default string value";
private boolean thirdSetting = true;
public ApplicationSettings() {
}
@Override
public int getFirstSetting() {
return this.firstSetting;
}
@Override
public void setFirstSetting(int value) {
this.firstSetting = value;
}
@Override
public String getSecondSetting() {
return this.secondSetting;
}
@Override
public void setSecondSetting(String value) {
this.secondSetting = value;
}
@Override
public boolean getThirdSetting() {
return this.thirdSetting;
}
@Override
public void setThirdSetting(boolean value) {
this.thirdSetting = value;
}
}
ApplicationSettingsModule.java
- 声明必要绑定的模块,Google Guice可以在依赖注入时使用它。
package com.mycompany.settingstest;
import com.google.inject.AbstractModule;
import com.google.inject.Scopes;
public class ApplicationSettingsModule extends AbstractModule {
@Override
protected void configure() {
//bind the service to implementation class
bind(SettingsInterface.class).to(ApplicationSettings.class).in(Scopes.SINGLETON);//this is lazy singleton
//bind(SettingsInterface.class).to(ApplicationSettings.class).asEagerSingleton();//this is eager one
}
}
MainApplication.java
- 主应用程序类,还声明了一个应用程序组件类,用于演示应用程序设置的功能。
package com.mycompany.settingstest;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
public class MainApplication {
public static void main(String[] args) {
Injector applicationSettingsInjector = Guice.createInjector(new ApplicationSettingsModule());
SettingsInterface applicationSettings = applicationSettingsInjector.
getInstance(SettingsInterface.class);
System.out.println(" ******* Main Application *******");
System.out.println(" Initial setting values ");
System.out.println("first setting: " + applicationSettings.getFirstSetting());
System.out.println("second setting: " + applicationSettings.getSecondSetting());
System.out.println("third setting: " + applicationSettings.getThirdSetting());
System.out.println(" ******* ******* *******");
System.out.println(" ******* We're changing settings *******");
applicationSettings.setFirstSetting(789);
applicationSettings.setSecondSetting("another custom string");
applicationSettings.setThirdSetting(false);
System.out.println(" Settings were changed. Check changes below: ");
System.out.println("first setting: " + applicationSettings.getFirstSetting());
System.out.println("second setting: " + applicationSettings.getSecondSetting());
System.out.println("third setting: " + applicationSettings.getThirdSetting());
System.out.println(" ******* ******* *******");
System.out.println(" Now composing the application component and injecting application settings to it");
ApplicationComponent applicationComponent = applicationSettingsInjector.getInstance(ApplicationComponent.class);
applicationComponent.execute();
}
}
class ApplicationComponent {
@Inject
private SettingsInterface applicationSettings;
public ApplicationComponent() {
}
public void execute() {
System.out.println(" ******* Application component *******");
System.out.println("Application settings instance is already injected");
System.out.println("first setting: " + applicationSettings.getFirstSetting());
System.out.println("second setting: " + applicationSettings.getSecondSetting());
System.out.println("third setting: " + applicationSettings.getThirdSetting());
System.out.println(" ******* ******* *******");
}
}
请注意,我们在ApplicationComponent
课程中声明:
@Inject
private SettingsInterface applicationSettings;
并且在execute()
方法中我们已经构造了ApplicationSettings
对象并且可以使用它,并且我们不会将此对象引用传递给ApplicationComponent
构造函数。
稍后,我们可以决定使用SettingsInterface
的其他实现,而不是ApplicationSettings
。例如,在第一个版本中,我们将应用程序设置存储到文本文件中。在我们产品的下一个版本中,我们希望使用一些复杂的集中设置提供程序机制。
在这种情况下,我们不需要全局更改代码。我们只需更改ApplicationSettingsModule
中的绑定以将另一个实现绑定到SettingsInterface
,它就足以为我们系统的所有组件提供新的应用程序设置机制。
<强>更新强>
忘了提及pom.xml
内容和输出。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany</groupId>
<artifactId>SettingsTest</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>3.0</version>
</dependency>
</dependencies>
</project>
output
--- exec-maven-plugin:1.2.1:exec (default-cli) @ SettingsTest ---
******* Main Application *******
Initial setting values
first setting: -1234567890
second setting: some default string value
third setting: true
******* ******* *******
******* We're changing settings *******
Settings were changed. Check changes below:
first setting: 789
second setting: another custom string
third setting: false
******* ******* *******
Now composing the application component and injecting application settings to it
******* Application component *******
Application settings instance is already injected
first setting: 789
second setting: another custom string
third setting: false
******* ******* *******
------------------------------------------------------------------------
答案 1 :(得分:1)
我相信第二个想法更好,因为它揭示了通过接口的依赖性。其他方法:如果您计划在代码中全局访问应用程序设置,则可以将它们设置为静态。这样的课程可以从任何地方访问。
使用Singleton可能是一个坏主意,因为它通常隐藏模块之间的连接,而不是通过接口显示它们。
虽然使全局可达的静态类可能看起来类似于单例(因为它不需要通过接口显式地显示依赖关系)我认为它仍然比单例更好,因为如果你有一个全局可达的静态类,你可以期待它是全局使用的(它变成了一个容器,或者一个方法工具箱,它可能不需要经常更改。)
答案 2 :(得分:0)
您还可以实现ApplicationSettingsFactory
定义方法ApplicationSettingsInterface Create()
并将其用作依赖项。因此,课程将更加可测试。
工厂可以合并对象创建策略(单线或非单线)。
最后,为什么不使用IoC容器来定义单声道? IoC容器本身就是工厂,如果你对在代码中的任意位置创建配置对象不感兴趣,我建议将`ApplicationSettingsInterface'的依赖注入作为最佳解决方案。否则,您可以使用自己的工厂。