设计模式(6)适配器模式

模式介绍

适配器模式用于协调两个不兼容的接口之间的差异。 当用于无法重构的接口时(例如,当接口由Web服务或API控制时),此模式特别有用。

示例

假设我们在维护肉类安全烹饪温度数据库,旧的系统是这样维护的:

public enum TemperatureType
{
    Fahrenheit,//华氏摄氏度
    Celsius//摄氏度
}

/// <summary>
/// The legacy API which must be converted to the new structure
/// </summary>
class MeatDatabase
{
    // Temps from http://www.foodsafety.gov/keep/charts/mintemp.html
    public float GetSafeCookTemp(string meat, TemperatureType tempType)
    {
        if (tempType == TemperatureType.Fahrenheit)
        {
            switch (meat)
            {
                case "beef":
                case "pork":
                    return 145f;

                case "chicken":
                case "turkey":
                    return 165f;

                default:
                    return 165f;
            }
        }
        else
        {
            switch (meat)
            {
                case "beef":
                case "veal":
                case "pork":
                    return 63f;

                case "chicken":
                case "turkey":
                    return 74f;

                default:
                    return 74f;
            }
        }
    }

    //每盎司(28.3495231克)包含的卡路里
    public int GetCaloriesPerOunce(string meat)
    {
        switch (meat.ToLower())
        {
            case "beef": return 71;
            case "pork": return 69;
            case "chicken": return 66;
            case "turkey": return 38; //Wow, turkey is lean!
            default: return 0;
        }
    }

     //每盎司(28.3495231克)包含的蛋白质
    public double GetProteinPerOunce(string meat)
    {
        switch (meat.ToLower())
        {
            case "beef": return 7.33f;
            case "pork": return 7.67f;
            case "chicken": return 8.57f;
            case "turkey": return 8.5f;
            default: return 0d;
        }
    }
}

可以看到,这个遗留系统没有使用面向对象模式开发,上面的那些数据应该是Meat类的属性。

我们创建一个Meat类:

/// <summary>
/// The new Meat class, which represents details about a particular kind of meat.
/// </summary>
class Meat
{
    protected string MeatName;
    protected float SafeCookTempFahrenheit;
    protected float SafeCookTempCelsius;
    protected double CaloriesPerOunce;
    protected double ProteinPerOunce;

    // Constructor
    public Meat(string meat)
    {
        this.MeatName = meat;
    }

    public virtual void LoadData()
    {
        Console.WriteLine("\nMeat: {0} ------ ", MeatName);
    }
}

现在的问题是,我们无法修改旧系统的API。我们需要另一个继承自Meat的类但保留对API的引用,以便API的数据可以加载到Meat类的实例中:

/// <summary>
/// The Adapter class, which wraps the Meat class and initializes that class's values.
/// </summary>
class MeatDetails : Meat
{
    private MeatDatabase _meatDatabase;

    // Constructor
    public MeatDetails(string name)
        : base(name)
    {
    }

    public override void LoadData()
    {
        // The Adaptee
        _meatDatabase = new MeatDatabase();

        SafeCookTempFahrenheit = _meatDatabase.GetSafeCookTemp(MeatName, TemperatureType.Fahrenheit);
        SafeCookTempCelsius = _meatDatabase.GetSafeCookTemp(MeatName, TemperatureType.Celsius);
        CaloriesPerOunce = _meatDatabase.GetCaloriesPerOunce(MeatName);
        ProteinPerOunce = _meatDatabase.GetProteinPerOunce(MeatName);

        base.LoadData();
        Console.WriteLine(" Safe Cook Temp (F): {0}", SafeCookTempFahrenheit);
        Console.WriteLine(" Safe Cook Temp (C): {0}", SafeCookTempCelsius);
        Console.WriteLine(" Calories per Ounce: {0}", CaloriesPerOunce);
        Console.WriteLine(" Protein per Ounce: {0}", ProteinPerOunce);
    }
}

最后,我们在客户端调用:

static void Main(string[] args)
{
    //Non-adapted
    Meat unknown = new Meat("Beef");
    unknown.LoadData();

    //Adapted
    MeatDetails beef = new MeatDetails("Beef");
    beef.LoadData();

    MeatDetails turkey = new MeatDetails("Turkey");
    turkey.LoadData();

    MeatDetails chicken = new MeatDetails("Chicken");
    chicken.LoadData();

    Console.ReadKey();
}

总结

适配器模式尝试协调两个不兼容的接口,并且在无法重构其中一个或两个接口时特别有用。

该模式和外观模式特别相似,但是用途却不同。简单来说,
外观模式相当于说“这永远不会奏效,我会建立自己的。” 适配器模式相当于说“当然它可以工作,只需要稍微调整一下。”

源代码

https://github.com/exceptionnotfound/DesignPatterns/tree/master/Adapter

原文

https://www.exceptionnotfound.net/the-daily-design-pattern-adapter/

posted @ 2018-10-22 09:58  talentzemin  阅读(201)  评论(0编辑  收藏  举报