• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Factory pattern new instance

 
Ranch Hand
Posts: 170
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Suppose I have a Factory class like



where InstanceA,InstanceB,InstanceC are all subclass .  If I think there are too many if/else and want to create a HashMap whose key is the var value, and value is the correspecding new Instance, and store the Hashmap as static class variable .  So when I do myMap.get("a"), I expect it to return me the new InstanceA()...  But the problem with that is, each time I want a NEW instance to be created.  But with that Hashmap approach, it seems I can only get the single same pre-created InstanceA every time and that's not what I want.   How do I simplify the if/else without compromising the new instance each time ?
 
Saloon Keeper
Posts: 10705
86
Eclipse IDE Firefox Browser MySQL Database VI Editor Java Windows ChatGPT
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
It doesn't seem to me that a bunch of else-if's should be an issue. If you end up with gobs of different classes to instantiate then you might need to examine your design to see if there would be a better overall approach. You could replace the else-ifs with a switch statement which might be cleaner.

There might be a way with Java-8 to do it with functions and a hash map but I'm not strong enough in that area to make a reccomendation.
 
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Functional interfaces and method references to the rescue. I would use a Map<String, Supplier<MyClass>> like so:


You can see it run here: https://repl.it/@jlacar/MapBasedFactory
 
Marshal
Posts: 79178
377
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You can also pass the name of the class to Class.forName(...).nextInstance(); but that is an error‑prone way to create instances. It also requires the class have a public no‑args constructor.
String className = "Instance" + "A";

[edit]Somebody pointed out I got the name of the method wrong. It should be newInstance().

 
Linwood Hayes
Ranch Hand
Posts: 170
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Junilu Lacar wrote:Functional interfaces and method references to the rescue. I would use a Map<String, Supplier<MyClass>> like so:


You can see it run here: https://repl.it/@jlacar/MapBasedFactory




This looks really neat.   But what if my FooA, FooB, FooC constructor all take an argument (i.e. I should return new FooA(input) or new FooB(input)) ?  How should I modify your code to do that ?  Thanks.
 
Junilu Lacar
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You can use lambda expressions. I'll leave it to you as an exercise to figure out the details.
 
Bartender
Posts: 5465
212
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hmmm... wonder what Junilu means here.

Meanwhile, I gave it a shot, a HashMap that generates an instance with input (even input that differs per class), and after a lot of struggle I got it running. But as we say in Holland:

churgery: succes
patient: dead

The if/else construct is way, way easier and way more flexible. Nevertheless, it was a nice exercise, kept me pleasantly busy for an hour or so.

By the way: if I run my code, I get this warning:

Anyone knows what my IDE is talking about?
Here's the code:
 
Sheriff
Posts: 22783
131
Eclipse IDE Spring VI Editor Chrome Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Piet Souris wrote:


That cast to List<String> is unsafe because at runtime the JVM can only check that it's a List, not that its generic type is String.
 
Carey Brown
Saloon Keeper
Posts: 10705
86
Eclipse IDE Firefox Browser MySQL Database VI Editor Java Windows ChatGPT
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I haven't completely thought this through but could you pass in a Builder object instead of Object... ?
 
Carey Brown
Saloon Keeper
Posts: 10705
86
Eclipse IDE Firefox Browser MySQL Database VI Editor Java Windows ChatGPT
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Here's a stab at creating various Foo objects depending on the parameters that are set in a FooBuilder object. Seems a little verbose to me.
 
Carey Brown
Saloon Keeper
Posts: 10705
86
Eclipse IDE Firefox Browser MySQL Database VI Editor Java Windows ChatGPT
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Leaning more towards a traditional Factory seems cleaner and more efficient.
 
Junilu Lacar
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
It's fun to let your imagination run wild but neither of the alternative solutions offered is what OP was asking.

Linwood Hayes wrote:what if my FooA, FooB, FooC constructor all take an argument (i.e. I should return new FooA(input) or new FooB(input)) ?


That is, OP wants to know how he would implement something that is called like this:
 
Junilu Lacar
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Ok, I guess it's time to show my hand...


I had initially thought you'd need to use lambdas but found that you can use Function<T, R> with the same constructor reference.

Sample program has been updated to reflect this variation: https://repl.it/@jlacar/MapBasedFactory
 
Piet Souris
Bartender
Posts: 5465
212
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
@Rob

thanks! I hadn't seen this warning before, as far as I can remember.

I'm curious if OP thinks this is all useful.
 
Junilu Lacar
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Piet Souris wrote:]
I'm curious if OP thinks this is all useful.


I hope he does but it's also likely he's already moved on to other things outside the Ranch.

It was a fun exercise nonetheless and I even learned a thing or two.
 
Linwood Hayes
Ranch Hand
Posts: 170
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Junilu,  I am still new to java.  I have run and test the code you gave and I have a basic question to ask -- I do find the Supplier<Foo> example you gave returned a different instance each time when I implemented it in hashMap in your way.  And if I don't use Supplier and just use map.put("A", new FooA()), it returns same instance always.  I don't understand exactly why.  Could you help explain this ? Didn't you also put a FooA:new as I put a new FooA() ? why it causes difference ?
 
Junilu Lacar
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You should read the Java Tutorial on Lambda Expressions, Functional Interfaces, and Method References to get a basic understanding of what the code in that example does.  When you add new FooA() to a map, you're adding a single instance of an object. Getting it back out of the map does not create any new instances.  On the other hand, Foo::new is a method reference that gets turned into an implementation of a Functional Interface.  Explaining these concepts is going to be way too long, so it's best that you go to the tutorials to bone up on those concepts.

Short version: FooA::new is NOT equivalent to new FooA(). There's a whole lot of "magic" that happens when the compiler processes FooA::new and what happens depends a lot on the context in which it is used.

In the case of noArgs.put("A", FooA::new), since noArgs is declared as a Map<String, Supplier<Foo>>, the Supplier<Foo> part dictates that FooA::new is compiled into something that is essentially this:

This is why you have noArgs.get(kind).get() as the return value for the factory method FooFactory.get(String kind).

In the case of withArg.put("A", FooA::new), since withArg is declared as a Map<String, Function<String, Foo>>, the Function<String, Foo> part dictates that FooA::new is compiled into something that is essentially this:

This is why you have withArg.get(kind).apply(value) as the return value for the factory method FooFactory.get(String kind, String value).
 
Linwood Hayes
Ranch Hand
Posts: 170
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you so much Julinu !
 
reply
    Bookmark Topic Watch Topic
  • New Topic