The adapter pattern helps two things work together when their interfaces don’t match. It’s a structural design pattern because it connects and combines the features of both.
Let’s say we have a LegacyCourier object and a function that should
fn do_delivery(sender: impl Delivery)
send the data. How do we connect them so that other delivery methods also work? This is where the Adapter Pattern comes in handy. Let’s look at the following code.
use std::sync::Arc;
use std::thread;
// Unified interface used by the app.
trait Delivery: Send + Sync {
fn deliver(&self, msg: &str);
}
// A modern implementation (already matches the trait).
struct HttpDelivery;
impl Delivery for HttpDelivery {
fn deliver(&self, msg: &str) {
println!("[HttpDelivery] POST /deliver => \"{}\"", msg);
}
}
// ----- Legacy system (incompatible interface) -----
struct LegacyCourier;
impl LegacyCourier {
// Legacy API ships raw bytes (not &str) and has a different method name.
fn ship(&self, data: Vec<u8>) {
println!("[LegacyCourier] shipping {} bytes", data.len());
}
}
// Adapter: wraps LegacyCourier and exposes the Delivery trait.
struct LegacyCourierAdapter {
inner: LegacyCourier,
}
impl LegacyCourierAdapter {
fn new(inner: LegacyCourier) -> Self {
Self { inner }
}
}
impl Delivery for LegacyCourierAdapter {
fn deliver(&self, msg: &str) {
// Adapt &str -> Vec<u8> for the legacy API.
self.inner.ship(msg.as_bytes().to_vec());
}
}
We can now use LegacyCourier
in a new delivery method via an adapter, while keeping the old interface intact. This is a very powerful example, as LegacyCourier
can still be used in legacy code where changes are undesirable. By implementing a delivery interface for LegacyCourier
, we avoid modifying the legacy code, but can extend it as needed without the risk of breaking backward compatibility.
fn main() {
// Legacy system part
let courier = LegacyCourier;
let courier_adapter = LegacyCourierAdapter::new(courier);
do_delivery(courier_adapter);
// Modern system part
let http = HttpDelivery;
do_delivery(http);
}
fn do_delivery(sender: impl Delivery) {
sender.deliver("some message");
}
Output
[LegacyCourier] shipping 12 bytes
[HttpDelivery] POST /deliver => "some message"
Top comments (0)