Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return same instance of an object each time

This is the situation, I have a param value and based on that I need to return a class instance. I want that on each invocation I return the same object.

obj1 and obj2 implement the same interface.

class Engine {
    public commonInterface getObj(int param) {
        switch(param) {
            case 1 : return obj1;
            case 2 : return obj2;
        }
    }
}

How do I achieve this? I understand it's within a JVM.

like image 562
user1071840 Avatar asked Jul 04 '26 22:07

user1071840


2 Answers

Here is one way to do it, but it isn't optimal:

This isn't the best way, mainly because the creation of the objects isn't restricted, for it to be optimal the MyObject constructor needs to be private and the instances need to be on itself.

Initializing the references in the static {} block is a better practice than inline because you can handle Exceptions and other logic before you initialize the references, this is a best practice.

But this is what you see in a lot of quick and dirty code that is mostly incorrect because the reasons below in the better solution.

class Engine {

private static final MyObject OBJ1;
private static final MyObject OBJ2;

static
{
    OBJ1 = new MyObject();
    OBJ2 = new MyObject();
}

public MyObject getObj(final int param)
{
    switch (param)
    {
        case 1:
            return Engine.OBJ1;
        case 2:
            return Engine.OBJ2;
        default:
            throw new IllegalArgumentException(String.format("%d is not a valid object id", param));
    }
}

Here is a better way:

This is better because it hides the implementation. Having a inner class allows you to make the class public but make the constructor private and control access to the creation of the MyObject instances.

public class Engine 
{
    private static final MyObject OBJ1;
    private static final MyObject OBJ2;

    static
    {
        OBJ1 = new MyObject1();
        OBJ2 = new MyObject2();
    }

    public MyObject getObj(final int param)
    {
        switch (param)
        {
            case 1:
                return Engine.OBJ1;
            case 2:
                return Engine.OBJ2;
            default:
                throw new IllegalArgumentException(String.format("%d is not a valid object id", param));
        }
    }

    public static class MyObject1 implements MyObject { private MyObject1() {} } 
    public static class MyObject2 implements MyObject { private MyObject2() {} } 

}

Best long term solution, using the most modern idiom at this time:

This solutions is more verbose, but also the most correct in enforcing the semantics of a Singleton. This method guarrantees that the Singleton contract in the current JVM. It also scales to multiple classloaders and multiple JVMs but allowing you to just change the MBeanServer to a different implemenation, and looks up the references remotely without changing any other code. DI/IoC is intentionally ignored here for simplicity.

import javax.management.InstanceAlreadyExistsException;
import javax.management.JMX;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;

public class Engine
{
    private static final MyObject OBJ1;
    private static final MyObject OBJ2;

    static
    {
        try
        {
            final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
            final ObjectName on1 = new ObjectName("com.example:type=MyObject1");
            final ObjectName on2 = new ObjectName("com.example:type=MyObject2");
            OBJ1 = JMX.newMBeanProxy(mbs, on1, MyObject.class);
            OBJ2 = JMX.newMBeanProxy(mbs, on2, MyObject.class);
        }
        catch (MalformedObjectNameException e) { throw new RuntimeException(e); }
    }

    public MyObject getObj(final int param)
    {
        switch (param)
        {
            case 1:
                return Engine.OBJ1;
            case 2:
                return Engine.OBJ2;
            default:
                throw new IllegalArgumentException(String.format("%d is not a valid object id", param));
        }
    }

    public static class MyObject1 implements MyObjectMBean
    {
        static
        {
            try
            {
                final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
                final ObjectName on1 = new ObjectName("com.example:type=MyObject1");
                mbs.registerMBean(new MyObject1(), on1);
            }
            catch (MalformedObjectNameException e) { throw new RuntimeException(e); }
            catch (NotCompliantMBeanException e) { throw new RuntimeException(e); }
            catch (InstanceAlreadyExistsException e) { throw new RuntimeException(e); }
            catch (MBeanRegistrationException e) { throw new RuntimeException(e); }
        }

        private MyObject1() { /* your interface implementations go here */ }
    }

    public static class MyObject2 implements MyObjectMBean
    {
        static
        {
            try
            {
                final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
                final ObjectName on1 = new ObjectName("com.example:type=MyObject2");
                mbs.registerMBean(new MyObject2(), on1);
            }
            catch (MalformedObjectNameException e) { throw new RuntimeException(e); }
            catch (NotCompliantMBeanException e) { throw new RuntimeException(e); }
            catch (InstanceAlreadyExistsException e) { throw new RuntimeException(e); }
            catch (MBeanRegistrationException e) { throw new RuntimeException(e); }
        }

        private MyObject2() { /* your interface implementations go here */ }
    }

    private static interface MyObjectMBean extends MyObject { /* mbean specific methods go here */ }
    private static interface MyObject { /* your interface methods go here */ }
}

The added benefit of using the JMX approach is you can manage these instances with JConsole very easily, while the application is running, you get that for free!

NOTE: I used a single file with the interfaces as inner classes for expediency, they could be in their own files without a problem.

Make the two instances static, and instantiate them once, and return only them. This is similar to a Singleton design pattern.

class Engine {
    private static commonInterface obj1 = ...
    private static commonInterface obj2 = ...
    public commonInterface getObj(int param) {
        switch(param) {
            case 1 : return obj1;
            case 2 : return obj2;
        }
    }
}
like image 35
Sergey Kalinichenko Avatar answered Jul 07 '26 12:07

Sergey Kalinichenko



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!