Teshte
Teshte

Reputation: 632

Is this a correct implementation of Bridge pattern in java?

I'm trying to improve my knowledge of design patterns, and I am a little bit confused with the Bridge pattern. Below you can see my example:

public interface Animal {
    public abstract void eat();
    public abstract void move();
}


public class Jaguar implements Animal{

    @Override
    public void eat() {
        System.out.println("The Jaguar eats meat");
    }

    @Override
    public void move() {
        System.out.println("The Jaguar runs");
    }
 }

public class Kangaroo implements Animal{

    @Override
    public void eat() {
        System.out.println("THe Kangaroo eats grass and other vegetables");
    }

    @Override
    public void move() {
        System.out.println("The Kangaroo hops around :)");
    }

}

public abstract class Environment {

    private Animal animal;

    public Environment(Animal animal){
        this.animal = animal;
    }

    public void someAnimalEats(){
        this.animal.eat();
    }

    public void someAnimalMoves(){
        this.animal.move();
    }

    public abstract void environmentPolutionStatus();
}


public class Australia extends Environment{

    public Australia(Animal animal) {
        super(animal);
    }

    @Override
    public void environmentPolutionStatus() {
        System.out.println("Australia is in good shape as far as polution is concerned.");
    }

}


public class Africa extends Environment{

    public Africa(Animal animal) {
        super(animal);
    }

    @Override
    public void environmentPolutionStatus() {
        System.out.println("Africa looks pretty good, however the hunting is kind of bad");

    }

}

public class BridgePatternMain {
    public static void main(String[] args){
        Environment australiaKangaroo = new Australia(new Kangaroo());
        Environment australiaJaguar = new Australia(new Jaguar());
        Environment africaKangaroo = new Africa(new Kangaroo());
        Environment africaJaguar = new Africa(new Jaguar());

        australiaKangaroo.environmentPolutionStatus();
        australiaKangaroo.someAnimalEats();
        australiaKangaroo.someAnimalMoves();

        australiaJaguar.environmentPolutionStatus();
        australiaJaguar.someAnimalEats();
        australiaJaguar.someAnimalMoves();

        africaKangaroo.environmentPolutionStatus();
        africaKangaroo.someAnimalEats();
        africaKangaroo.someAnimalMoves();

        africaJaguar.environmentPolutionStatus();
        africaJaguar.someAnimalEats();
        africaJaguar.someAnimalMoves();
    }
}

My questions:

  1. Is this a correct Bridge pattern?
  2. Is it possible to have Bridge pattern if interfaces are replaced by abstract classes (I saw this approach in this tutoarial http://www.newthinktank.com/2012/10/bridge-design-pattern-tutorial/). But according to this one (https://dzone.com/articles/design-patterns-bridge) is seems like Animal in my case shouldn't be an abstract class..
  3. Is it necessary to have the methods someAnimalEats() and someAnimalMoves() in the Environment class? More precisely, is it mandatory to have in this class methods corresponding to each of the methods from the Animal interface?

Many Thanks!

Upvotes: 3

Views: 801

Answers (2)

sprinter
sprinter

Reputation: 27946

  1. The domain you demonstrate (animals and their environments) is not really a good use case for the bridge pattern. It has a pretty specific purpose: to separate an abstraction (including extensions of that abstraction) from implementations (again possibly including extensions). One of the key characteristics is that the abstraction references the implementation (which is the 'bridge' in the name) rather than the implementation extending or implementing the abstraction. Generally the concrete implementation is decided by the client at run-time.

It's not easy to think of a natural use case for bridge that models real world objects (like animals and environments). Easier is to think of class that is designed to perform some function.

// abstraction 
abstract class Logger {
    protected final LogOutputter outputter;
    public abstract void log(String message);
}

// abstraction extension
class ErrorLogger extends Logger {
    public void log(String message) {
        outputter.output("Error: " + message);
    }
}

// implementation interface
interface LogOutputter {
    void output(String message);
}

// implementation extensions
class FileLogOutputter implements LogOutputter ...
class ConsoleLogOutputter implements LogOutputter ...

And the client might do something like:

Logger logger = new ErrorLogger(new FileLogOutputter("errors.log"));
  1. I would suggest the class / interface combinations I used in this example are fairly typical. You could make the abstraction and interface but given the point of the bridge is to reference the implementation it makes it easier to make it an abstract class.

  2. Hopefully the example also answers this: you could have similar methods in the abstraction and implementation but it's certainly not necessary. One of the interesting and useful aspects of the pattern is that different independent characteristics (in this example, what is logged and how it's logged) can be separately defined as extensions of the abstraction and implementation. This allows you to mix and match the characteristics without having a class structure that gets out of control. The independence of these characteristics (i.e. orthogonality) will often require the methods in the two structures to be quite different.

Upvotes: 5

Klitos Kyriacou
Klitos Kyriacou

Reputation: 11621

  1. It has the structure of the Bridge pattern, in that it has the sort of class relationships and methods that are typical in the pattern. I'm not sure that the semantics of your example are a good fit for this pattern. The main gist of this pattern is that you have a class that uses one or more implementation classes. Would you say your Animal classes provide the implementation for Environment?

  2. Yes, you can indeed use abstract classes. Remember, design patterns are typical language-agnostic. In some languages, such as C++, interfaces are created using classes that contain pure abstract methods, since unlike Java there is no specific keyword for an interface in C++. The label "interface" in diagrams denotes an interface in the conceptual sense, not the actual Java keyword. In fact, you can even use a concrete class as your implementor, even though it's typically good practice to use an interface as that provides more flexibility.

  3. No, it's not necessary to have methods that exactly mirror those of the implementor classes. The implementor classes provide a number of implementation methods which are used by the other class. How it uses them depends on what features the class wants to provide.

The Bridge pattern is more useful in other languages, such as C++, where it's known as the "pImpl idiom" or "opaque pointer", because it can be used to hide the implementation from users: they cannot see anything about the implementing class. In Java, this kind of hiding is not possible.

Upvotes: 2

Related Questions