From: Nina J. <ni...@ac...> - 2005-09-28 08:52:31
|
> > It's an interesting discussion indeed. What do others think about this? > > Egon > Nice discussion. As for my point of view, I would stick to Occam razor - do not complicate things beyond necessity :) BTW, what are the advantages of the singleton ChemObjectBuilder compared to a single static method somewhere, which can create any object given its class name? (I can think of one - it only calls a constructor without any parameters). But the advantage is there will be no need of adding additional methods for different types; even if a third party object implements a CDK interface it will still work. static Object createObject(String className) throw SomeException { Object object = null; try { Class classDefinition = Class.forName(className); object = classDefinition.newInstance(); } catch (InstantiationException e) { throw new SomeException(x); } catch (IllegalAccessException e) { throw new SomeException(x); } catch (ClassNotFoundException e) { throw new SomeException(x); } return object; } Regards, Nina -- ------------------------------------------------------------------ Assoc. Prof. Dr. Nina Nikolova-Jeliazkova * * Institute for Parallel Processing * * Bulgarian Academy of Sciences * IST Foundation * Acad. G. Bonchev St 25-A * The Bulgarian NREN * 1113 Sofia, Bulgaria * * Tel: +3592 9796616 * * ICQ: 10705013 http://www.ist.bg www: http://ambit.acad.bg/nina ------------------------------------------------------------------ PGP Public Key http://cert.acad.bg/pgp-keys/keys/nina-nikolova-0xEEABA669.asc 8E99 8BAD D804 1A43 27B7 7F87 CF04 C7D1 EEAB A669 ------------------------------------------------------------------ |
From: Egon W. <e.w...@sc...> - 2005-09-28 09:49:56
|
On Wednesday 28 September 2005 11:44 am, Nina Jeliazkova wrote: > Egon Willighagen <e.w...@sc...> wrote: > > The problem with this solution is that you need to know the class name... > > and generally you don't know... > > well, I aggree one doesn't know the class name at compile time. But in > run-time all objects that are passed are class instances, not just > interfaces. So one could always get the class name with smth like this > > ((Classs) object).getClassName(); True, but that only gives you the name of the Class passed, not the name of the Class you want to instantiate... It is used in code like this (all interfaces): public AtomContainer makeLitiumSalt(AtomContainer container) { container.addAtom( container.getBuilder().newAtom("Li") .setFormalCharge(+1) ); return container; } Even if you know the class name of container, you still don't know the class name of the Atom class you need... Egon |
From: Nina J. <ni...@ac...> - 2005-09-28 09:56:11
|
Egon Willighagen <e.w...@sc...> wrote: > On Wednesday 28 September 2005 11:44 am, Nina Jeliazkova wrote: > > Egon Willighagen <e.w...@sc...> wrote: > > > The problem with this solution is that you need to know the class name... > > > and generally you don't know... > > > > well, I aggree one doesn't know the class name at compile time. But in > > run-time all objects that are passed are class instances, not just > > interfaces. So one could always get the class name with smth like this > > > > ((Classs) object).getClassName(); > > True, but that only gives you the name of the Class passed, not the name of > the Class you want to instantiate... > > It is used in code like this (all interfaces): > > public AtomContainer makeLitiumSalt(AtomContainer container) { > container.addAtom( > container.getBuilder().newAtom("Li") > .setFormalCharge(+1) > ); > return container; > } > > Even if you know the class name of container, you still don't know the class > name of the Atom class you need... > > Egon > I was assuming class passed and class to instantiate are the same (e.g. if SetOfMolecules are to be read, SetOfMolecules class is to be instantiated.) Your example is convincing, although I still wonder if there other ways to do the same :) Regards, Nina |
From: Egon W. <e.w...@sc...> - 2005-09-28 10:03:00
|
On Wednesday 28 September 2005 11:56 am, Nina Jeliazkova wrote: > Egon Willighagen <e.w...@sc...> wrote: > I was assuming class passed and class to instantiate are the same (e.g. if > SetOfMolecules are to be read, SetOfMolecules class is to be instantiated.) > > Your example is convincing, although I still wonder if there other ways to > do the same :) Yes, there are... Rich made a suggestion. This was easy for me to implement, and has little impact on existing code. I'm slowly converting code to the new system, making them more able to deal with custom data interfaces implementations. Egon |
From: Nina J. <ni...@ac...> - 2005-09-28 21:08:48
|
Hi Rich, Thanks for replying. It's too late here to read everything carefully, just a single comment at this moment - the piece of code I've cited does work, I have all the clone() mechanism in my project imlemented as a single function like that (lazy I am). Thus the method does not know which class it is cloning at compile time, but it learns it at run time. It seemed to me logical to extend the concept. I have to confess it is not my invention , I have read it somewhere :) Reflection is very powerfull (I was once really impressed), but smart people say it has performance problems if used extensively (it does). "Rich Apodaca" <rap...@oc...> wrote: > On Wed, 28 Sep 2005 11:52:23 +0300, Nina Jeliazkova wrote > > > BTW, what are the advantages of the singleton ChemObjectBuilder > > compared to a single static method somewhere, which can create any > > object given its class name? (I can think of one - it only calls a > > constructor without any parameters). But the advantage is there will > > be no need of adding additional methods for different types; even if > > a third party object implements a CDK interface it will still work. > > > > static Object createObject(String className) throw SomeException { > > Object object = null; > > try { > > Class classDefinition = Class.forName(className); > > object = classDefinition.newInstance(); > > } catch (InstantiationException e) { > > throw new SomeException(x); > > } catch (IllegalAccessException e) { > > throw new SomeException(x); > > } catch (ClassNotFoundException e) { > > throw new SomeException(x); > > } > > return object; > > } > > Hi Nina, > > There are some other disadvantages of the above approach: > > (1) It does not remove any dependencies. Let's say the name of the class > hosting the above method is "ChemObjectFactory". If a piece of code needs to > construct an org.openscience.cdk.Atom it will be done like this: > > Atom atom = ChemObjectFactory.createClass("org.openscience.cdk.Atom"); > > This has the same dependency pattern as: > > Atom atom = new org.openscience.cdk.Atom(); > > In both cases, developer code is tied to org.openscience.cdk.Atom. Because the > payoff of using interfaces is ultimately to remove dependencies on concrete > implementations, this approach fails to achieve the main goal. Not only this, > but a new dependency on ChemObjectFactory has been introduced in the process. if it is called like createClass(((Object) object).getClassName()) no dependency is introduced. BTW, I suppose serialization mechanism in Java uses similar approach. > > The above apporoach could be improved by passing > "org.openscience.cdk.interfaces.Atom" to ChemObjectFactory.createClass. A > private Map instance in ChemObjectFactory could then be used to instantiate > objects based on your above code (I'm not familiar enough with reflection to > know if it will work or not): > > static Object createObject(String interfaceName) throws SomeException > { > Object object = null; > > // map is a private static instance of java.util.Map > // TODO handle case in which map key not found > > String className = (String) map.get(interfaceName); > > try > { > Class classDefinition = Class.forName(className); > object = classDefinition.newInstance(); > } > > catch (InstantiationException e) > { > throw new SomeException(x); > } > > catch (IllegalAccessException e) > { > throw new SomeException(x); > } > > catch (ClassNotFoundException e) > { > throw new SomeException(x); > } > > return object; > } > > One way to get the Map configured would be to use a configuration file > (dictionary) that maps fully-qualified interface names to fully-qualified > implementations. The static initializer of the above class would read this > file and populate its Map accordingly. > > (2) Even with the above changes, using the static createObject approach > precludes third parties from extending it (static methods can not be > overridden). So, when Developer X wants to implement a new mechanism in > ChemObjectFactory.createObject, they have to modify the source code itself. > This is something OO Programming is designed to obviate. > > A more flexible solution would be to define a new interface: > > public interface ChemObjectFactory > { > public Object createObject(String interfaceName); > } > > ... and a concrete implementation: > > public class BasicChemObjectFactory > { > public Object createObject(String interfaceName) > { > //implementation > } > } > > (3) But even with the above changes, this approach suffers from being > ambiguous. How do clients know the set of interfaceName parameters that can be well, clone example was simple, no parameters For example Java Serialization requires a Serializable object to have an empty constructor :) . One can write custom inherited writeObject/readObject methods in order to set some parameters and provide further customization, but usually not a single line of code is needed. > used with createObject? One could use javadoc to spell that out, but we've all > seen how documentation tends to get out of sync with code ;-). Even then, > developers would need to rigorously avoid casting the returned object to a > concrete implementation, or risk getting a ClassCastException. > > (4) Finally, many smart people have spent a lot of time thinking about exactly > this problem. There are several pre-made solutions out there: > > PicoContainer: http://picocontainer.org - this is the one I am most interested > in at the moment. Lightweight and free (as in speech). > > Spring Framework: http://www.springframework.org/ Also free, but I haven't had > a chance to look very closely at it. > > Roll your own with guidance from experts: This article by Martin Fowler gives > some interesting ideas (http://www.martinfowler.com/articles/injection.html). > His "Service Locator" is similar in spirit to the solution discussed in this > email. > > cheers, > rich > I'll study the links for sure (i have to find some time...) Regards, Nina Assoc. Prof. Dr. Nina Nikolova-Jeliazkova * * Institute for Parallel Processing * * Bulgarian Academy of Sciences * IST Foundation * Acad. G. Bonchev St 25-A * The Bulgarian NREN * 1113 Sofia, Bulgaria * * Tel: +3592 9796616 * * ICQ: 10705013 http://www.ist.bg www: http://ambit.acad.bg/nina ------------------------------------------------------------------ PGP Public Key http://cert.acad.bg/pgp-keys/keys/nina-nikolova-0xEEABA669.asc 8E99 8BAD D804 1A43 27B7 7F87 CF04 C7D1 EEAB A669 ------------------------------------------------------------------ |
From: Rich A. <rap...@oc...> - 2005-09-29 03:52:20
|
On Thu, 29 Sep 2005 00:08:40 +0300, Nina Jeliazkova wrote > Hi Rich, > > Thanks for replying. It's too late here to read everything carefully, > just a single comment at this moment - the piece of code I've > cited does work, I have all the clone() mechanism in my project > imlemented as a single function like that (lazy I am). Thus the > method does not know which class it is cloning at compile time, but > it learns it at run time. It seemed to me logical to extend the > concept. Thanks, nina -this is very useful to know. > > (2) Even with the above changes, using the static createObject approach > > precludes third parties from extending it (static methods can not be > > overridden). So, when Developer X wants to implement a new mechanism in > > ChemObjectFactory.createObject, they have to modify the source code itself. > > This is something OO Programming is designed to obviate. > > > > A more flexible solution would be to define a new interface: > > > > public interface ChemObjectFactory > > { > > public Object createObject(String interfaceName); > > } > > > > ... and a concrete implementation: > > > > public class BasicChemObjectFactory > > { > > public Object createObject(String interfaceName) > > { > > //implementation > > } > > } > > > > (3) But even with the above changes, this approach suffers from being > > ambiguous. How do clients know the set of interfaceName parameters that can > be > > well, clone example was simple, no parameters > For example Java Serialization requires a Serializable object to > have an empty constructor :) . One can write custom inherited > writeObject/readObject methods in order to set some parameters and > provide further customization, but usually not a single line of code > is needed. I'm not sure I follow. Can you give a complete example? cheers, rich |
From: Egon W. <e.w...@sc...> - 2005-09-28 09:28:59
|
On Wednesday 28 September 2005 10:52 am, Nina Jeliazkova wrote: > BTW, what are the advantages of the singleton ChemObjectBuilder compared to > a single static method somewhere, which can create any object given its > class name? (I can think of one - it only calls a constructor without any > parameters). When you have classes with static methods, you still need to choose an implementation, whereas if one uses .getBuilder() from the ChemObject interface you don't need to know. > But the advantage is there will be no need of adding additional methods for > different types; even if a third party object implements a CDK interface it > will still work. > > static Object createObject(String className) throw SomeException { > Object object = null; > try { > Class classDefinition = Class.forName(className); > object = classDefinition.newInstance(); > } catch (InstantiationException e) { > throw new SomeException(x); > } catch (IllegalAccessException e) { > throw new SomeException(x); > } catch (ClassNotFoundException e) { > throw new SomeException(x); > } > return object; > } The problem with this solution is that you need to know the class name... and generally you don't know... A good example is the IO in ChemObjectReader: public ChemObject read(ChemObject object); Both ChemObject's here are interfaces, allowing one to pass a custom ChemObject implementation (CMLReader and CDK-Taverna are users of custom implementations), like in: ChemObjectReader reader = new MDLReader(); ChemFile customFile = new MyCustomChemFile(); reader.read(customFile); // is equivalent to: customFile = reader.read(customFile) Consequently, the ChemObjectReader know *nothing* about which classes to use... it only sees the ChemObject -interface-. However, since only recently, we have the method cdk.interfaces.ChemObject.getBuilder() which actually returns a ChemObjectBuilder for the specific implementation of the interfaces used. In CDK CVS, currently only one complete implementation of the data interfaces exist, being the classes we all used in the past five years. CMLReader uses a custom ChemFile [1], but really extends on the default implementation. CDK-Taverna does the same with a different customized ChemFile class [2]. Consequently, all use the same ChemObjectBuilder implementation [3]. Egon 1. http://cvs.sf.net/viewcvs.py/cdk/cdk-taverna/src/org/openscience/cdk/applications/taverna/CMLChemFile.java 2. http://cvs.sf.net/viewcvs.py/cdk/cdk/src/org/openscience/cdk/io/cml/ChemFileCDO.java 3. http://cvs.sf.net/viewcvs.py/cdk/cdk/src/org/openscience/cdk/DefaultChemObjectBuilder.java |
From: Nina J. <ni...@ac...> - 2005-09-28 09:48:38
|
forgot to cc the list ... > When you have classes with static methods, you still need to choose an > implementation, whereas if one uses .getBuilder() from the ChemObject > interface you don't need to know. > > > But the advantage is there will be no need of adding additional methods for > > different types; even if a third party object implements a CDK interface it > > will still work. > > > > static Object createObject(String className) throw SomeException { > > Object object = null; > > try { > > Class classDefinition = Class.forName(className); > > object = classDefinition.newInstance(); > > } catch (InstantiationException e) { > > throw new SomeException(x); > > } catch (IllegalAccessException e) { > > throw new SomeException(x); > > } catch (ClassNotFoundException e) { > > throw new SomeException(x); > > } > > return object; > > } > > The problem with this solution is that you need to know the class name... and > generally you don't know... well, I aggree one doesn't know the class name at compile time. But in run-time all objects that are passed are class instances, not just interfaces. So one could always get the class name with smth like this ((Classs) object).getClassName(); //haven't checked although :) -- ------------------------------------------------------------------ Assoc. Prof. Dr. Nina Nikolova-Jeliazkova * * Institute for Parallel Processing * * Bulgarian Academy of Sciences * IST Foundation * Acad. G. Bonchev St 25-A * The Bulgarian NREN * 1113 Sofia, Bulgaria * * Tel: +3592 9796616 * * ICQ: 10705013 http://www.ist.bg www: http://ambit.acad.bg/nina ------------------------------------------------------------------ PGP Public Key http://cert.acad.bg/pgp-keys/keys/nina-nikolova-0xEEABA669.asc 8E99 8BAD D804 1A43 27B7 7F87 CF04 C7D1 EEAB A669 ------------------------------------------------------------------ |
From: Rich A. <rap...@oc...> - 2005-09-28 15:54:21
|
On Wed, 28 Sep 2005 11:52:23 +0300, Nina Jeliazkova wrote > BTW, what are the advantages of the singleton ChemObjectBuilder > compared to a single static method somewhere, which can create any > object given its class name? (I can think of one - it only calls a > constructor without any parameters). But the advantage is there will > be no need of adding additional methods for different types; even if > a third party object implements a CDK interface it will still work. > > static Object createObject(String className) throw SomeException { > Object object = null; > try { > Class classDefinition = Class.forName(className); > object = classDefinition.newInstance(); > } catch (InstantiationException e) { > throw new SomeException(x); > } catch (IllegalAccessException e) { > throw new SomeException(x); > } catch (ClassNotFoundException e) { > throw new SomeException(x); > } > return object; > } Hi Nina, There are some other disadvantages of the above approach: (1) It does not remove any dependencies. Let's say the name of the class hosting the above method is "ChemObjectFactory". If a piece of code needs to construct an org.openscience.cdk.Atom it will be done like this: Atom atom = ChemObjectFactory.createClass("org.openscience.cdk.Atom"); This has the same dependency pattern as: Atom atom = new org.openscience.cdk.Atom(); In both cases, developer code is tied to org.openscience.cdk.Atom. Because the payoff of using interfaces is ultimately to remove dependencies on concrete implementations, this approach fails to achieve the main goal. Not only this, but a new dependency on ChemObjectFactory has been introduced in the process. The above apporoach could be improved by passing "org.openscience.cdk.interfaces.Atom" to ChemObjectFactory.createClass. A private Map instance in ChemObjectFactory could then be used to instantiate objects based on your above code (I'm not familiar enough with reflection to know if it will work or not): static Object createObject(String interfaceName) throws SomeException { Object object = null; // map is a private static instance of java.util.Map // TODO handle case in which map key not found String className = (String) map.get(interfaceName); try { Class classDefinition = Class.forName(className); object = classDefinition.newInstance(); } catch (InstantiationException e) { throw new SomeException(x); } catch (IllegalAccessException e) { throw new SomeException(x); } catch (ClassNotFoundException e) { throw new SomeException(x); } return object; } One way to get the Map configured would be to use a configuration file (dictionary) that maps fully-qualified interface names to fully-qualified implementations. The static initializer of the above class would read this file and populate its Map accordingly. (2) Even with the above changes, using the static createObject approach precludes third parties from extending it (static methods can not be overridden). So, when Developer X wants to implement a new mechanism in ChemObjectFactory.createObject, they have to modify the source code itself. This is something OO Programming is designed to obviate. A more flexible solution would be to define a new interface: public interface ChemObjectFactory { public Object createObject(String interfaceName); } ... and a concrete implementation: public class BasicChemObjectFactory { public Object createObject(String interfaceName) { //implementation } } (3) But even with the above changes, this approach suffers from being ambiguous. How do clients know the set of interfaceName parameters that can be used with createObject? One could use javadoc to spell that out, but we've all seen how documentation tends to get out of sync with code ;-). Even then, developers would need to rigorously avoid casting the returned object to a concrete implementation, or risk getting a ClassCastException. (4) Finally, many smart people have spent a lot of time thinking about exactly this problem. There are several pre-made solutions out there: PicoContainer: http://picocontainer.org - this is the one I am most interested in at the moment. Lightweight and free (as in speech). Spring Framework: http://www.springframework.org/ Also free, but I haven't had a chance to look very closely at it. Roll your own with guidance from experts: This article by Martin Fowler gives some interesting ideas (http://www.martinfowler.com/articles/injection.html). His "Service Locator" is similar in spirit to the solution discussed in this email. cheers, rich |