包你懂设计模式之:适配器模式(Adapter)

适配器模式是这个专栏的第一个结构型设计模式,之前六篇介绍了创建型设计模式,欢迎大家查看和交流。适配器模式:将一个类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适配器这个模式在我们生活中可以找到很多原型,比如下图中的港版插头需要接在内地的插座上,需要一个适配器,通过这个适配器使得原本不兼容的插座和插头可以连接起来,而我们又没有去改造插头或插座:

 

 在我们实际开发中也常常存在这样的情况,比如我们现在有下面这样一个接口,抽象了对数据库的增删改查操作,当前我们使用的是sqlServer数据库,有一个针对这个接口实现的帮助类,代码如下:

=================数据库操作接口======================

/// <summary>
/// 数据操作帮助类接口
/// </summary>
public interface IDbHepler
{
    //增
    void Add<T>();
    //删
    void Delete<T>();
    //改
    void Update<T>();
    //查
    void Query<T>();
}

=================sqlServer帮助类======================

/// <summary>
/// 实现IDbHepler接口,实现增删改查的具体操作方法
/// </summary>
public class SqlServerHelper : IDbHepler
{
    public void Add<T>()
    {
        Console.WriteLine($" 使用 {this.GetType().Name} 添加了数据。 ");
    }

    public void Delete<T>()
    {
        Console.WriteLine($" 使用 {this.GetType().Name} 删除了数据。 ");
    }

    public void Update<T>()
    {
        Console.WriteLine($" 使用 {this.GetType().Name} 修改了数据。 ");
    }

    public void Query<T>()
    {
        Console.WriteLine($" 使用 {this.GetType().Name} 查询数据。 ");
    }
}

=================客户端代码======================

Console.WriteLine("+++++++++++++++++++++++++++++++++++++++++++");
IDbHepler dbHepler = new SqlServerHelper();
dbHepler.Add<Program>();
dbHepler.Delete<Program>();
dbHepler.Update<Program>();
dbHepler.Query<Program>();

运行结果:

上面的代码是现在系统中运行的代码,但是由于当前贸易战升级,影响到码农的世界,sqlserver不能使用了,所以我们选择拥抱开源,打算将数据迁移到mysql中,配置好环境并下载了相关类库,其中包含一个MySqlHelper,代码如下

=================MySqlHelper帮助类======================

/// <summary>
/// Mysql帮助类,他没有实现IDbHelper接口
/// 假设我们也不太方便修改代码来让他实现接口
/// </summary>
public class MySqlHelper 
{
    public void AddData<T>()
    {
        Console.WriteLine($" 使用 {this.GetType().Name} 添加了数据。 ");
    }

    public void DeleteData<T>()
    {
        Console.WriteLine($" 使用 {this.GetType().Name} 删除了数据。 ");
    }
    public void UpdateData<T>()
    {
        Console.WriteLine($" 使用 {this.GetType().Name} 修改了数据。 ");
    }

    public void QueryData<T>()
    {
        Console.WriteLine($" 使用 {this.GetType().Name} 查询数据。 ");
    }

    public void TruncateTable() 
    {
        Console.WriteLine($" 使用 {this.GetType().Name} 删库跑路,这是 MySqlHelper 中特有的方法,IDbHelper中不包含这个方法。。。 ");
    }
}

上面的类和SqlServer帮助类很像,有增删改查,但是方法名不同(实际开发中比这个要复杂的多,可能参数都不一样),假设我们修改不了这个类,但是又找不到替代品,接口不兼容意味着我们要对现有代码做很多修改,会很复杂,而且可能破坏系统的稳定性(违反开闭原则)。这个情况是不是和我们要把港版插座插在大陆插座上的情况很类似?这个时候我们也需要一个适配器:

下面我们来编写适配器类:

=================MySqlClassAdapter(MySqlHelper的类适配器)======================

/// <summary>
/// MySql帮助类的类适配器
/// 1、实现IDbHepler 接口,使其适配现有项目
/// 2、继承MySqlHelper,用它的方法来实现接口中的方法
/// 注意:由于它继承了MySqlHelper,所以它具备父类的全部能力,他能使用IDbHepler接口的方法,也能使用父类中的全部方法
/// </summary>
public class MySqlClassAdapter : MySqlHelper, IDbHepler
{
    public void Add<T>()
    {
        base.AddData<T>();
    }

    public void Delete<T>()
    {
        base.DeleteData<T>();
    }

    public void Update<T>()
    {
        base.UpdateData<T>();
    }

    public void Query<T>()
    {
        base.QueryData<T>();
    }
}

注意上面代码中的注释部分,理解为啥这样写就能适配。

        =================客户端代码======================

Console.WriteLine("+++++++++++++++++++++++++++++++++++++++++++");
//IDbHepler dbHepler1 = new MySqlClassAdapter();
MySqlClassAdapter dbHepler1 = new MySqlClassAdapter();
dbHepler1.Add<Program>();
dbHepler1.Delete<Program>();
dbHepler1.Update<Program>();
dbHepler1.Query<Program>();
dbHepler1.TruncateTable();//使用 MySqlClassAdapter 类型来接收对象的实例 才能用父类方法  用接口来接收是不行的

 运行结果:

可以看到,我们通过适配器类,成功解决了不兼容的问题。但是注意代码和运行结果的最后一行,TruncateTable()方法没有在接口中定义,我们也能调用到,是因为适配器类继承了MySqlHelper类,TruncateTable()是MySqlHelper的方法,所以子类继承了它的所有成员,故能也调用这个方法,vs的代码提示中也可以看到,它能调用接口中的方法,也能调用MySqlHelper类的方法(使用 MySqlClassAdapter 类型来接收对象的实例 才能用父类方法  用接口来接收是不行的):

很多时候,我们这个抽象接口可能是一个抽象类(abstract class 而非 interface),由于C#可以实现多个接口,但是只能继承一个父类。上面这个适配器就会报多继承错误,因此就用到了适配器的另外一种写法:

=================MySqlObjectAdapter(MySqlHelper的对象适配器)======================

/// <summary>
/// MySql帮助类的对象适配器
/// 1、实现IDbHepler 接口,使其适配现有项目
/// 2、内部实例化MySqlHelper 对象或者通过其他途径得到MySqlHelper 的实例,用它的方法实现接口的方法
/// </summary>
public class MySqlObjectAdapter : IDbHepler
{
    MySqlHelper _helper = new MySqlHelper();

    //private readonly MySqlHelper _helper;

    //public MySqlObjectAdapter(MySqlHelper helper)
    //{
    //    _helper = helper;
    //}

    public void Add<T>()
    {
        _helper.AddData<T>();
    }

    public void Delete<T>()
    {
        _helper.DeleteData<T>();
    }

    public void Update<T>()
    {
        _helper.UpdateData<T>();
    }

    public void Query<T>()
    {
        _helper.QueryData<T>();
    }
}

        =================客户端代码======================

Console.WriteLine("+++++++++++++++++++++++++++++++++++++++++++");
//IDbHepler dbHepler2 = new MySqlObjectAdapter(new MySqlHelper());
IDbHepler dbHepler2 = new MySqlObjectAdapter();
dbHepler2.Add<Program>();
dbHepler2.Delete<Program>();
dbHepler2.Update<Program>();
dbHepler2.Query<Program>();
//dbHepler2.TruncateTable(); //只能调用接口定义的方法,与类适配器的区别之一

运行结果:

注意上面适配器代码中的注释部分,与第一种不同点在于它不是通过继承来获得调用MySqlHelper类方法的能力,而是在内部得到实例,然后通过实例调用,所以它不能像上面那样,得父类的全部方法。

上面介绍的两种适配器,第一种称作类适配器,第二种是对象适配器。这两种写法首先的区别就在于适配器类和功能类的耦合度不一样,继承是一种强的耦合。另外我上面提到的,类适配器能访问到功能类的全部成员,还有一些我不太清楚的区别,请大家自己摸索。

总结:我觉得适配器模式是不应该在项目设计之处就考虑使用的,因为设计的时候不应该出现这种不兼容的情况。不要为了要使用设计模式而使用设计模式,应该根据实际需求合理使用。我认为适配器模式是一种亡羊补牢的手段,是一种妥协的选择,我说的可能有点绝对,希望大家在结合自身的需求,合理使用适配器。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值