java bridge pattern
java bridge pattern
Bridge Pattern in Java
The Bridge Pattern is a design pattern that separates an abstraction from its implementation so that the two can vary independently.
Let's consider a real-world scenario where we have a simple console game that displays different types of graphics. The game has different graphic modes like text, ASCII art, and high-quality graphics. Each mode has its own way of displaying the game's elements, but they all share the same core functionality.
Abstract Class: GraphicMode
We can start by defining an abstract class GraphicMode
that provides a basic interface for all types of graphic modes:
public abstract class GraphicMode {
public abstract void display();
}
Concrete Classes: TextGraphic
, ASCIIArtGraphic
, and HQGraphic
Now, let's create three concrete classes that implement the GraphicMode
interface. Each class represents a specific type of graphic mode:
TextGraphic
: Displays text-based graphics.
public class TextGraphic extends GraphicMode {
@Override
public void display() {
System.out.println("Displaying text graphics...");
}
}
ASCIIArtGraphic
: Displays ASCII art graphics.
public class ASCIIArtGraphic extends GraphicMode {
@Override
public void display() {
System.out.println("Displaying ASCII art graphics...");
}
}
HQGraphic
: Displays high-quality graphics.
public class HQGraphic extends GraphicMode {
@Override
public void display() {
System.out.println("Displaying high-quality graphics...");
}
}
Refined Abstraction: Game
We can then create a refined abstraction, Game
, that depends on the concrete classes (graphic modes). This allows us to vary the graphic mode without changing the Game
class.
public abstract class Game {
private GraphicMode graphicMode;
public Game(GraphicMode graphicMode) {
this.graphicMode = graphicMode;
}
public void play() {
graphicMode.display();
}
}
Concrete Game Classes:
Now, we can create concrete game classes that use the different graphic modes:
public class TextGame extends Game {
public TextGame() {
super(new TextGraphic());
}
}
public class ASCIIArtGame extends Game {
public ASCIIArtGame() {
super(new ASCIIArtGraphic());
}
}
public class HQGame extends Game {
public HQGame() {
super(new HQGraphic());
}
}
Benefits:
The Bridge Pattern provides several benefits:
Decoupling: TheGame
class is decoupled from the specific graphic modes, making it easier to add or change graphic modes without affecting the game. Polymorphism: The Game
class can work with different types of graphic modes, which allows for polymorphic behavior. Extension: New graphic modes can be added without modifying the Game
class.
In conclusion, the Bridge Pattern in Java provides a way to separate an abstraction from its implementation, allowing for more flexibility and extensibility. It's particularly useful when you need to vary an abstraction's implementation without affecting its interface or behavior.
Java adapter example
Here is an example of a Java adapter class for the "Device" interface:
public class AudioAdapter implements Device {
private final Audio audio;
public AudioAdapter(Audio audio) {
this.audio = audio;
}
@Override
public void play() {
audio.play();
}
@Override
public void stop() {
audio.stop();
}
@Override
public void pause() {
audio.pause();
}
}
In this example, the AudioAdapter
class is implementing the Device
interface. The AudioAdapter
class has a private field audio
that represents an instance of the Audio
class. The AudioAdapter
class provides implementations for the play()
, stop()
, and pause()
methods defined in the Device
interface.
When you want to use an Audio
object as if it were a Device
, you can create an instance of the AudioAdapter
class, passing the Audio
object to its constructor. Then you can call the methods on the AudioAdapter
object, such as play()
, stop()
, or pause()
. The AudioAdapter
class will delegate these calls to the underlying Audio
object.
Here is an example of how you might use this adapter:
public class Main {
public static void main(String[] args) {
Audio audio = new Audio(); // Create an instance of the Audio class
Device device = new AudioAdapter(audio); // Create an instance of the AudioAdapter class
device.play(); // Call the play method on the adapter, which will delegate it to the underlying Audio object
device.stop(); // Call the stop method on the adapter, which will delegate it to the underlying Audio object
device.pause(); // Call the pause method on the adapter, which will delegate it to the underlying Audio object
}
}
In this example, we create an instance of the Audio
class and then use it as if it were a Device
. We can call methods such as play()
, stop()
, or pause()
on the device
variable, which is actually an instance of the AudioAdapter
class. The AudioAdapter
class will delegate these calls to the underlying Audio
object, allowing us to use the Audio
object in a way that is consistent with the Device
interface.
This is just one example of how you might use an adapter in Java. Adapters can be very useful for converting between different interfaces or classes, and they are a common design pattern in software development.