代码改变世界

C#面向对象设计模式纵横谈 笔记7 Adapter 适配器(结构型模式)

2011-08-22 00:59  lujiao_cs  阅读(303)  评论(0编辑  收藏  举报

适配(转换)的概念无处不在

  适配,即在不改变原有实现的基础上,将原先不兼容的接口转换为兼容的接口。 

动机(Motivation

   在软件系统中,由于应用环境的变化,常常需要将一些现存的对象放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。

   如何应对这种迁移的变化?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?


意图(Intent

将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。   ——  《设计模式》GoF


例说Adapter应用 Codes in .NET

 

   /// <summary>
/// 客户希望的接口
/// </summary>
interface IStack
{
void Push(object item);
object Pop();
Object Peek();
}
/// <summary>
/// 适配的对象
/// </summary>
class Adapter : IStack
{
/// <summary>
/// 被适配的对象 可能有多个
/// </summary>
ArrayList adaptee;
public Adapter()
{
adaptee
= new ArrayList();
}
#region IStack 成员
public void Push(object item)
{
adaptee.Add(item);
//委派调用
}
public object Pop()
{
object obj = adaptee[adaptee.Count - 1];
adaptee.RemoveAt(adaptee.Count
- 1);
return obj;
}
public object Peek()
{
return adaptee[adaptee.Count - 1];
}
#endregion
}


结构(Structure) ——对象适配器(对象组合)

优先选择组合的方式,而不是继承的方式,更加松耦合。推荐使用这种方式。

结构(Structure ——类适配器(支持类的继承)

不推荐使用类适配器的原因是:违反了单一职责原则。而且继承它是紧耦合的,父类的任何接口改变都会导致子类的改动。

    /// <summary>
/// 类适配器: 有点不伦不类
/// </summary>
class Adapter :ArrayList, IStack
{
#region IStack 成员
public void Push(object item)
{
this.Add(item);//委派调用
}

public object Pop()
{
object obj = this[this.Count - 1];
this.RemoveAt(this.Count - 1);
return obj;
}

public object Peek()
{
return this[this.Count - 1];
}
#endregion
}


Adapter模式的几个要点

1)Adapter模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况” ,在遗留代码复用、类库迁移等方面非常有用。

2)GoF 23 定义了两种Adapter模式的实现结构:对象适配器和类适配器。但类适配器采用多继承的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用对象组合的方式,更符合松耦合精神。

3)Adapter模式可以实现的非常灵活,不必拘泥于Gof23中定义的两种结构。例如,完全可以将Adapter模式中的现存对象作为新的接口方法参数,来达到适配的目的

4)Adapter模式本身要求我们尽可能地使用面向接口的编程风格,这样才能在后期很方便地适配。如果用的不是面向接口编程风格,被适配的东西不是接口,例如直接绑定到了实现上了,而不是绑定到了接口上,则比较麻烦。如下代码:)


    /// <summary>
/// 已有的类
/// </summary>
class ExistingClass
{
public void SecificRequest1()
{ }

public void SpecificRequest2()
{ }
}

/// <summary>
/// 新系统需要使用的接口
/// </summary>
interface ITarget
{
void Request();
}

/// <summary>
/// 新系统需要使用接口 ITarget
/// </summary>
class MySystem
{
public void Process(ITarget target)
{

}
}
/// <summary>
/// 适配器
/// </summary>
class Adapter : ITarget
{
ExistingClass adaptee;

/// <summary>
/// 将对 MyAdapter 的请求委托到adaptee
/// </summary>
public void Request()
{
adaptee.SecificRequest1();
adaptee.SpecificRequest2();
}
}

如果现在将接口ITarget变为 MyTarget类:
    /// <summary>
/// 这里变成了类,如果Request是私有的 那么就无法适配了,
/// 所以应该尽可能使方法的参数,返回值,变量类型为接口或者抽象类。
/// 避免声明为实体类,避免无法适配。
/// </summary>
class MyTarget
{
public void Request()
{

}
}

.NET框架中的Adapter应用

1.NET中复用COM对象:

    – COM对象不符合.NET对象的接口

    – 使用tlbimp.exe 来创建一个Runtime Callable Wrapper (RCW) 以使其符合.NET对象的接口。

2.NET数据访问类(Adapter变体)

    –  各种数据库并没有提供DataSet接口

–  使用DbDataAdapter可以将任何各数据库访问/存取适配到一个DataSet对象上。

理解:


   /// <summary>
/// 正规的适配器
/// </summary>
class MyDataSet
{
DbDataAdapter dataAdapter;
DataTable dt;
public MyDataSet(DbDataAdapter dataAdapter)
{
dataAdapter.Fill(dt);
}
}

    但是不是这样实现的微软将适配器和被适配的东西(DataSet)分离了但是其实现了适配的目的将不同的数据源都统一得到了DataSet接口可以在多种情况下很好的使用

  例如:  

        public DataSet GetAllEmployee()
{
DbDataAdapter dataAdapter
= new SqlDataAdapter();
//......
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet);
return dataSet;
}

3集合类中对现有对象的排序(Adapter变体):

    –  现有对象未实现IComparable接口

–  实现一个排序适配器(继承IComparer接口),然后在其Compare 方法中对两个对象进行比较。

举例:

    /// <summary>
/// 员工类
/// </summary>
class Employee
{
int age;
public int Age
{
get { return this.age; }
set { age = value; }
}
}

/// <summary>
/// 比较适配器
/// </summary>
class EmployeeSortAdapter : IComparer
{
#region IComparer 成员
public int Compare(object x, object y)
{
if (x.GetType() != typeof(Employee)
|| y.GetType() != typeof(Employee))
{
throw new Exception();
}

Employee e1
= (Employee)x;
Employee e2
= (Employee)y;
if (e1.Age == e1.Age)
{
return 0;
}
else if (e1.Age > e2.Age)
{
return 1;
}
else
{
return -1;
}
}
#endregion
}

class Program
{
static void Main(string[] args)
{
Employee[] employees
= new Employee[100];

//.....

Array.Sort(employees,
new EmployeeSortAdapter());
}
}

推荐资源

1)《设计模式:可复用面向对象软件的基础》GoF

2)《面向对象分析与设计》Grady Booch

3)《敏捷软件开发:原则、模式与实践》Robert C. Martin

4)《重构:改善既有代码的设计》Martin Fowler

5)Refactoring to PatternsJoshua Kerievsky