设计模式之适配器模式

什么是设计模式

在软件开发的世界里,许多领域中的问题具有相似的特性。就像造房子一样,不管要盖一座购物中心,还是要盖一座假日酒店,它们之间都有基本上相似的工作步骤,都需要搭建梁柱,铺置房顶等工作。在做这些工作时都需要遵循某种特殊的技术要求,以使得房子的结构、承受能力达到合理,这些规则是前人经过精确的计算和失败的教训得来的。对于软件开发也一样,如果我们希望编写出来健壮、灵活的应用程序,也有必要进行精细的设计,并且可以通过遵循某种规则以达到这个目标。这些规则或者称之为技巧就是设计模式。

设计模式领域中有一本经典的著作:《Design Patterns: Elements of Reusable Object-Oriented Software》(即《设计模式》一书),由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 合著(Addison-Wesley,1995)。这几位作者常被称为“四人组(Gang of Four)”,而这本书也就被称为“四人组(或 GoF)”书。此书中介绍了23种设计模式,随着技术的不断发展,新的设计模式不断的被发现,但这23种设计模式是最值得学习和掌握的。 C#设计模式中将设计模式划分为以下5类:

  • 接口型模式
  • 职责型模式
  • 构造型模式
  • 操作型模式
  • 扩展型模式

各类别中包含的模式:

类别包含的设计模式
接口型适配器模式,外观模式,合成模式,桥接模式
职责型单件模式,观察者模式,中介者模式,代理模式,职责链模式,享元模式
构造型生成器模式,工厂方式模式,抽象工厂模式,原型模式,备忘录模式
操作型模板方法模式,状态模式,策略模式,命令模式,解释器模式
扩展型装饰模式,迭代器模式,访问者模式

适配器模式

一. 现实问题

在进行项目开发时,经常会发生类之间的调用关第,有可能存在以下两种情况:

1. 我们已经定义了类调用关系,例如类需要通过调用实现了某个接口的类来实现特定功能,而这个功能第三方类已经实现了,第三方类的方法签名可能与客户期望的签名不太一致。

2.这些类提供的方法可能只是完成了我们所需要的大部分功能,我们还要在此基础上执行一点点额外的操作;

二. 解决方案

以上两种问题实际上都是客户需要对提供功能的服务类的调用,可以有以下几种解决方案:

1. 直接在客户类里实例化服务类,进行方法调用,并且根据需要在调用后进行额外的处理。

2. 创建一个中介,将方法调用传递给中介,由中介请求服务类并执行额外操作。

比较而言,第二种方式更为合理,因为通过中介类的引处,降低了客户类和服务类的耦合,在服务类发生变化时,可以不影响到客户类。

三. 概念定义

对于这种问题,可以使用适配器模式。适配器模式的目的在于:如果客户需要使用某个类的服务,而这项服务是这个类用一个不同的接口提供的,那么,可以使用适配器为客户提供一个期望的接口。

存在两种适配器的形式:接口适配器(类适配器)和对象适配器。

四. 项目示例

1. 接口适配器

客户类需要通过对某个接口的调用来实现某个功能,而这个功能已经被服务类实现了。可以引入适配器类,该类继承自服务类并实现了客户端要调用的接口。

例如现在期望实现用户登录的同时更新最后活动时间的功能,约定客户类需要调用某个接口的方法实现该操作。此时发现已有现成的类(服务类)实现了用户登录的功能,但没有实现更新活动时间的操作,因此可以创建一个接口适配器。类关系图如下所示:

image

客户类代码如下(只定义了相应的方法,未编写实现):

/// <summary>
/// 客户类
/// </summary>
class Client
{
    public IUser User
    {
        get
        {
            throw new System.NotImplementedException();
        }
        set
        {
        }
    }
    /// <summary>
    /// 用户登录
    /// </summary>
    /// <param name="user">用户名称</param>
    /// <param name="lastActivityDate">最后活动日期</param>
    public void Login(string user, DateTime lastActivityDate)
    {
        User = new InterfaceAdapter();

        User.Login(user, lastActivityDate);
    }
}

接口定义如下:

interface IUser
{
    void Login(string name,DateTime lastActivityDate);
}

服务类定义如下:

/// <summary>
/// 服务类
/// </summary>
class Service
{
    /// <summary>
    /// 用户登录
    /// </summary>
    /// <param name="user">用户名称</param>
    public void UserLogin(string user)
    {
        throw new NotImplementedException();
    }
}

接口适配器定义如下:

class InterfaceAdapter : Service, IUser
{
    /// <summary>
    /// 用户登录
    /// </summary>
    /// <param name="name"></param>
    /// <param name="lastActivityDate"></param>
    public void Login(string name, DateTime lastActivityDate)
    {
        UserLogin(name);

        UpdateActivityDate(name, lastActivityDate);
    }

    private void UpdateActivityDate(string name, DateTime lastActivityDate)
    {
        throw new NotImplementedException();
    }
}

此时可以直接实例化Client类并完成登录调用,例如:

Client client = new Client();

client.Login("admin",DateTime.Now);

以上是接口适配器的示例,如果客户端不存在这样的一个接口,那么可以使用对象适配器。

2. 对象适配器

使用对象适配器时,需要创建客户类的一个子类,并重写相应的方法,在该适配器类中通过服务类完成相应的功能并执行其他操作,在执行客户类操作时可以通过适配器完成。但是需要注意把客户端相应的方法定义为virtual以能够重写。上述示例使用对象适配器的类关系图如下所示:

image 

ObjectAdapter定义如下:

class ObjectAdapter:Client
{
    internal Service Service
    {
        get;
        set;
    }

    public override void Login(string user, DateTime lastActivityDate)
    {
        Service.UserLogin(user);
        UpdateActivityDate(user, lastActivityDate);
    }

    private void UpdateActivityDate(string name, DateTime lastActivityDate)
    {
        throw new NotImplementedException();
    }
}

在执行用户登录时可以通过ObjectAdapter完成,例如:

Client client = new ObjectAdapter();

client.Login("admin",DateTime.Now);

和接口适配器相比,对象适配器更加脆弱,这主要是由于对象适配器没有引入接口,在服务类发生变化时,仍然需要修改客户端代码。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值