3
\$\begingroup\$

I've used the UK Plug -> US Socket and US Plug -> UK Socket analogy. I've also made it possible for UK plug to use a UK socket and a US plug to use a US socket. The output of the SSCCE below is

UKPlug -> UKSocket
USPlug -> USSocket
UKPlug -> UKUSAdapter -> USSocket
USPlug -> USUKAdapter -> UKSocket

And the code

package com.structural.adapter.adapter2;

interface IAdapter {
  public void plugin();
}

class UKUSAdapter implements IAdapter {
  UKPlug plug;
  USSocket socket;
  public UKUSAdapter(UKPlug plug, USSocket socket) {
    this.plug = plug;
    this.socket = socket;
  }

  @Override
  public void plugin() {
    System.out.print("UKUSAdapter -> ");
    socket.accept_2pin_plug(plug.pin1, plug.pin2);
  }
}

class USUKAdapter implements IAdapter {
  USPlug plug;
  UKSocket socket;
  public USUKAdapter(USPlug plug, UKSocket socket) {
    this.plug = plug; 
    this.socket = socket;
  }

  @Override
  public void plugin() {
    System.out.print("USUKAdapter -> ");
    socket.accept_3pin_plug(plug.pin1, plug.pin2, 1);
  }

}

class USSocket {
  USPlug plug;
  public USSocket(USPlug plug) {
    this.plug = plug;
  }

  public void accept_2pin_plug(int pin1, int pin2) {
    System.out.print("USSocket");
  }
}

class UKSocket {
  UKPlug plug;
  public UKSocket(UKPlug plug) {
    this.plug = plug;
  }
  public void accept_3pin_plug(int pin1, int pin2, int pin3) {
    System.out.print("UKSocket");
  }
}

class UKPlug {   
  int pin1;
  int pin2;
  int pin3;

  IAdapter adapter;
  UKSocket socket;

  public UKPlug(int pin1, int pin2, int pin3) {
    this.pin1 = pin1;
    this.pin2 = pin2;
    this.pin3 = pin3;
  }

  public void plug_into_3pin() {
    System.out.print("UKPlug -> ");
    if(this.socket == null) {
      adapter.plugin(); 
    } else {
     socket.accept_3pin_plug(pin1, pin2, pin3); 
    }    
  }

  public void setAdapter(IAdapter adapter) {
    this.adapter = adapter;
    this.socket = null;    
  }

  public void setSocket(UKSocket socket) {
    this.socket = socket;
    this.adapter = null;
  }
}

class USPlug {   
  int pin1;
  int pin2;

  IAdapter adapter;
  USSocket socket;

  public USPlug(int pin1, int pin2) {
    this.pin1 = pin1;
    this.pin2 = pin2;
  }

  public void plug_into_2pin() {
    System.out.print("USPlug -> ");
    if(this.socket == null) {
      adapter.plugin(); 
    } else {
      socket.accept_2pin_plug(pin1, pin2);
    }    
  }

  public void setAdapter(IAdapter adapter) {
    this.adapter = adapter;
    this.socket = null;
  }

  public void setSocket(USSocket socket) {
    this.socket = socket;
    this.adapter = null;
  }
}

public class TestPlugAdapters {
  public static void main(String[] args) {
    UKPlug ukPlug = new UKPlug(1,1,1);
    UKSocket ukSocket = new UKSocket(ukPlug);

    USPlug usPlug = new USPlug(1,1);
    USSocket usSocket = new USSocket(usPlug);

    //Test UK Plug to UK Socket
    ukPlug.setSocket(ukSocket);
    ukPlug.plug_into_3pin();
    System.out.println();

    //Test US Plug to US Socket
    usPlug.setSocket(usSocket);
    usPlug.plug_into_2pin();
    System.out.println();

    //Test UK Plug to US Socket
    IAdapter ukusAdapter = new UKUSAdapter(ukPlug, usSocket);
    ukPlug.setAdapter(ukusAdapter);
    ukPlug.plug_into_3pin();
    System.out.println();

    //Test US Plug to UK Socket
    IAdapter usukAdapter = new USUKAdapter(usPlug, ukSocket);
    usPlug.setAdapter(usukAdapter);
    usPlug.plug_into_2pin();
    System.out.println();
  }
}
\$\endgroup\$
4
  • \$\begingroup\$ You should add ISocket and IPlug, and your adapter in your case is a socket and a plug in the same time, so IAdapter should inherit from IPlug and ISocket \$\endgroup\$
    – Turkish
    May 19, 2014 at 14:17
  • \$\begingroup\$ You can, in java you can inherit from multiple interfaces, but not from multiple classes \$\endgroup\$
    – Turkish
    May 19, 2014 at 14:47
  • \$\begingroup\$ Here is a class diagram showing a modeling of your problem. \$\endgroup\$
    – Turkish
    May 19, 2014 at 15:03
  • \$\begingroup\$ @YAT useful thanks, isn't this a class adapter rather than an object adapter (which is what I based mine on)? \$\endgroup\$
    – PDStat
    May 19, 2014 at 15:14

2 Answers 2

5
\$\begingroup\$

This is not an implementation of the Adapter pattern.

What is the Adapter pattern trying to solve?

Lets' say I have :

public interface UKPlug {

    int getPin1();

    int getPin2();

    int getPin3();
}

public class UKSocketImpl {

    public void plugin(UKPlug plug) {

        if (plug.getPin1() == -plug.getPin2() && plug.getPin3() == 0) {
            System.out.println(plug.toString() + " -> UKSocket");
        }

    }
}

A UK socket, that accepts UKPlugs. The socket is the client in this scenario, which makes use of an interface (UKPlug)

Now I want to plug in my device, but it has a USPlug :

import java.math.BigInteger;

public class USPlugImpl {

    public BigInteger getPin1() {
        return BigInteger.TEN;
    }

    public BigInteger getPin2() {
        return BigInteger.TEN.negate();
    }

    @Override
    public String toString() {
        return "USPlug";
    }
}

This is where we can write an Adapter class to adapt our Adaptee (USPlugImpl) :

package adapter.improved;

public class USUKAdapter implements UKPlug {

    private final USPlugImpl plug;

    public USUKAdapter(USPlugImpl plug) {
        this.plug = plug;
    }

    @Override
    public int getPin1() {
        return plug.getPin1().intValue();
    }

    @Override
    public int getPin2() {
        return plug.getPin2().intValue();
    }

    @Override
    public int getPin3() {
        return 0;
    }

    @Override
    public String toString() {
        return plug.toString() + " adapted to UKPlug";
    }
}

This is what the Adapter class does :

  • it implements the needed interface
  • it does so by delegating to methods of the Adaptee instance (the USPlug), or by supplying some default behaviour.
  • Both the USPlug as the UKSocket are unaware of the use of an Adapter.

example use :

private void plugIn(UKSocketImpl ukSocket, USPlugImpl usPlug) {
    ukSocket.plugin(new USUKAdapter(usPlug));
}

How do your Adapter classes differ?

  • They implement an interface, but not one that is used by the Client
  • The UKPlug and USPlug classes have a reference to an adapter. What if these were classes from a 3rd party library, and you couldn't add members to them? That's what the Adapter pattern solves.

Final style note : don't use '_' in method names.

\$\endgroup\$
3
\$\begingroup\$

Assorted thoughts

IAdapter should be renamed Adapter -- when in doubt, look to the standard libraries for guidance on how to name things (List and ArrayList, not IList and ArrayList)

Also, you are missing the abstractions that allow you to substitute the adapter for the socket (or the plug, if you prefer -- that your real world inspiration is bi-directional, which can make it confusing to talk about).

Where I would start: a US plug can go into anything that accepts US plug. So make an interface for that second thing.

public interface USPlug {
    // ...
}

public interface USPlugAcceptor {
    void plugIn(USPlug);
}

public class USSocket implements USPlugAcceptor {
    void plugIn(USPlug plug) {
        // connect pins...
    }
}

Extend the same idea on the UK side...

public interface UKPlug {
    // ...
}

public interface UKPlugAcceptor {
    void plugIn(UKPlug);
}

public class UKSocket implements UKPlugAcceptor {
    void plugIn(UKPlug plug) {
        // connect pins...
    }
}

Now, let's think about connecting a USPlug to a UKSocket. So this adapter must have a signature that accepts USPlugs, therefore it must be a USPlugAcceptor.

public class ToUKAdapter implements USPlugAcceptor {
    void plugIn(USPlug plug) {
        // connect pins...
    }
}

But the key point of the adapter is that is can be used just the same way that a UKPlug is used....

public class ToUKAdapter implements USPlugAcceptor, UKPlug {
    void plugIn(USPlug plug) {
        // connect pins...
    }
}

You should also be able to go the other way...

public class ToUSAdapter implements UKPlugAcceptor, USPlug {
    void plugIn(UKPlug plug) {
        // connect pins...
    }
}

With this, we can now start connecting things in the combinations we expect

new UKSocket().plugIn(new UKPlug());
new UKSocket().plugIn(new ToUKAdapter().plugIn(new USPlug()));
new USSocket().plugIn(new ToUSAdapter().plugIn(new ToUKAdapter().plugIn(new USPlug())));
// etc...

Most often when working with adapters in java, you aren't expecting to disconnect and reconnect objects on demand, but are instead creating an object graph that persists as a whole for the lifetime of the composition. In that case, where the client code is not ever going to decompose the objects that you assembling, it is probably better to use dependency injection to connect the pieces together.

// Connecting a plug directly to a matching socket
UKPlug firstPlug = new CommonUKPlug();
UKSocket firstSocket = new UKSocket(firstPlug);

// Connecting a plug via an adapter
USPlug secondPlug = new CommonUSPlug();
UKSocket secondSocket = new UKSocket(new ToUKAdaptor( secondPlug ) ;

Another problem with this example: you've written logic to couple plugs and sockets, without any expression for how they interact. Why do we connect a plug to a socket? because we want power (in the form of a current). plugIn() is more about construction than it is about behavior. Interfaces usually define behavior, rather than constraining construction. Which of these ideas you want usually depends on the kind of thing that you are trying to model - ie, the problem you are actually trying to solve.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.