Adapter Pattern in Rust

Migo·2024년 6월 11일
0

Fluent Rust

목록 보기
22/27
post-thumbnail

Problem

You need to deal with incompatible interfaces given existing code bases.(The client expects to use the same interface as before)

Here, I'll give a simple yet practical example involving electrical plugs and receptacles.



Defining the receptacles

First, let's define our receptacle trait and implement it for Japanese and Korean receptacles:

// Receptacle is what the adapter will adapt to. It is the interface that the adapter will implement.

pub trait TReceptacle {
    const VOLT: u8;
    fn receive(&self, volt: u8) -> Result<(), &'static str> {
        if volt != Self::VOLT {
            return Err("Receptacle cannot receive the plug.");
        }
        println!("Receptacle received the plug.");
        Ok(())
    }
    fn bolt_to_accept(&self) -> u8 {
        Self::VOLT
    }
}

pub struct JapaneseReceptacle;

pub struct KoreanReceptacle;

impl TReceptacle for JapaneseReceptacle {
    const VOLT: u8 = 110;
}
impl TReceptacle for KoreanReceptacle {
    const VOLT: u8 = 220;
}

Defining the Plugs

Next, we define our plug trait and implement it for Japanese and Korean plugs

// plug is what we want to put to the receptacle. It is the interface that the adapter will take as dependency.
pub trait TPlug {
    fn plug(&self) -> u8;
}

pub struct JapanesePlug;

impl TPlug for JapanesePlug {
    fn plug(&self) -> u8 {
        110
    }
}

pub struct KoreanPlug;

impl TPlug for KoreanPlug {
    fn plug(&self) -> u8 {
        220
    }
}

Creating adapter

Now, we create PowerAdapter struct that will act as intermediary layer between the plug and the receptacle. It will handle the voltage conversion if needed.


struct PowerAdapter<T: TPlug> {
    port: T,
}

impl<T> PowerAdapter<T>
where
    T: TPlug,
{
    fn new(port: T) -> Self {
        PowerAdapter { port }
    }
    fn convert<U: TPlug>(self, port: U) -> PowerAdapter<U> {
        PowerAdapter { port }
    }
    fn convert_bolt(&self) -> u8 {
        if self.port.plug() == 110 {
            220
        } else {
            110
        }
    }

    fn connect(&self, receptacle: impl TReceptacle) -> Result<(), &'static str> {
        let volt = self.port.plug();
        if volt == receptacle.bolt_to_accept() {
            receptacle.receive(volt)
        } else {
            let volt = self.convert_bolt();
            receptacle.receive(volt)
        }
    }
}

Is it SOLID enough?

  • SRP - each component in our example(plugs, receptacles, and the adapter) has a single responsibility.

  • OCP - the code is open for extension but closed for modification. We can add new types of plugs and receptacles without modifying existing code, simply by implementing the relevant traits.

  • LSP - no need to explain.

  • DIP - adapter depends only on abstractions(generic and trait bound)

  • ISP - the traits(TPlug and TReceptacle) are specific to their functionality and do not force the implementing types to depend on methods they may not use.

profile
Dude with existential crisis

0개의 댓글

관련 채용 정보