zoukankan      html  css  js  c++  java
  • 适配器模式

    摘要

    本文以C#示例说明适配器模式的概念和应用场景。

    定义

    适配器模式(adapter Pattern, 有时又被称为包装样式或者包装(wrapper), 是软件设计模式的一种。在此种模式中,将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类能在一起工作,做法是将自己的接口包裹在一个已存在的类中。维基百科

    Alt Text

    解读

    • 关键词:适配;
    • 使得原本由于接口不兼容而不能在一起工作的类可以一起工作;
    • 符合开闭原则;
    • 应用场景: 对接<第三方>类库(接口);项目重构背景下的新老接口对接;
    • 适配器模式解决的是正在服役的项目中存在的问题;
    • 对于正在开发的新项目,如非必要,不要使用适配器模式,条件允许的情况下请重构;
    • 项目中过多的适配器模式的应用,会让系统变得凌乱不堪,难以把握;
    • 在GoF的设计模式中, 适配器模式分为两种类型, 类适配器模式和对象适配器模式。 由于类适配器模式通过多重继承对一个接口与另一个接口进行匹配,而C#,Java等语言都不支持多重继承,因而这里只介绍对象适配器。

    现实生活中的场景

    • 翻译工作
    • 笔记本电源
    • 在Linux系统中运行Windows程序
    • ...

    思考

    下面用代码示例进行说明:

    Alt Text

    代码示例

    ORM包装

    假设数据库ORM有一个Get方法专门用于根据传入的SQL语句获取DataTable,而且此方法所在的Class不允许修改,随着数据的增多,开发人员需要一个获取分页数据的方法且能够自定义排序字段,但却不想每次获取数据时都编写分页查询的语句,这个时候,我们可以适配一个中间类来操作

    // adaptee - 获取DataTable
    public class DataRepo
    {
        public DataTable Get(string select_sql, string connectionString)
        {
            using (SqlConnection con = new SqlConnection(connectionString))
            using (var da = new System.Data.SqlClient.SqlDataAdapter(select_sql, con))
            {
                var dt = new DataTable();
    
                try
                {
                    con.Open();
                    da.Fill(dt);
                }
                catch (Exception)
                {
                    throw;
                }
    
                return dt;
            }
        }
    
    }
    
    // adapter - 分页获取数据
    public class UserDataRepo
    {
        public DataTable GetPageTable(string select_sql, int pageIndex, int pageSize, string orderfield = "date desc")
        {
            int row_from = (pageIndex - 1) * pageSize;
            var page_sql = $"select * from ({select_sql}) as cc order by cc.{orderfield}  
                            offset {row_from} row fetch next {pageSize} rows only";
    
            var dataRepo = new DataRepo();                        
            return dataRepo.Get(page_sql);
        }
    }
    
    // client - 分页获取
    public class UserOperationCls
    {
        var u_data_repo = new UserDataRepo();
        DataTable table = u_data_repo.GetPageTable("select * from Users where isDelete=false  
            and createDate > 2015-1-1", 
            1, 
            10, 
            "createDate desc");
    
        // do something else...
    }
    
    

    上面的例子,在 UserOperationCls 和 DataRepo 之间定义了一个适配类UserDataRepo,解决了用户和ORM之间的接口适配问题。可能例子太过简单,也可能不太恰当,就当是我扔了个砖头,没砸到人就权当是引玉了。

    延伸阅读:单接口适配器

    当不需要全部接口实现的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择的覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况,因此也称为单接口适配器模式。

    
     /// <summary>
    /// 日志操作接口
    /// </summary>
    public interface ILog
    {
        bool Insert(string message);
        void Clear();
        bool Delete(int id);
    }
    
    /// <summary>
    /// 实现ILog接口的抽象基类
    /// </summary>
    public abstract class absLog : ILog
    {
        public abstract void Clear();
    
        public abstract bool Delete(int id);
    
        public virtual bool Insert(string message)
        {
            throw new NotImplementedException();
        }
    }
    
    /// <summary>
    /// 用户日志
    /// </summary>
    public abstract class absUserLog : absLog
    {
        /// <summary>
        /// 删除指定ID的日志
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public override bool Delete(int id)
        {
            throw new NotImplementedException();
        }
    
        /// <summary>
        /// 清空日志,注意加了sealed关键字
        /// </summary>
        public sealed override void Clear()
        {
    
        }
    }
    
    /// <summary>
    /// 更完美一点:彻底隐藏Clear方法
    /// </summary>
    public class WebUserLog : absUserLog
    {
        public override bool Delete(int id)
        {
            return base.Delete(id);
        }
    
        public override bool Insert(string message)
        {
            return base.Insert(message);
        }
    }
    
    
    

    好了,今天的适配器模式就聊到这里,大家继续回去写BUG吧

    All Text

  • 相关阅读:
    Java动态绑定与多态
    Java中的equals,==,compareTo和compare的比较
    Java访问控制权限
    如何用eclipse进行jar文件打包?
    堆和栈的区别
    Java命名规则
    Java面向对象的基本概念
    java中length,length(),size()区别
    《剑指offer》第二十题:表示数值的字符串
    《剑指offer》第十九题:正则表达式匹配
  • 原文地址:https://www.cnblogs.com/zanpen2000/p/7607986.html
Copyright © 2011-2022 走看看