Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static Factory: Java Design Issue

I have a Base interface that defines some common functionality. Now this base interface can be implemented by more than 50 classes each having some different additional methods. I want to have a static factory that will return an instance of any one of 50 classes depending on a parameter pass to that static factory method.

public interface Base {
     public void a();
     public void b();
}

public class myclass implements Base {
     //   a and b implementation

     public String c() {
     }
     public String d() {
     }
}

public class secondclass implements Base {
     //   a and b implementation

     public String e() {
     }
     public String f() {
     }
}

How can I implement a static factory method. I am unsure of return type

  public synchronized static {return type} getInstance(String arg0) {
         // do something and return any one class based on arg0
  }

EDITS: A scenario

If the passed parameter is 300 I want to return object of class myclass

If parameter is 900 I want to return object of class secondclass

And so on. having so many conditions is not feasible.

NOT TO DO

public synchronized static {return type} getInstance(String arg0) {
   // do something and return any one class based on arg0
   if(arg0.equals("300")) {
       return new myclass();

   }

}

REASON: This will not be salable. I am developing a public API and class list may increase to 100 or may be 500. I hope my question is more clear now.

UPDATE: Very useful points highlighted here and this is what I thought of

public abstract class Base {

      public abstract void a();
      public abstract void b();

      public synchronized staticabstract Base getInstance(String arg0);

}


public class myclass extends Base {

     private myclass() {}    // private constructor not publicly instantiate 
     // a and b implementation

     public void c();
     public void d();

     @Override
     public synchronized static abstract Base getInstance(String arg0) {
           if(arg0.equalsIgnoreCase("300")) {      // only 1 if condition
                return new myclass();
           }
     }
}



public class secondclass extends Base {

     private secondclass() {}    // private constructor not publicly instantiate 
     // a and b implementation

     public void e();
     public void f();

     @Override
     public synchronized static abstract Base getInstance(String arg0) {
           if(arg0.equalsIgnoreCase("900")) {      // only 1 if condition
                return new secondclass();
           }
     }
}



Client side:
one applicaiton
Base b=Base.getInstance(300);
if(b instanceof myclass) {

}

second application

Base b=Base.getInstance(900);
if(b instanceof secondclass) {

}
like image 650
user123 Avatar asked Mar 21 '23 00:03

user123


2 Answers

A better approach would be to generify the static method:

static <T extends Base> T create(Class<T> type){
  // create concrete instance of type T and return it
}

so now the create method is parameterized by the actual concrete type Class object. You can use type to create the appropriate sub-type, either via reflection or using nested if statements.

Using generics has the advantage of giving you strong type safety for free, so you can avoid unchecked casts and go home after work with no worries.

EDIT: as an example if you had a no-arg constructor in each sub-class you could implement the above as:

return type.newInstance();

EDIT 2: To sum up the discussion, if the factory can't take the type or the logic to create the object depends in a non trivial way on the value of the argument, the only option is the following:

static Base create(String someParameter){
  // create the concrete class and return it
}

And the client code will look like:

Base b = create("myArgument");
if(b instanceof SomeDerived){
  SomeDerived d = (SomeDerived) b;
  // use d
} else if(b instanceof OtherDerived){
  // you get the idea
}

Not ideal but there's no way to generify the create method without an instance of Class<? extends Base>.

like image 162
Giovanni Botta Avatar answered Mar 22 '23 15:03

Giovanni Botta


One of the relevant reasons of using static factory over constructors (quote from Effective Java):

A third advantage of static factory methods is that, unlike constructors, they can return an object of any subtype of their return type

So, essentially it can give you ability to write something like :

Base x = Factory.getInstance(TypeX.class); 
x.methodOfBase();

where TypeX could be completely hidden and have private constructor.

You may want to have a look at java.util.Collections API implementation, for instance consider static factory method :

public static <T> List<T> synchronizedList(List<T> list)

as you see return type is an interface, and therefore returned object(some hidden implementation of List) could only be used as a List.

So if you want to use methods x1(), x2() which are exclusive to subtype TypeX and do not belong to Base interface, you will have to cast your object to TypeX anyway.
In this case you can instantiate TypeX directly via constructor (by keeping it public), as static factory won't give you anything.

You may also want to have a look at other 3 reasons to use static factory over constructors in the book and verify that non of them are applicable to what you described in your question so far.

like image 35
kiruwka Avatar answered Mar 22 '23 14:03

kiruwka