5 java oop singleton design-patterns
假设有一个复杂的应用程序,我们在其中存储和检索一组应用程序设置。
应用程序设置用于许多应用程序类,有两种方法可以解决此任务。
第一个。将 ApplicationSettings 声明为单例。
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
}
}
Run Code Online (Sandbox Code Playgroud)
我们有一个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
}
}
Run Code Online (Sandbox Code Playgroud)
它可能看起来很方便,但是使用单例有一些严重的缺点,例如,单例类很难测试。使用单例类会降低类的灵活性和可管理性。
作为一种选择,为了避免使用单例,我们声明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
}
}
Run Code Online (Sandbox Code Playgroud)
我相信第二种方式比第一种单例方式更灵活,并且生成更易于管理的代码。
我的问题:第二种方法假设我们需要将对应用程序设置的引用存储到每个使用 app 的类实例中。设置并获取应用程序。设置对象作为构造函数参数。
是否可以将这些应用程序设置引用存储到多个应用程序中?对象实例中,或者有更好的第三种方法来处理此任务?
谢谢。
小智 4
第一种方式(使用单例/工厂)有严重的缺点。单例很难测试,并且单例的使用会导致与系统的紧密耦合。当组件紧密耦合时,很难将不同的组件相互组合,这大大降低了整个产品的灵活性。
第二种方法,当我们ApplicationSettings作为参数传递给构造函数时,有它自己的缺点,当我们有复杂的继承树时就会出现这种缺点。
想象一下我们有课程:GrandParentOfSomeObject-> SomeObject->ChildSomeObject
而类根本SomeObject不需要。ApplicationSettings但ChildSomeObject需要访问应用程序设置。
在这种情况下我们需要将ApplicationSettings对象引用传递给SomeObject构造函数,即使SomeObject不需要这个对象,也有必要传递它。稍后提供给ChildSomeObject.
这种方法会用不必要的功能污染代码,并使其不太优雅和灵活。
第三种首选方法是使用依赖项注入方法,在需要时注入应用程序设置对象。
我使用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);
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
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
}
}
Run Code Online (Sandbox Code Playgroud)
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(" ******* ******* *******");
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,在ApplicationComponent课堂上我们声明:
@Inject
private SettingsInterface applicationSettings;
Run Code Online (Sandbox Code Playgroud)
在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>
Run Code Online (Sandbox Code Playgroud)
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
******* ******* *******
------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)