1

I am not sure how I can inject the abstract class and its extensions in the bridge design pattern in Spring. Let's consider the classic bridge design pattern example of shape:

Color.java

public interface Color {

  String fill();
}

Blue.java

@Service("Blue")
public class Blue implements Color {
  @Override
  public String fill() {
    return "Color is Blue";
  }
}

Red.java

@Service("Red")
public class Red implements Color {
  @Override
  public String fill() {
    return "Color is Red";
  }
}

Shape.java

public abstract class Shape {
  protected Color color;

  @Autowired
  private Something something;

  @Value(${foo.bar})
  private String fooBar;

  public Shape(Color color){
    this.color = color;
  }

  abstract public String draw();
}

Square.java

public class Square extends Shape {

  public Square(Color color) {
    super(color);
  }

  @Override
  public String draw() {
    return "Square drawn. " + color.fill();
  }
}

Triangle.java

public class Triangle extends Shape {

  public Triangle(Color color) {
    super(color);
  }

  @Override
  public String draw() {
    return "Triangle drawn. " + color.fill();
  }
}

BridgeApplication.java

@SpringBootApplication
public class BridgeApplication {
  public static void main(String[] args) {
    SpringApplication.run(BridgeApplication.class, args);
  }
}

Controller:

@RestController
public class BridgeController {

  @Autowired
  @Qualifier("Red")
  private Color red;

  @GetMapping("/red")
  @ResponseStatus(HttpStatus.OK)
  public String redSquare() {
    Shape square = new Square(red);
    return square.draw();
  }

}

The problem arises when we would like to inject the abstract class Shape or its extensions like Square or Triangle. Because of the fact that constructor accepts a Color object and we cannot decide if the Color is a type of Red or Blue (That's the whole point of using Bridge design pattern here) then we cannot define Shape or its extensions to be a Spring Bean because there will be multiple beans of the type Color available. The easy workaround is like what I did to construct the object directly instead of injecting it. However, now there will be all challenges regarding any value I am injecting or any other Bean I would like to inject in the Shape class or its extensions (like fooBar or something) because the corresponding classes are constructed manually.

One solution is to start dealing with the manual injection that will create a lot of unnecessary complexity with the value and bean injections and it's not a clean solution at all. Like this or referring to this question. So I was wondering if there is a clean way of having some form of Bridge design pattern in the Spring framework or due to the Spring limitations, there is none.

Ali
  • 1,759
  • 2
  • 32
  • 69

1 Answers1

0

You can take bean from ApplicationContext.....But you need to pass color to take bean....

Here is one Example :

@RestController
public class BridgeController {

    private final Color red;
    private final Shape redSquareShape;

    public BridgeController(@Qualifier("Red") Color red,
                            ApplicationContext applicationContext) {
        this.red = red;
        this.redSquareShape = (Shape) applicationContext.getBean("square", red);
    }

    @GetMapping("/red")
    @ResponseStatus(HttpStatus.OK)
    public String redSquare() {
        Shape square = new Square(red);
        return square.draw();
    }

}
GolamMazid Sajib
  • 8,698
  • 6
  • 21
  • 39
  • Do I need to define the Shape or Square to be a bean now? As soon as I define the Shape to be a bean then the Shape contractor creates the ambiguity. – Ali Apr 28 '20 at 07:48
  • Here shape create with color constructor. You can take square or trinagle also. Give it in type casting. From applicationcontext you can take any bean by passing bean name – GolamMazid Sajib Apr 28 '20 at 07:56
  • How should it now if the "square" is referring to class Square? Do we need a qualifier for that as well? – Ali Apr 28 '20 at 08:08
  • First uppercase be small letter rest of all will be same – GolamMazid Sajib Apr 28 '20 at 08:10
  • I have to actually test it but, I don't think it will resolve the whole problem, Still, I need to manually construct the "Square" which is going to cause some issues with injecting "Something" and "fooBar" because we cannot have a bean for "Square" – Ali Apr 28 '20 at 08:12
  • I've just tested it. If I define "Square" and "Triangle" to be a bean then it gives me type ambiguity on their constructor (as it accepts "Shape" and they are two beans of type Shape available". If I don't define them to be a bean, it does not work as it complains that no bean with the name of "square" exists. – Ali Apr 28 '20 at 08:30
  • Here is the error I am getting: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'square' available I have noticed if I put all classes in a single package it passes this stage, but when they are placed in different packages neither this approach nor referring to the bean by using fqdn work for me. – Ali Apr 28 '20 at 10:35
  • if you keep them differenct packages then give all packages in @ComponentScan in main method class... Its another issue.not related to this – GolamMazid Sajib Apr 28 '20 at 10:39
  • I did actually it wasn't helpful. – Ali Apr 28 '20 at 12:10