?
Solved

Forcing object creation through a factory

Posted on 2006-10-30
29
Medium Priority
?
564 Views
Last Modified: 2008-02-01
In Java, how does one force someone to call a factory in order to create an object of some type (i.e. MyFactory.createObjectA()) and have it so they cannot create an instance of that object without the factory?
0
Comment
Question by:DiamonDogX
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 8
  • 5
  • 5
  • +2
29 Comments
 
LVL 6

Accepted Solution

by:
valipotor earned 100 total points
ID: 17834131
You cannot force something like that.
Even if the constructor is private() the class can still be instantiated using introspection.

The coder must obey the rules of creating the object throgh a factory, in order for the app to be extended or patch without the need to recompile it.

0
 
LVL 20

Assisted Solution

by:Venabili
Venabili earned 100 total points
ID: 17834191
Or if you do not trust the developer - make your constructor to determine which is the calling class and initialise only if it is the factory
This was working in 1.4.2
http://www.aptana.com/dev/index.php/Determining_the_calling_method_in_Java
I think there was a better way in Java 5, but let me try to find it

However - I would simply not do it... :) The Developer is supposed to use the thing in the way specified by the API...
0
 
LVL 11

Expert Comment

by:dbkruger
ID: 17834426
If the constructor is private, then introspection will fail to acquire the method. You should be able to use that method to restrict who can call it.
0
Get 15 Days FREE Full-Featured Trial

Benefit from a mission critical IT monitoring with Monitis Premium or get it FREE for your entry level monitoring needs.
-Over 200,000 users
-More than 300,000 websites monitored
-Used in 197 countries
-Recommended by 98% of users

 
LVL 20

Expert Comment

by:Venabili
ID: 17834441
If the constructor is private, how the Factory will call it? Because from what I read here, The Asker needs a second class to create objects from his class... :)
0
 
LVL 11

Expert Comment

by:dbkruger
ID: 17834457
public class TestIntrospect {
      public static void main(String args[]) {
            Class c = Foo.class;
            try {
                  Object x = c.newInstance();
            } catch (Exception e) {
                  System.out.println(e);
            }
      }

}
class Foo {
      private Foo() { System.out.println("Can't call me!"); }
}


java.lang.IllegalAccessException: Class TestIntrospect can not access a member of class Foo with modifiers "private"
0
 
LVL 20

Expert Comment

by:Venabili
ID: 17834468
Yep - the private constructor cannot be called from outside at all - this is for sure. But the Asker needs a Factory class to call it. :)
0
 
LVL 11

Expert Comment

by:dbkruger
ID: 17834475
> If the constructor is private, how the Factory will call it? Because from what I read here, The Asker needs a second class to create objects from his class... :)

In C++ you would use a friend. In Java, since that's not available, you use an inner class.

public class NotToBeInstantiatedByItself {
  private NotToBeInstantiatedByItself( ... ) { .. }


  public Factory gimmeAFactory() { ..}

  public static class FooFactory extends Factory {

  }
}
0
 
LVL 20

Expert Comment

by:Venabili
ID: 17834508
The problem is that the Asker do not ask for inner classes. :) He asks for 2 separate classes...
The inner classes are good solution. To some extent... But not the perfect one.

Basically Java do not have the friends concept - sometimes I really miss it. Yes - you can go very close but not really to make it the same...

Protected constructor for class A and Factory class in the same package is what I usually use in such cases. And good documentation explaining how to create objects of the class. Usually works.
0
 
LVL 6

Expert Comment

by:valipotor
ID: 17834690
To dbkruger & Venabili:

I wanted to say that a class can be instantiated even if the constructor was made private.

For example:


 Class a = A.class;
      try {
         Constructor contst = a.getDeclaredConstructor();
         contst.setAccessible(true);
         contst.newInstance();
      } catch (IllegalArgumentException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } catch (InstantiationException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } catch (IllegalAccessException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } catch (InvocationTargetException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } catch (SecurityException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } catch (NoSuchMethodException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }

where class a is defined :

public class A {

   private A()
   {
      System.out.println("prevent instantiation");
   }
}
0
 
LVL 5

Expert Comment

by:kannan_ekanath
ID: 17834723
valipotor

Your argument is sorta wrong.
 contst.setAccessible(true);

consults the JVM security manager to check if private access of fields is allowed. However in typical production environment or for that matter in most app-servers, these flags are set to the level of security where private accesses of field will result in IllegalAccessException
0
 

Author Comment

by:DiamonDogX
ID: 17834800
So basically the closest solution is to put the factory and the object you want to instantiate via the factory (with a protected constructor) in the same package and then just call the factory method from anywhere... ?
0
 
LVL 20

Expert Comment

by:Venabili
ID: 17834822
Yep.
Or in the constructor of class A check who is the calling class and method and do something based on this. But I personally had never really done this :)
0
 
LVL 11

Expert Comment

by:dbkruger
ID: 17834918
I would put it in the class wth a private constructor, as my example showed. If you put it in the package, anyone can write a class in that package and you have allowed them access.

If you use protected, any child could get it as well. If we could all declare who our parents are, Donald Trump and Queen Elizabeth would probably have a lot of self-declared "children" hoping to inherit.
0
 
LVL 11

Expert Comment

by:dbkruger
ID: 17834937
Venabili --
You don't need to write the class as an inner class, you only need a tiny wrapper which instantiates the object in question and calls the generic factory.
But I think my post above highlights why you cannot sensibly use protected....
0
 
LVL 5

Expert Comment

by:kannan_ekanath
ID: 17834981
A factory is no different from any other java class. In fact you are hitting one of the design patterns called "Singleton Pattern"

http://www.fluffycat.com/Java-Design-Patterns/Factory-Method/

Let me suggest smething I found best. Integrate the Factory class into the same class; For example,

class SomeImportantObject {
     SomeImportantObject instance = new SomeImportantObject();

     private SomeImportantObject() {
        // no one other than this class can use it
     }

     public static SomeImportantObject getSingletonObject() {
          return instance;
     }
}
0
 
LVL 5

Expert Comment

by:kannan_ekanath
ID: 17835004
The above is something i found best, since most design experts would call "Singleton pattern" as somethign sort of "Anti pattern"

To put the argument in a different way, a singleton pattern if used badly beats the purpose of "Data encapsulation". If you have a class like above then it is equivalent to having a *DECLARED GLOBAL OBJECT* in C, C++ etc

Imagine, anyone can take that importantobject any time they want, call methods on it....

If your application needs it, then use it but use it sparingly :)
0
 

Author Comment

by:DiamonDogX
ID: 17835898
What seems to break encapsulation is reflection.  I am able to call private constructors and private methods of a class from outside of it!  It feels like hacking... I can call private methods in the java API classes through reflection as well.  Is there anything I'm missing?  Doesn't this type of thing seem vulnerable?
0
 
LVL 11

Expert Comment

by:dbkruger
ID: 17836335
I don't know how your permissions are set, but as I showed above, I could  not break encapsulation. If you have the code permissions set up properly, which they are by default, this should not happen. Reflection does not get you code that you could not otherwise access.
0
 

Author Comment

by:DiamonDogX
ID: 17836414
But I can set them to whatever I want... therefore, I can call the private methods...

Class c = MyClass.class;
Method m = c.getDeclaredMethods[ 0 ];
m.settAccessible( true );
m.invoke( null, null );
0
 
LVL 5

Expert Comment

by:kannan_ekanath
ID: 17840067
m.settAccessible( true );

This consults the security manager. Your security managers *allows* it. That was my previous comment :)

check the source code here,
http://docjar.com/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible(boolean)

It consults reflect permission. Google it if you want to know how to turn it on/off
0
 

Author Comment

by:DiamonDogX
ID: 17842291
I understand that this concerns the security manager... my point is that I CAN turn this on or off and invoke private methods.  How does this work with the Java API classes?  I listed the methods for GridBagLayout.class for example and I saw a private method called removeConstraints() that I was able to invoke.  What else am I missing here?  Obviously the Java API classes are secure...
0
 
LVL 11

Expert Comment

by:dbkruger
ID: 17843273
Your code obviously cannot even compile, so I suggest you compile it before claiming it works.
A constructor is not a method
You have to have an object in order to invoke a method on it

Your code is below. I fixed it and tried to get the constructor, and was told it did not exist, the exception comes on the getConstructor method, so you don't even have the opportunity to try to set it.


>Class c = MyClass.class;
>Method m = c.getDeclaredMethods[ 0 ]; // WRONG
>m.settAccessible( true );
>m.invoke( null, null );

Correct (but fails on getConstructor)

import java.lang.reflect.*;


public class TestIntrospect {
      public static void main(String args[]) {
            Class c = Foo.class;
            try {
                  //Object x = c.newInstance();
                  Class[] parameterTypes = new Class[0];
                  Constructor con = c.getConstructor(parameterTypes);
                  con.setAccessible( true );
                  Object[] empty = new Object[0];
                  Object f = con.newInstance(empty);

            } catch (Exception e) {
                  System.out.println(e);
            }
      }

}
class Foo {
      private Foo() { System.out.println("Can't call me!"); }
}
0
 
LVL 6

Expert Comment

by:valipotor
ID: 17844498
As I have already posted:

Class a = A.class;
      try {
         Constructor contst = a.getDeclaredConstructor();
         contst.setAccessible(true);
         contst.newInstance();
      } catch (Throwable e) {
         e.printStackTrace();
      }

where class a is defined :

public class A {

   private A()
   {
      System.out.println("Can't call me? ");
   }
}
0
 

Author Comment

by:DiamonDogX
ID: 17844530
I was just about to post the exact same thing valipotor :).  It definitely compiles, and definitely prints out "Can't call me? "
0
 
LVL 6

Expert Comment

by:valipotor
ID: 17844542
Yes, it is a matter of security manager.

As kannan_ekanath said:

>> This consults the security manager. Your security managers *allows* it.
0
 
LVL 11

Expert Comment

by:dbkruger
ID: 17845004
Sorry, copied the wrong code of yours. The other one doesn't compile. My environment does stop you from doing this.
0

Featured Post

New feature and membership benefit!

New feature! Upgrade and increase expert visibility of your issues with Priority Questions.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction This article is the last of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article covers our test design approach and then goes through a simple test case example, how …
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
Viewers will learn about if statements in Java and their use The if statement: The condition required to create an if statement: Variations of if statements: An example using if statements:
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …
Suggested Courses
Course of the Month12 days, 17 hours left to enroll

777 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question