【问题标题】:What is the difference between Two-way Adapter and Pluggable Adapter Pattern in C#?C# 中的双向适配器和可插入适配器模式有什么区别?
【发布时间】:2017-03-14 21:53:17
【问题描述】:

双向适配器和可插入适配器都可以访问这两个类,还可以更改需要更改的方法的行为。以下是我的代码:

双向适配器

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();
    }
}

可插拔模式

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();

    }
}

在上面的两个代码示例中,我没有发现关于模式功能的任何不同之处。那么这些模式有什么区别呢?谁能帮我理解一下?我一直在参考这个Design Pattern C# 3.0

更新 1

我看不懂这个参考中给出的例子,所以我更新了一个简单的代码,我想根据代码从场景中实现双向适配器

 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();
        }
    }

现在在这种情况下,我怎样才能将其作为双向适配器。 双向适配器解决了两个系统的问题,其中 一个系统的特性必须在另一个系统中使用,反之亦然 反之亦然。设置了一个 Adapter 类来吸收重要的公共 两种方法,并为两者提供适应。所结果的 双方都可以接受适配器对象

【问题讨论】:

  • 接口。可插拔,您不必有接口或具体类型。如果您愿意,您的 Pluggable 适配器可以接收委托而不是类。您在此处拥有的 MSDN 代码只是根据我的研究创建 Pluggable 适配器的一种方式。
  • @TyCobb 我无法理解你所说的。现在,如果我使用adaptee.Precise(x,3) 调用该方法,那么它会被称为双向适配器而不是可插拔适配器?
  • 没有。 Pluggable 可以接受任何需要做的事情(类、接口、委托等)。双向使用接口。使用双向,您正在定义一个可以利用的合约。可插拔,您可以准确地为其提供所需的内容,例如将两个数字相加的方法 - 您不需要完整的对象/接口。
  • @TyCobb 来自第一个示例,如果我使用委托调用 increaseRevs() 方法,那么它将是可插入适配器吗?
  • 你应该考虑引用C# 3.0 Design Patterns这本书作为你的来源。通过访问本书的全部内容,它可能会很有用并帮助其他人回答您的问题。

标签: c# design-patterns adapter


【解决方案1】:

C# 3.0 Design Patterns 中提取的所有引号,恰好是您问题的同一来源。
对报价的大胆强调在我身上。

在双向适配器上:

适配器提供对 Adaptee 中某些行为的访问(行为 ITarget 接口中需要),但适配器对象不是 可与 Adaptee 对象互换。它们不能在哪里使用 Adaptee 对象可以因为它们在执行 Adaptee,而不是它的接口。 有时我们需要有对象可以 是透明的 ITarget 或 Adaptee 对象。这很容易 如果适配器从两个类都继承,则实现;然而, 这样的多重继承在C#中是不可能的,所以我们必须看看 其他解决方案。

双向适配器解决两个问题 必须使用一个系统的特性的系统 其他,反之亦然。设置了一个适配器类来吸收 两者的重要共同方法,并为两者提供适应。 生成的适配器对象将被双方接受。 理论上,这个想法可以扩展到两个以上的系统,所以 我们可以有多路适配器,但有一些实现 限制:没有多重继承,我们必须插入一个 每个原始类和适配器之间的接口。

在这种情况下,除了在多个系统之间调整通用功能之外,我们正在讨论使来自不同系统的两个(或更多)不同功能可用于在同一个适配器上调用。在您的代码示例中:

//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(  ); 

现在,在可插拔适配器上:

可插拔适配器的一个显着特点是 客户端调用的方法和存在于 ITarget 接口中的方法 可以不同。适配器必须能够处理名称更改。 在之前的适配器变体中,所有 Adaptee 都是如此 方法,但客户端必须使用 ITarget 接口中的名称。 (...)

可插拔适配器会整理出插入的对象 时间。一旦一个服务被插入并且它的方法已经被 分配给委托对象,关联持续到另一个 分配了一组方法。 可插拔适配器的特点是 它将为它适应的每种类型都有构造函数。 在它们中的每一个中,它都执行委托分配(一个或多个 如果有其他方法可以重新路由,则返回一个)。

所以这里我们有一个通用名称,可以调用任何系统的任何插入方法,但在给定时间只能使用一个。 我认为这两种方法都可以通过不同的方式或不同的详细程度执行提供相似结果的操作,但这似乎不是该模式的规则。

再次,使用您的示例:

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));

结论:

尽管所有适配器都具有相同的目标,即通过 ITarget 将 Adaptee 提供给客户端,但每个适配器都为一组不同的问题提供了解决方案,无论是 双向适配器使两个 Target 都可用于Adaptee 和反之亦然Pluggable 以原子方式抽象 Target 和 Adaptee 的行为

希望这有助于清除两个适配器之间的差异。

更新 1. 更多关于双向:

我可以从您的示例中看出您没有理解双向适配器的用途。仅当您需要同时使用 Adaptee 和 Target 时才需要它,就像您将它们不同的功能合并到一个对象中一样。
如果他们都做同样的事情(即骑行),你最好使用可插拔适配器。

让我们以使用双向适配器的方式改进您的新示例。

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();
    }
}

【讨论】:

  • 如果被适配者没有接口怎么办,那么就不可能有双向适配器吗?如果以前的编码人员实现了一个没有接口契约的方法,那么呢?
  • 我无法理解给出 C# 3.0 设计模式的示例,这就是我提出这个问题的原因。我在我的问题中更新了一个简单的例子,请看看并帮助我理解
  • 您提供的新示例不是一个好示例,因为双向适配器的唯一用途是使适配器既是适配器又是目标。我会尝试在我的答案中添加一个示例。
  • 我已从更新的示例代码中删除了 Adapter 方法。请看一下,谢谢
  • 对于新示例中的任何编码错误,我提前道歉,因为我必须在没有 VS 的美化帮助的情况下从头开始添加,因此无法对其进行测试。不过,如果需要,我可以在明天进行测试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-19
  • 1970-01-01
  • 2017-04-21
  • 1970-01-01
  • 2011-04-15
  • 1970-01-01
相关资源
最近更新 更多