|
| 1 | +--- |
| 2 | +title: Abstract Factory Pattern |
| 3 | +created: 2025-07-30 |
| 4 | +tags: |
| 5 | + - structural |
| 6 | +--- |
| 7 | +## Definition |
| 8 | + |
| 9 | +The Adapter Pattern converts the interface of a class into another interface that clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces. |
| 10 | + |
| 11 | +--- |
| 12 | +## Real World Analogy |
| 13 | + |
| 14 | +Imagine you bought a machine from China and imported it to India. The problem is that the plugs and sockets used in China are totally different from those in India. You still need to power on the machine, but you can’t simply connect it directly to the Indian socket. |
| 15 | + |
| 16 | +Here the adapter comes into play. Instead of cutting wires and reattaching them to an Indian plug, you can use an adapter that converts the Chinese plug to fit the Indian socket. |
| 17 | + |
| 18 | +![[adapter_workflow.png]] |
| 19 | +_Example of the Adapter Pattern to use a China plug with an Indian socket board._ |
| 20 | + |
| 21 | +Similarly, in our code example we have a `Duck` interface and implementations from the [[Strategy Pattern]]. We want to add a new bird type, `Turkey`, but also use the existing `Duck` interface without changing any existing code. As the [[#Definition]] says, we can translate one interface to another without touching the existing implementations. Here, we’ll adapt the `Turkey` to behave like a `Duck`. |
| 22 | + |
| 23 | +--- |
| 24 | +## Design |
| 25 | + |
| 26 | +```mermaid |
| 27 | +classDiagram |
| 28 | + %% Interfaces |
| 29 | + class Duck { |
| 30 | + <<interface>> |
| 31 | + +fly() |
| 32 | + +quack() |
| 33 | + } |
| 34 | + class Turkey { |
| 35 | + <<interface>> |
| 36 | + +fly() |
| 37 | + +run() |
| 38 | + } |
| 39 | +
|
| 40 | + %% Concrete classes |
| 41 | + class MallardDuck { |
| 42 | + +fly() |
| 43 | + +quack() |
| 44 | + } |
| 45 | + class WildTurkey { |
| 46 | + +fly() |
| 47 | + +run() |
| 48 | + } |
| 49 | + class TurkeyAdapter { |
| 50 | + -turkey: Turkey |
| 51 | + +TurkeyAdapter(turkey: Turkey) |
| 52 | + +fly() |
| 53 | + +quack() |
| 54 | + } |
| 55 | +
|
| 56 | + %% Relationships |
| 57 | + Duck <|.. MallardDuck |
| 58 | + Turkey <|.. WildTurkey |
| 59 | + TurkeyAdapter ..|> Duck |
| 60 | + TurkeyAdapter --> Turkey : adapts |
| 61 | +``` |
| 62 | + |
| 63 | +_Design diagram showing how `TurkeyAdapter` adapts a `Turkey` to the `Duck` interface._ |
| 64 | + |
| 65 | +--- |
| 66 | +## Implementation In Java |
| 67 | + |
| 68 | +Firstly, let’s look at the existing `Duck` interface and its `MallardDuck` implementation. |
| 69 | + |
| 70 | +```java title="Duck.java" |
| 71 | +interface Duck { |
| 72 | + public void fly(); |
| 73 | + public void quack(); |
| 74 | +} |
| 75 | +``` |
| 76 | + |
| 77 | +```java title="MallardDuck.java" |
| 78 | +class MallardDuck implements Duck { |
| 79 | + |
| 80 | + @Override |
| 81 | + public void fly() { |
| 82 | + System.out.println("Mallard Duck Flying"); |
| 83 | + } |
| 84 | + |
| 85 | + @Override |
| 86 | + public void quack() { |
| 87 | + System.out.println("Mallard Duck quacking"); |
| 88 | + } |
| 89 | + |
| 90 | +} |
| 91 | +``` |
| 92 | +The `Duck` interface defines two methods: `fly()` and `quack()`. `MallardDuck` implements these methods, providing the concrete behavior for a normal duck. |
| 93 | + |
| 94 | +Next, we define the `Turkey` interface and its implementation: |
| 95 | + |
| 96 | +```java title="Turkey.java" |
| 97 | +interface Turkey { |
| 98 | + public void fly(); |
| 99 | + public void run(); |
| 100 | +} |
| 101 | +``` |
| 102 | + |
| 103 | +```java title="WildTurkey.java" |
| 104 | +class WildTurkey implements Turkey { |
| 105 | + |
| 106 | + @Override |
| 107 | + public void fly() { |
| 108 | + System.out.println("Turkey Flying"); |
| 109 | + } |
| 110 | + |
| 111 | + @Override |
| 112 | + public void run() { |
| 113 | + System.out.println("Turkey Running"); |
| 114 | + } |
| 115 | + |
| 116 | +} |
| 117 | +``` |
| 118 | +The `Turkey` interface has two methods: `fly()` and `run()`. `WildTurkey` provides concrete behavior for these methods. |
| 119 | + |
| 120 | +Since `Turkey` does not have a `quack()` method, we create an adapter to translate a `Turkey` into a `Duck`: |
| 121 | + |
| 122 | +```java title="TurkeyAdapter.java" |
| 123 | +class TurkeyAdapter implements Duck { |
| 124 | + private Turkey turkey; |
| 125 | + |
| 126 | + public TurkeyAdapter(Turkey turkey) { |
| 127 | + this.turkey = turkey; |
| 128 | + } |
| 129 | + |
| 130 | + @Override |
| 131 | + public void fly() { |
| 132 | + // Delegate the fly call to the turkey |
| 133 | + this.turkey.fly(); |
| 134 | + } |
| 135 | + |
| 136 | + @Override |
| 137 | + public void quack() { |
| 138 | + // Since turkeys don't quack, delegate quack to run() |
| 139 | + this.turkey.run(); |
| 140 | + } |
| 141 | +} |
| 142 | +``` |
| 143 | +`TurkeyAdapter` implements the `Duck` interface. It holds a reference to a `Turkey` object and delegates `fly()` calls directly, while `quack()` calls are mapped to the turkey’s `run()` method. |
| 144 | + |
| 145 | +Finally, we put everything together: |
| 146 | + |
| 147 | +```java title="ObjectAdapterDemo.java" |
| 148 | +public class ObjectAdapterDemo { |
| 149 | + public static void main(String[] args) { |
| 150 | + // Original Duck |
| 151 | + Duck duck = new MallardDuck(); |
| 152 | + // Adapted Turkey |
| 153 | + Duck duck2 = new TurkeyAdapter(new WildTurkey()); |
| 154 | + |
| 155 | + duck.quack(); |
| 156 | + duck2.quack(); |
| 157 | + } |
| 158 | +} |
| 159 | +``` |
| 160 | +*Output:* |
| 161 | +```text |
| 162 | +Mallard Duck quacking |
| 163 | +Turkey Running |
| 164 | +``` |
| 165 | +--- |
| 166 | +## Class Adapter |
| 167 | + |
| 168 | +The above example uses the **Object Adapter** approach. You can also implement a **Class Adapter** when the language supports multiple inheritance. Java does not allow multiple class inheritance, but you can achieve similar behavior using multiple interfaces. In languages like C++, you could directly inherit both the target interface and the adaptee. |
| 169 | + |
| 170 | +--- |
| 171 | +## Real World Example |
| 172 | + |
| 173 | +A classic example in Java is `InputStreamReader`: |
| 174 | +```java title="InputStreamExample.java" |
| 175 | +public static void main(String[] args) throws FileNotFoundException, IOException { |
| 176 | + String path = System.getProperty("user.dir"); |
| 177 | + path = path.concat("/src/adapter/file.txt"); |
| 178 | + |
| 179 | + File file = new File(path); |
| 180 | + // Adaptee: byte-based API |
| 181 | + InputStream fileStream = new FileInputStream(file); |
| 182 | + // Adapter: converts bytes to characters |
| 183 | + Reader inputStreamReader = new InputStreamReader(fileStream); |
| 184 | + // Client: reads text lines |
| 185 | + BufferedReader reader = new BufferedReader(inputStreamReader); |
| 186 | + String line; |
| 187 | + while ((line = reader.readLine()) != null) { |
| 188 | + System.out.println(line); |
| 189 | + } |
| 190 | + reader.close(); |
| 191 | +} |
| 192 | +``` |
| 193 | +**Why it is called an Adapter?** |
| 194 | +1. **Adaptee**: `InputStream` (byte‐based API). |
| 195 | +2. **Target Interface**: `Reader` (char‐based API that many APIs expect). |
| 196 | +3. **Adapter**: `InputStreamReader` extends `Reader` and internally holds an `InputStream`, delegating calls and translating bytes into characters. |
| 197 | + |
| 198 | +Whenever you need to feed a `byte[]`, a `Socket.getInputStream()`, or a `FileInputStream` into a component that only accepts a `Reader`, you’re using the Adapter Pattern—straight out of the core Java I/O library. |
| 199 | + |
| 200 | +--- |
| 201 | +## Design Principles: |
| 202 | + |
| 203 | +- **Encapsulate What Varies** - Identify the parts of the code that are going to change and encapsulate them into separate class just like the Strategy Pattern. |
| 204 | +- **Favor Composition Over Inheritance** - Instead of using inheritance on extending functionality, rather use composition by delegating behavior to other objects. |
| 205 | +- **Program to Interface not Implementations** - Write code that depends on Abstractions or Interfaces rather than Concrete Classes. |
| 206 | +- **Strive for Loosely coupled design between objects that interact** - When implementing a class, avoid tightly coupled classes. Instead, use loosely coupled objects by leveraging abstractions and interfaces. This approach ensures that the class does not heavily depend on other classes. |
| 207 | +- **Classes Should be Open for Extension But closed for Modification** - Design your classes so you can extend their behavior without altering their existing, stable code. |
| 208 | +- **Depend on Abstractions, Do not depend on concrete class** - Rely on interfaces or abstract types instead of concrete classes so you can swap implementations without altering client code. |
0 commit comments