Lijin John Lijin John - 13 days ago 6
C# Question

What is the difference between Two-way Adapter and Pluggable Adapter Pattern in C#?

Both Two-way Adapter and Pluggable Adapter can access both the classes and also change the behavior of the method which is required to be changed. The following is my code:

Two-Way Adapter

public interface IAircraft
{
bool Airborne { get; }
void TakeOff();
int Height { get; }
}

// Target
public sealed class Aircraft : IAircraft
{
int height;
bool airborne;
public Aircraft()
{
height = 0;
airborne = false;
}
public void TakeOff()
{
Console.WriteLine("Aircraft engine takeoff");
airborne = true;
height = 200; // Meters
}
public bool Airborne
{
get { return airborne; }
}
public int Height
{
get { return height; }
}
}
// Adaptee interface
public interface ISeacraft
{
int Speed { get; }
void IncreaseRevs();
}
// Adaptee implementation
public class Seacraft : ISeacraft
{
int speed = 0;
public virtual void IncreaseRevs()
{
speed += 10;
Console.WriteLine("Seacraft engine increases revs to " + speed + " knots");
}
public int Speed
{
get { return speed; }
}
}
// Adapter
public class Seabird : Seacraft, IAircraft
{
int height = 0;
// A two-way adapter hides and routes the Target's methods
// Use Seacraft instructions to implement this one
public void TakeOff()
{
while (!Airborne)
IncreaseRevs();
}
// Routes this straight back to the Aircraft
public int Height
{
get { return height; }
}

// This method is common to both Target and Adaptee
public override void IncreaseRevs()
{
base.IncreaseRevs();
if (Speed > 40)
height += 100;
}
public bool Airborne
{
get { return height > 50; }
}
}
class Experiment_MakeSeaBirdFly
{
static void Main()
{
// No adapter
Console.WriteLine("Experiment 1: test the aircraft engine");
IAircraft aircraft = new Aircraft();
aircraft.TakeOff();
if (aircraft.Airborne) Console.WriteLine(
"The aircraft engine is fine, flying at "
+ aircraft.Height + "meters");
// Classic usage of an adapter
Console.WriteLine("\nExperiment 2: Use the engine in the Seabird");
IAircraft seabird = new Seabird();
seabird.TakeOff(); // And automatically increases speed
Console.WriteLine("The Seabird took off");
// Two-way adapter: using seacraft instructions on an IAircraft object
// (where they are not in the IAircraft interface)
Console.WriteLine("\nExperiment 3: Increase the speed of the Seabird:");
(seabird as ISeacraft).IncreaseRevs();
(seabird as ISeacraft).IncreaseRevs();
if (seabird.Airborne)
Console.WriteLine("Seabird flying at height " + seabird.Height +
" meters and speed " + (seabird as ISeacraft).Speed + " knots");
Console.WriteLine("Experiments successful; the Seabird flies!");

Console.Read();
}
}


Pluggable Pattern

class Adaptee
{
public double Precise(double a, double b)
{
return a / b;
}
}

// New standard for requests
class Target
{
public string Estimate(int i)
{
return "Estimate is " + (int)Math.Round(i / 3.0);
}
}

// Implementing new requests via old
class Adapter : Adaptee
{
public Func<int, string> Request;
// Different constructors for the expected targets/adaptees
// Adapter-Adaptee
public Adapter(Adaptee adaptee)
{
// Set the delegate to the new standard
Request = x =>
{
return "Estimate based on precision is " +
(int)Math.Round(Precise(x, 3));
};
}

// Adapter-Target
public Adapter(Target target)
{
// Set the delegate to the existing standard
Request = target.Estimate;
}
}

class Client
{
static void Main()
{
Adapter adapter1 = new Adapter(new Adaptee());
Console.WriteLine(adapter1.Request(5));

Adapter adapter2 = new Adapter(new Target());
Console.WriteLine(adapter2.Request(5));
Console.Read();

}
}


In the above two code samples, I don't find anything different in respect of the patterns' functionality. So what is the difference between the patterns? Can anyone help me understand it? I have been refering to this Design Pattern C# 3.0

UPDATE 1

I couldnt understand the example given in this refrence, so i have updated a simple code, and i want to implement Two-Way adapter from the scenaerio based on the code

interface Ibike {
void Ride(int energy,int time);
}
class Bike : Ibike {
public void Ride(int energy,int time) {
Console.WriteLine("riding bike with calories of energy "+energy+" spend time "+time);
}
}
interface Imotorcycle {
void Ride(int fuel);
}
class Motorcycle : Imotorcycle {
public void Ride(int fuel) {
Console.WriteLine("riding motorbike with fuel "+fuel);
}
}
class Client {
static void Main() {
Ibike bike = new Bike();
Imotorcycle motorBike = new Motorcycle();
bike.Ride(50, 2);
motorBike.Ride(3);


Console.Read();
}
}



Now in this scenario how can i make it as a Two-way Adapter.
The two-way adapter addresses the problem of two systems where the
characteristics of one system have to be used in the other, and vice
versa. An Adapter class is set up to absorb the important common
methods of both and to provide adaptations to both. The resulting
adapter objects will be acceptable to both sides

Answer

All quotes extracted from the C# 3.0 Design Patterns, which happens to be same source of your question.
Bold emphasys on the quotatios are on me.

On two-way adapters:

Adapters provide access to some behavior in the Adaptee (the behavior required in the ITarget interface), but Adapter objects are not interchangeable with Adaptee objects. They cannot be used where Adaptee objects can because they work on the implementation of the Adaptee, not its interface. Sometimes we need to have objects that can be transparently ITarget or Adaptee objects. This could be easily achieved if the Adapter inherited both from both classes; however, such multiple inheritance is not possible in C#, so we must look at other solutions.

The two-way adapter addresses the problem of two systems where the characteristics of one system have to be used in the other, and vice versa. An Adapter class is set up to absorb the important common methods of both and to provide adaptations to both. The resulting adapter objects will be acceptable to both sides. Theoretically, this idea can be extended to more than two systems, so we can have multiway adapters, but there are some implementation limitations: without multiple inheritance, we have to insert an interface between each original class and the adapter.

In this case, on top of adapting common functionality between multiple systems, we are talking about making two (or more) distinct functionalities from distinct systems available for the calling on the same adapter. In your code sample:

//The adapter
IAircraft seabird = new Seabird(  );

// This is a IAircraft method
seabird.TakeOff(  ); 

//This is NOT a IAircraft method, but is made available through the adapter.
(seabird as ISeacraft).IncreaseRevs(  ); 

Now, on pluggable adapters:

A distinguishing feature of pluggable adapters is that the name of a method called by the client and that existing in the ITarget interface can be different. The adapter must be able to handle the name change. In the previous adapter variations, this was true for all Adaptee methods, but the client had to use the names in the ITarget interface. (...)

The pluggable adapter sorts out which object is being plugged in at the time. Once a service has been plugged in and its methods have been assigned to the delegate objects, the association lasts until another set of methods is assigned. What characterizes a pluggable adapter is that it will have constructors for each of the types that it adapts. In each of them, it does the delegate assignments (one, or more than one if there are further methods for rerouting).

So here we have a common name by which any plugged method of any system could by called, but only one can be used at a given time. I supose it would be expected that both methods perform operations that delivers similar results through different means or with distinct detail levels, but that does not seem to be a rule for the pattern.

Again, using your sample:

Adapter adapter1 = new Adapter (new Adaptee(  ));
//Here, it will call the Adaptee's abstracted method. 
adapter1.Request(5);

//The only way to call the Target's method is to instantiate a new adapter with the target    
Adapter adapter2 = new Adapter (new Target(  ));
Console.WriteLine(adapter2.Request(5));

Conclusion:

Even though all adapters share the same objetive of making a Adaptee available to a Client through a ITarget, each one offers a solution for a different set of problems, be it the Two-way adapter making both Target available to Adaptee and vice-versa or the Pluggable abstracting the behavior of both Target and Adaptee in an atomic manner.

Hope that helps clearing out the difference between the two adapters.

Update 1. More on Two-way:

I can tell by your example that you did not get the purpose of the Two-way adapter. It is required only when you need to use both the Adaptee and the Target interchangeably, as if you were merging their distinct functionalities into a single object.
If they both do the same thing (that is, Ride), you would be better off using a Pluggable Adapter instead.

Let's revamp your new sample in a way that it makes sense to use a Two-way adapter.

interface IBike {
    void Pedal();
}
class Bike : IBike {
    public void Pedal() {
        Console.WriteLine("Moving my vehicle with my body");
    }
}

interface IMotorcycle {
    void Accelerate();
}
class Motorcycle : IMotorcycle {
    public virtual void Accelerate() {
        Console.WriteLine("Moving my vehicle with a hydrocarbon fuel engine");
    }
}

class ElectricBike : Motorcycle, IBike {
    bool _isAccelerating = false;

    public override void Accelerate() {
        _isAccelerating = true;
        Console.WriteLine("Moving my vehicle with a electric engine");
    }

    public void Pedal() {
        if (!_isAccelerating)
            Console.WriteLine("Moving my vehicle with my body");
        else
            Console.WriteLine("Occupying my body with senseless effort, for my vehicle is already moving"); 
    }        
}

class MovingMyVehicle {
    static void Main() {
        IMotorcycle motorBike = new Motorcycle();
        //That is expected, as IMotorcycle can Accelerate.
        motorBike.Accelerate();

        IBike newBike = new ElectricBike();
        //That too is expected, as IBike can Pedal.
        newBike.Pedal();

        //Now that´s something new, as IBike cannot Accelerate, 
        //but the the ElectricBike adapter can, as it implements both interfaces.
        (newBike as IMotorcycle).Accelerate();

        Console.Read();
    }
}
Comments