Reputation: 632
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:
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
Reputation: 27946
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"));
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.
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
Reputation: 11621
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
?
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.
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