The Builder Pattern can be used as a dependency inector to eliminate the deadlock during initialization. Since the Chicken
class depends on an interface ( Egg
), which itself cannot be instantiated, the pattern mentioned in this question can be applied.
There are 3 versions of introducing it for this example. The first is simpler, but it also gives the possibility to access the half-done Chicken
class. In each version more and more ambiguity is eliminated at the cost of complexity.
public class MyClass {
/**
* Handles eggs
* */
public static class Chicken{
private Egg[] eggs;
private Chicken(){}
private void setEggs(Egg... eggs_){ eggs = eggs_; }
public void accept_signal(String message){ /* ... */ }
public static class Builder{
Chicken momma;
public Builder(){
momma = new Chicken();
}
public Chicken build(Egg... eggs){
momma.setEggs(eggs);
return momma;
}
public Chicken get(){
return momma;
}
}
}
/**
* Signals when its ready to hatch
* */
public static abstract class Egg{
private final Chicken parent;
public Egg(Chicken parent_){
parent = parent_;
}
public void hatch(){
parent.accept_signal("chirp chirp");
}
}
/**
* The resulting children to be handled
* */
public static class YellowChick extends Egg{
public YellowChick(Chicken parent_){
super(parent_);
}
}
public static class BrownChick extends Egg{
public BrownChick(Chicken parent_){
super(parent_);
}
}
public static class UglyDuckling extends Egg{
public UglyDuckling(Chicken parent_){
super(parent_);
}
}
public static void main (String[] args){
Chicken.Builder mommaBuilder = new Chicken.Builder();
Chicken momma = mommaBuilder.build(
new YellowChick(mommaBuilder.get()),
new BrownChick(mommaBuilder.get()),
new UglyDuckling(mommaBuilder.get())
);
System.out.println("Success!");
}
}
The other version aims to restrict access to the half-done Chicken
class only to the relevant classes ( subclasses of Egg
) using a solution from this question:
import java.util.Objects;
public class MyClass {
/**
* Handles eggs
* */
public static class Chicken{
private Egg[] eggs;
private Chicken(){}
private void setEggs(Egg... eggs_){ eggs = eggs_; }
public void accept_signal(String message){
/* ... */
}
public static class Builder{
Chicken momma;
public Builder(){
momma = new Chicken();
}
public Chicken build(Egg... eggs){
momma.setEggs(eggs);
return momma;
}
public Chicken get(Egg.Token token){
Objects.requireNonNull(token, "Only call this in function from a valid Builder");
return momma;
}
}
}
/**
* Signals when its ready to hatch
* */
public static abstract class Egg{
public static class Token{ private Token(){}}
private final Chicken parent;
public Egg(Chicken.Builder builder){
parent = builder.get(new Token());
}
public void hatch(){
parent.accept_signal("chirp chirp");
}
}
/**
* The resulting children to be handled
* */
public static class YellowChick extends Egg{
public YellowChick(Chicken.Builder builder){
super(builder);
}
}
public static class BrownChick extends Egg{
public BrownChick(Chicken.Builder builder){
super(builder);
}
}
public static class UglyDuckling extends Egg{
public UglyDuckling(Chicken.Builder builder){
super(builder);
}
}
public static void main (String[] args){
Chicken.Builder mommaBuilder = new Chicken.Builder();
Chicken momma = mommaBuilder.build(
new YellowChick(mommaBuilder),
new BrownChick(mommaBuilder),
new UglyDuckling(mommaBuilder)
);
System.out.println("Success!");
}
}
Although this still allows each chick to be built with different builder instances, and still be added under a Chicken
. To patch this up, an exception can be thrown whenever a Chicken
has a Chick from another nest ( Chicken.Builder
):
import java.util.HashMap;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class MyClass {
/**
* Handles eggs
* */
public static class Chicken{
private Egg[] eggs;
private Chicken(){}
private void setEggs(Egg... eggs_){ eggs = eggs_; }
public void accept_signal(String message){
/* ... */
}
public static class Builder{
Chicken momma;
HashSet<Integer> accessors;
public Builder(){
momma = new Chicken();
accessors = new HashSet<>();
}
public Chicken build(Egg... eggs){
for(Egg egg : eggs){
if(!accessors.contains(egg.getToken()))
throw new IllegalStateException("Unable to accept step-chick!");
}
momma.setEggs(eggs);
return momma;
}
public Chicken get(Egg.Token token){
Objects.requireNonNull(token, "Only call this in function from a valid Builder");
accessors.add(token.hashCode());
return momma;
}
}
}
/**
* Signals when its ready to hatch
* */
public static abstract class Egg{
public static class Token{ private Token(){}}
private final Chicken parent;
private final Token token = new Token();
public Egg(Chicken.Builder builder){
parent = builder.get(token);
}
public int getToken(){return token.hashCode();}
public void hatch(){
parent.accept_signal("chirp chirp");
}
}
/**
* The resulting children to be handled
* */
public static class YellowChick extends Egg{
public YellowChick(Chicken.Builder builder){
super(builder);
}
}
public static class BrownChick extends Egg{
public BrownChick(Chicken.Builder builder){
super(builder);
}
}
public static class UglyDuckling extends Egg{
public UglyDuckling(Chicken.Builder builder){
super(builder);
}
}
public static void main (String[] args){
Chicken.Builder mommaBuilder = new Chicken.Builder();
Chicken momma = mommaBuilder.build(
new YellowChick(mommaBuilder),
new BrownChick(mommaBuilder),
new UglyDuckling(mommaBuilder)
);
System.out.println("Success!");
}
}