Search code examples
javasingletongetter-setter

Add setter to an singleton class


I have a singleton class like this

public class Service{

// SingletonHolder is a container class to hold singleton instance 
 private static final SingletonHolder<A> singleton = new SingletonHodler<>(new Service());

 private Service(){
}
  public static Service getInstance(){

//.instance() is a method in SingletonHolder to return singleton instance
    return singleton.instance();
  }

 //Method to start the Service class
  public void start(){
   // start the service
   // get the initial configuration and use the configuration value to speify a URL, something like:
   String initialConfiguration = Configuration.getSettings();
   TargetUrl = initialConfiguration.get.......
}

}

Another class for configuration. This class will initialize the configuration and use configurationUpdate() to get the updated configuration value.

public class Configuration{


    public void configurationInitialize(){

    // initialize the configuration and get the value
    initialConfigValue = ..........    
}


   // Method to update configuration
   public void configurationUpdate(){

   // some mehtod which will get the updated configuration value
   String updateConfiguration = .............

    }    
  //method to retrun configuration settings
  public static String getSettings(){.........}
}

Now I want to get the initial configuration first. Then, get the updatedValue if the configuration is changed.

One of my options is to add a setter method to the Service class. And call the setter method in the configurationUpdate() method of Configuration class, pass the UpdateVlaue to that setter.

I am not sure if it is the proper way to do that. If I add the setter method, is it still a singleton? Will this operation cause any problem? Thank you very much!!!

Edit: Another initialization class will call the configuration class to initialize configuration and also call Service.getInstance().start()


Solution

  • I am not sure if it is the proper way to do that. If I add the setter method, is it still a singleton? Will this operation cause any problem?

    It will still be a singleton. If all the threads should use same targetUrl then it should be fine. If each thread should use it's own targetUrl then a setter changing the value of the targetUrl field will affect all the threads.

    Not sure if this is what is needed, but maybe it give some useful hints.

    Instead of passing the configuration to the service instance, make the configuration available via a ThreadLocal and a static method so the Service class can retrieve the configuration when it needs it (see Configuration#settingsHolder variable and the Configuration#getSettings method)

    NOTE: I didn't use a SingletonHolder as the purpose of it is not clear to me (the way it is described it doesn't provide lazy initialisation and usually an instance holder is used for lazy initialisation purpose)

    public class Service {
    
        private static final Service INSTANCE = new Service();
    
        private static final ThreadLocal<TargetUrl> TARGET_URL_HOLDER = new ThreadLocal();
    
        private Service() {
    
        }
    
        public static Service getInstance() {
            return Service.INSTANCE;
        }
    
        public void start() {
            Settings initialConfiguration = Configuration.getSettings();
            TARGET_URL_HOLDER.set(initialConfiguration.getTargetUrl());
            // some more stuff before starting
        }
    
        public void reinitialize() {
            Settings updatedConfiguration = Configuration.getSettings();
            TARGET_URL_HOLDER.set(updatedConfiguration.getTargetUrl());
            .......
        }
    }
    
    public class Settings {
        private TargetUrl targetUrl;
    
        public TargetUrl getTargetUrl() {
            return this.targetUrl;
        }
    
        public void setTargetUrl(TargetUrl targetUrl) {
            this.targetUrl = targetUrl;
        }
    }
    
    public class Configuration {
        // use threadLocal to store the settings instance so it can be retrieved via a static method (e.g. getSettings)
        private static ThreadLocal<Settings> SETTINGS_HOLDER = new ThreadLocal<>();
    
        public void initialize() {
    
            // initialize the configuration
            Settings initialSettings = ..........
            SETTINGS_HOLDER.set(initialSettings);    
        }
    
        // Method to update configuration
        public void update() {
            // some method which will get the updated configuration value
           Settings updatedSettings = .............
           SETTINGS_HOLDER.set(updatedSettings);
        }
    
        //method to retrun configuration settings
        public static Settings getSettings() {
            return SETTINGS_HOLDER.get();
        }
    }
    
    public class Test {
    
        public static void main(String[] args) {
            Configuration configuration = new Configuration();
            configuration.initialize();
    
            Service service = Service.getInstance();
            service.start();
            // use service instance
            ...................
    
            configuration.update();
    
            service.reinitialize();
    
            // keep using the service instance
           .................
        }
    }
    

    Depending on the use case, if all the threads must use same targetUrl, it might also be implemented using a static variable of type AtomicReference instead of a static variable of type ThreadLocal