『7x24小时有问必答』

适配器模式(Adapter Pattern)代码解析

---

一、概述

本文档详细解析  frmDataAdapter.cs  中适配器模式的完整实现,核心代码包括:
lskids = new ListAdapter(lsKids);         // 对象适配器:适配 ListBox

lsNewKids = new GridAdapter(dGrid);      // 类适配器:适配 DataGridView

核心设计意图
通过  ListAdapter  将 WinForms 的  ListBox  控件包装一层,提供更简洁、更业务化的接口
通过  GridAdapter(继承  LstAdapter)实现类适配器模式,统一不同 UI 控件的操作接口
实现 UI 控件与业务逻辑的解耦,使得业务代码可以以统一方式操作不同类型的列表控件

---

二、类图结构

2.1 完整类关系图

┌─────────────────────────────────────────────────────────────┐

│                           frmDataAdapter                               │   ← 客户端/业务层

│    - lskids: ListAdapter                                          │

│    - lsNewKids: GridAdapter                                     │

│    - lsKids: ListBox                                                │

│    - dGrid: DataGridView                                          │

└───────────────────────┬─────────────────────────────┘

                                    │ 使用(调用统一接口)

                                    ▼

┌─────────────────────────────────────────────────────┐

│                        LstAdapter(抽象目标)                        │   ← 抽象适配器接口

│    + Add(string)                                                      │

│    + Add(Swimmer)                                                    │

│    + SelectedIndex()                                                │

│    + Clear()                                                            │

│    + clearSelection()                                              │

└───────────────────────┬─────────────────────────────┘

               ▲                                       ▲

               │ 继承                               │ 继承

               │                                       │

┌─────────┴─────────┐      ┌──────────┴──────────┐

│    ListAdapter       │      │    GridAdapter          │   ← 具体适配器

│    (对象适配器)       │      │    (类适配器)            │

│    - listBox          │      │    - grid                  │

│    - ListBox          │      │    - dTable               │

└─────────┬─────────┘      └──────────┬──────────┘

               │ 组合                               │ 组合

               ▼                                       ▼

┌─────────────────┐      ┌─────────────────────┐

│      ListBox         │      │    DataGridView         │   ← 被适配者

│   (WinForms控件) │      │    (WinForms控件)      │

└─────────────────┘      └─────────────────────┘

2.2 对象适配器模式结构图(ListAdapter)

      客户端 (Client)

              │

              │ 调用简化接口

              ▼

     ListAdapter ?──────────── 目标接口 (Target)

      (Adapter)

              │

              │ 持有(Has-A)组合

              ▼

      ListBox ?─────────────── 被适配者 (Adaptee)

2.3 类适配器模式结构图(GridAdapter)

      客户端 (Client)

              │

              │ 调用统一接口

              ▼

     GridAdapter ?──────────── 目标接口 (Target)

      (Adapter)

              │

              │ 继承(Is-A)

              ▼

     LstAdapter ?───────────── 抽象目标 (Target)

              │

              │ 组合(Has-A)

              ▼

   DataGridView ?──────────── 被适配者 (Adaptee)

---

三、核心代码剖析

3.1 对象适配器:ListAdapter

public class ListAdapter

{

      private ListBox listBox;   // 持有被适配的 ListBox 控件

      // 构造函数注入

      public ListAdapter(ListBox lb)

      {

            listBox = lb;

      }

      // 简化的业务接口:添加字符串

      public void Add(string s)

      {

            listBox.Items.Add(s);   // 内部调用原生方法

      }

      // 简化的业务接口:添加 Swimmer 对象(自动格式化)

      public void Add(Swimmer sw)

      {

            listBox.Items.Add(sw.getName() + "\t" + sw.getTime());

      }

      // 获取选中索引

      public int SelectedIndex()

      {

            return listBox.SelectedIndex;

      }

      // 清空列表

      public void Clear()

      {

            listBox.Items.Clear();

      }

      // 取消选中

      public void clearSelection()

      {

            int i = SelectedIndex();

            if (i >= 0)

            {

                  listBox.SelectedIndex = -1;

            }

      }

}

设计要点:通过组合方式持有  ListBox,提供业务友好的简化接口,屏蔽底层 API 复杂度。

3.2 类适配器:GridAdapter

public class GridAdapter : LstAdapter   // 继承抽象目标接口

{

      private DataGridView grid;

      private DataTable dTable;

      private int row;

      public GridAdapter(DataGridView grd)

      {

            grid = grd;

            dTable = (DataTable)grid.DataSource;

            grid.MouseDown += Grid_Click;   // 绑定点击事件

            row = -1;

      }

      // 处理网格点击事件,记录行索引

      private void Grid_Click(object sender, MouseEventArgs e)

      {

            DataGridView.HitTestInfo hti = grid.HitTest(e.X, e.Y);

            if (hti.Type == DataGridViewHitTestType.Cell)

            {

                  row = hti.RowIndex;

            }

      }

      // 添加 Swimmer 对象到 DataGridView

      public void Add(Swimmer sw)

      {

            DataRow row = dTable.NewRow();

            row["Frname"] = sw.getFrname();

            row[1] = sw.getLName();

            row[2] = sw.getAge();

            dTable.Rows.Add(row);

            dTable.AcceptChanges();

      }

      public void clearSelection() { }   // 空实现(DataGridView 不需要)

      public int SelectedIndex()

      {

            return row;

      }

      public void Clear()

      {

            int count = dTable.Rows.Count;

            for (int i = 0; i < count; i++)

            {

                  dTable.Rows.Delete();

            }

      }

}

设计要点:通过继承  LstAdapter  实现接口统一,同时组合  DataGridView  和  DataTable,实现对不同控件的适配。

3.3 客户端调用

public partial class frmDataAdapter : Form

{

      private ListAdapter lskids;       // 对象适配器引用

      private GridAdapter lsNewKids;   // 类适配器引用

      private void init()

      {

            swdata = new SwimData("../../..//swimmers.txt");

            lskids = new ListAdapter(lsKids);    // 适配 ListBox

            createGrid();

            lsNewKids = new GridAdapter(dGrid); // 适配 DataGridView

            reload();

      }

      private void btClone_Click(object sender, EventArgs e)

      {

            int i = lskids.SelectedIndex();

            if (i >= 0)

            {

                  Swimmer sw = swdata.getSwimmer(i);

                  lsNewKids.Add(sw);            // 统一接口调用

                  lskids.clearSelection();   // 统一接口调用

            }

      }

}

关键优势:客户端代码通过统一接口操作不同控件,无需关心底层实现细节。

---

四、适配器模式优势

4.1 接口对比

操作
直接使用 ListBox
直接使用 DataGridView
使用适配器
添加字符串
listBox.Items.Add(str)
需手动创建 DataRow
adapter.Add(str)
添加 Swimmer
需手动格式化
需手动创建 DataRow
adapter.Add(swimmer)
获取选中索引
listBox.SelectedIndexgrid.SelectedRows[0].Indexadapter.SelectedIndex()
清空列表
listBox.Items.Clear()dTable.Rows.Clear()adapter.Clear()
取消选中
listBox.SelectedIndex = -1grid.ClearSelection()adapter.clearSelection()

4.2 设计价值

维度
说明
本实现体现
解耦
业务代码不直接依赖 UI 控件
frmDataAdapter
  依赖  ListAdapter/GridAdapter,而非直接依赖  ListBox/DataGridView
简化
屏蔽底层 API 复杂度
统一的  Add()Clear()SelectedIndex()  接口
可替换
更换控件只需修改适配器
新增控件类型只需实现新适配器,业务代码无需改动
可测试
易于 Mock 和单元测试
可注入 Mock 适配器进行单元测试,无需实际 UI 控件
统一接口
不同控件提供一致接口
ListAdapter
  和  GridAdapter  实现相同接口,客户端透明调用
业务封装
将业务逻辑封装到适配器
Add(Swimmer)
  自动处理对象格式化,业务代码无需关心

---

五、对象适配器 vs 类适配器

5.1 对比分析

特性
对象适配器(ListAdapter)
类适配器(GridAdapter)
实现方式
组合(Composition):持有被适配对象
继承(Inheritance)+ 组合:继承抽象目标,持有被适配对象
灵活性
高,可适配多个对象
中等,继承固定但可组合多个对象
耦合度
低,仅通过接口依赖
较高,继承带来强耦合
代码复用
需重复实现接口方法
可继承父类实现,代码复用性好
扩展性
易于扩展新适配器
受继承层级限制
适用场景
需要适配多个不同对象
需要严格遵循接口契约,代码复用需求高
本项目实现ListAdapter
  持有  ListBox
GridAdapter
  继承  LstAdapter,持有  DataGridView

5.2 本项目中的混合使用

本项目巧妙地结合了两种适配器模式:
// 对象适配器:直接组合 ListBox

public class ListAdapter

{

      private ListBox listBox;   // 组合关系

}

// 类适配器:继承抽象目标 + 组合控件

public class GridAdapter : LstAdapter   // 继承关系

{

      private DataGridView grid;   // 组合关系

}

设计优势
ListAdapter
  作为基础适配器,采用对象组合方式,灵活适配  ListBox
GridAdapter
  通过继承  LstAdapter  确保接口一致性,同时组合  DataGridView
客户端可以通过统一接口操作两种不同的控件

5.3 选择建议

场景
推荐模式
原因
需要适配多个独立对象
对象适配器
组合方式更灵活
需要严格遵循接口契约
类适配器
继承保证接口一致性
需要代码复用
类适配器
可继承父类实现
需要运行时切换适配对象
对象适配器
可动态注入不同对象

---

六、总结

适配器模式本质:接口转换,解决接口不兼容问题,使原本不兼容的接口能够协同工作。
本实现要点
实现要素
具体体现
双重模式
同时使用对象适配器(ListAdapter)和类适配器(GridAdapter
组合优先ListAdapter
  通过组合持有  ListBox,降低耦合
继承保证一致性GridAdapter
  继承  LstAdapter,确保接口统一
接口简化
将复杂的控件 API 封装为简洁的业务接口
业务封装Add(Swimmer)
  自动处理对象格式化,简化客户端代码
解耦设计
业务代码依赖适配器接口,而非具体控件
实践价值
统一接口
:不同 UI 控件通过适配器提供一致的操作接口
易于扩展
:新增控件类型只需实现新适配器
便于测试
:可通过 Mock 适配器进行单元测试
代码复用
:类适配器模式支持代码复用
改进建议
抽取  ILstAdapter  接口,使适配器实现更加规范
使用泛型适配器支持更多类型的数据源
添加异常处理和日志记录
1.webp

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

上一主题上一主题         下一主题下一主题
QQ手机版小黑屋粤ICP备17165530号

关于我们·投诉举报· 用户帮助· 联系我们 · 本站服务 · 版权声明· 隐私政策 · 投搞指南

法律保护:PLC技术网,plcjs.com,plcjs.net等字样
Copyright 2010-2030. All rights reserved. 


微信公众号二维码 抖音二维码 百家号二维码 今日头条二维码哔哩哔哩二维码