工程视角:数据结构驱动的应用开发--字典(dictionary),列表(list)与实体

这里写目录标题

业务

业务场景

在一个金融应用系统中,用户需要登录自己的账户以查看和管理个人信息。系统首先提供一个登录界面,让用户输入必要的登录信息(尽管登录界面未在图片中显示)。一旦用户成功登录,系统将根据用户的登录凭证(如用户名和密码)从数据库中检索该用户的详细账户信息,并将这些信息显示在一个页面上。

流程分析

从数据库读取数据显示在页面上的流程

  1. 用户登录:
    用户通过登录界面输入账号和密码。
    系统验证用户的登录凭证是否正确。
  2. 检索账户信息:
    一旦登录验证成功,系统会根据用户的账号(卡号、密码等)在数据库中查询对应的账户信息。
    查询的账户信息包括但不限于:卡号、姓名、余额、类型以及注册日期。
  3. 数据处理:
    系统从数据库中检索到的信息需要进行适当的处理,以确保其适用于页面显示(例如,格式化日期、转换货币格式等)。
  4. 页面显示:
    将处理后的数据传递到前端页面。
    页面上将显示用户的卡号、姓名、余额、类型和注册日期等账户信息。
  5. 用户交互:
    用户可以在页面上查看这些信息,并根据需要进行进一步的操作(如修改信息、进行交易等)。

实现

在这个业务场景中,使用C#结合三层架构(UI层、业务逻辑层BLL、数据访问层DAL)和接口与反射来实现,:

数据访问层(DAL)

DAL 负责与数据库交互,获取数据。

使用 SQLHelper 类来执行SQL语句,并返回 DataTable 对象。

将 DataTable 对象转换为 List,其中dynamic在运行时实际是自定义的实体类,用来存储数据库中的一行数据。

D层的select方法

csharp 复制代码
 public List<dynamic> Select(string cardID)
 {
     //CustomerEntity customerEntity = (CustomerEntity)enCustomer; //实例化一个消费者实体类
 
     //创建sql语句
     string sql = "select * from T_Customer where cardID=@cardID";
     //给参数变量进行赋值
     SqlParameter sqlParameter = new SqlParameter("@cardID",cardID);
     //建立一个数据表,将查询的结果放到数据表中
     DataTable dataTable = SQLHelper.GetDataTable(sql, sqlParameter);
     //创建list集合
     List<object> list = new List<object>();
     //并遍历数据表中的数据给list集合
     foreach (DataRow row in dataTable.Rows) 
     {
         list.Add(new CustomerEntity()
         {
             CardID = Convert.ToInt32(row["cardID"]),//将获取的dataTable表中的数据进行类型转换后赋值给list集合中的数据
             CardType = row["cardType"].ToString(),
             Password = row["password"].ToString().Trim(),
             CardCash = Convert.ToDecimal(row["cardCash"]),
             Time = Convert.ToDateTime(row["time"]),
             UserName = row["userName"].ToString(),
         });
     }
     return list;
 }
        

业务逻辑层(BLL)

BLL 接收来自UI层的请求,调用DAL层获取数据,并进行业务逻辑处理。

BLL层可以将 List转换为 Dictionary<Key, Value>,其中 Key 通常是唯一标识符(如账号),Value 是整个实体或实体的部分属性集合。

csharp 复制代码
public Dictionary<string, string> GetDictionary(params string[] values)
{
    ISelect iCustomerSelect = AbstractFactory.Factory.CreateConcreteClass("ConcreteDAL", "DAL", "T_Customer");
    List<dynamic> list = iCustomerSelect.Select(values[0]);
    Dictionary<string, string> dictionary = new Dictionary<string, string>();
    foreach (CustomerEntity row in list)   //遍历传入的数据添加至字典
    {
        dictionary.Add("txtCard", row.CardID.ToString());
        dictionary.Add("txtCardType", row.CardType.ToString());
        dictionary.Add("txtCash", row.CardCash.ToString());
        dictionary.Add("txtTime", row.Time.ToString());
        dictionary.Add("txtUserName", row.UserName.ToString());
    }
    return dictionary;  //将字典返回
}

但请注意,通常BLL层会直接返回List或更复杂的对象,如ViewModel,而不是直接返回Dictionary。Dictionary的使用更多是在内部处理中,以便快速查找或映射数据。(这里为了说明Dictionary的好处)

用户界面层(UI)

UI 层负责展示数据和接收用户输入。

UI层通过接口和反射调用BLL层的方法,获取数据并展示。

UI层接收到的数据可以是这里设计为 Dictionary<TKey, TValue>

csharp 复制代码
private void tabCardContent_Enter(object sender, EventArgs e)
{
    Dictionary<string, string> dictionary = new Dictionary<string, string>(); //实例化一个字典
    InvariableEntity invariable = new InvariableEntity();   //实例化常量类
    IDictionary iDictionary = AbstractFactory.Factory.CreateConcreteClass("ConcreteBLL", "BLL", "T_Customer");
    dictionary = iDictionary.GetDictionary(invariable.UserID); //接受外观层传入的数据
    try
    {
        TextAssignment textAssingment = new TextAssignment(); //实例化Utility的文本赋值方法
        textAssingment.AddData(tabContent, dictionary); //将选项卡名称和已经赋值的字典传入方法中进行添加
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);  //将未查到的信息通知给用户
    }
}

工具类

向文本框赋值的方法,抽出来一个工具类

csharp 复制代码
public class TextAssignment
{
    /// <summary>
    /// 文本赋值
    /// </summary>
    /// <param name="control">要赋值的tab选项卡名称</param>
    /// <param name="dictionary">字典名称</param>
    public void AddData(Control controlTab, Dictionary<string, string> dictionary)
    {
        string row;//定义一个字符串接受返回值
        foreach (Control control in controlTab.Controls)   //遍历control控件,
        {
            if (control is TextBox)  //如果空间中包含文本框
            {
                dictionary.TryGetValue(control.Name, out row);  //根据控件名称传出对应的value值
                control.Text = row;  //将值赋值给对应的文本框
            }
        }
    }
}

设计思路

为什么抽出工具类

抽出TextAssignment这样的工具类来向文本框赋值,而不是直接在代码中使用实体或数据直接赋值给文本框,有几个重要的原因和好处:

1、解耦和复用:

解耦 :通过将赋值逻辑封装在独立的类中,业务逻辑代码(如UI层代码)与具体的赋值操作解耦。意味着如果将来需要改变赋值逻辑(比如添加验证、修改数据格式等),只需修改这个工具类,而不需要去修改业务逻辑代码。

复用:这个工具类可以在多个地方被复用。如果有多个地方需要类似的赋值操作,可以直接调用这个工具类的方法,而不是每次都写一遍相同的逻辑。

2、清晰性和可维护性:

清晰性:通过将赋值逻辑放在专门的类中,代码更加清晰易懂。其他开发者(或未来的你)在查看代码时,可以更容易地理解各个部分的功能和职责。

可维护性:当需要修改或扩展赋值逻辑时,只需要关注这个工具类,而不需要在整个项目中搜索所有相关的赋值代码。

3、灵活性和可扩展性:

灵活性:你可以轻松地修改这个工具类来适应不同的需求。比如,你可以添加额外的参数来控制赋值行为(如是否进行验证、是否允许空值等)。

可扩展性:如果需要支持除了文本框以外的其他控件的赋值(如下拉列表、复选框等),可以在工具类中添加更多的逻辑来处理这些控件,而不需要在每个使用点都进行改动。

4、测试:

将赋值逻辑封装在单独的类中,可以更容易地对其进行单元测试。可以创建不同的测试场景来验证赋值逻辑的正确性,而不需要运行整个应用程序。

5、遵循设计模式:

抽出工具类也符合一些设计模式的原则,如单一职责原则(一个类应该只负责一项职责),开放封闭原则(软件实体(类、模块、函数等)应该对扩展开放,对修改关闭)等。

除了上面经常说的软件工程的思想,还有一点,也是我特别想说的--变量的思想

给文本框赋值,使用实体的属性不是很方便吗,为什么要引入字典,来看一下,这个逻辑,给文本框赋值,需要实体.属性,假设使用泛型可以接收任何实体,那么实体就变成了一个变量,用A来表示,为了让整体(实体.属性)变得有通用性,就要整体都是变量,假设属性是b,为了整体是变量,就必须让实体是变量,属性也是变量--A.b.但是每个实体的属性是特定的,怎么让它是变量,只有字典这种结构更适合。

如果没理解,再从业务说起:

在创建一个能够通用地处理不同实体对象并将其属性映射到UI控件(如文本框)的场景中,存在几个挑战,主要是因为不同的实体类会有不同的属性集。看这里的几个解决方案

使用反射:

反射允许在运行时检查对象的类型并访问其属性。但是,它可能会引入性能开销,并且如果属性名或类型不匹配,可能会导致运行时错误。

定义接口或基类:

创建一个包含所有可能属性的接口或基类,并让实体类实现该接口或继承自该基类。然而,这要求所有实体都有相同的属性集,这在大多数情况下是不切实际的。

使用字典:

字典提供了一种灵活的方式来映射键(通常是字符串)到值(可以是任何类型)。在这个场景下,可以将实体对象的属性名作为键,将属性值作为值存储在字典中。这样,无论实体对象的类型如何,都可以使用相同的逻辑来处理这些字典项,并将它们映射到UI控件上。

为什么使用字典可能是最佳选择

灵活性:字典可以包含任意数量的键值对,且键和值可以是任意类型(尽管在实际应用中,键通常是字符串,值是与UI控件相关的数据类型)。这使得字典能够轻松适应不同实体对象的属性集。

简单性:与反射或表达式树相比,字典的API相对简单且易于使用。不需要编写复杂的代码来动态解析属性名或构建查询。

性能:虽然反射可能会引入性能开销,但字典的查找和更新操作通常很快。此外,如果只需要在UI加载时从实体对象填充到字典中,并在之后直接使用这些字典项来更新UI,那么性能影响可能很小。

总之,虽然直接使用实体或数据给文本框赋值在某些情况下是可行的,但在更复杂或更大型的项目中,使用工具类来封装这类操作通常会带来更好的代码质量和可维护性。

关于U层使用字典的好处

在UI层使用 Dictionary 而不是 List 或实体类,有以下几个潜在的好处(尽管这些好处可能并不总是适用):

快速查找:如果你需要频繁地根据某个键(如账号)来查找数据,Dictionary 提供了非常快的查找速度(接近O(1))。

减少内存占用(在某些情况下):如果你只需要存储数据的某个子集,或者只需要快速访问数据的一部分,使用 Dictionary 可能比存储整个实体对象更节省内存。

简化数据访问:在某些情况下,使用 Dictionary 可以使数据访问逻辑更加清晰和简单。

然而,Dictionary 也有其局限性,比如它不保证元素的顺序(在.NET Core 3.5及以后的版本中,Dictionary 的枚举顺序是确定的,但它仍然不是有序的集合),并且它只能根据一个键来索引数据。

字典的出现 是为了提供一种更高效、更灵活的方式来存储和访问键值对数据。在编程中,我们经常需要快速查找或映射数据,而 Dictionary 正好满足了这一需求。

最后,选择使用 List、Dictionary 还是其他数据结构,应该基于具体需求和场景来决定。在UI层,通常更倾向于使用易于绑定和展示的数据结构,如 List 或自定义的ViewModel类。

工程视角

在计算机科学和软件工程领域,数据结构是组织、存储和管理数据的方式,它们对于设计高效、可扩展和可维护的软件系统至关重要。字典、List(列表)与实体都是常见且重要的数据结构,它们在数据处理、算法设计和系统开发中扮演着核心角色。

  1. 字典(Dictionary)

定义:

字典是一种通过键(Key)来访问其对应值(Value)的数据结构。每个键都是唯一的,并且与一个值相关联。这种键值对的关系使得字典成为了一种非常灵活和高效的数据存储方式。

具体实现:

在许多编程语言中,字典都是通过哈希表(Hash Table)实现的。哈希表利用哈希函数将键映射到数组中的一个索引位置,从而实现对字典的快速访问。

Python中的dict类型就是一个典型的字典实现,它支持通过键来快速检索、插入和删除元素。

特点:

快速访问:通过键可以快速定位到对应的值,时间复杂度通常为O(1)(在平均情况下)。

无序性:字典中的元素是无序的,即它们没有固定的顺序。

键值唯一性:字典中的键必须是唯一的,但值可以重复。

  1. List(列表)

定义:

列表是一种有序的数据结构,它可以包含多个元素,并且这些元素可以是不同类型的。列表中的元素可以通过索引来访问,索引通常是从0开始的。

具体实现:

列表在内存中是连续存储的,这使得通过索引访问元素变得非常高效。

Python中的list类型就是一个典型的列表实现,它支持动态扩容,即在运行时可以添加或删除元素。

特点:

有序性:列表中的元素是有序的,即它们有固定的顺序。

随机访问:通过索引可以立即访问任何位置的元素,时间复杂度为O(1)。

动态性:列表的大小可以在运行时动态改变,支持在任意位置插入或删除元素(尽管这可能会导致后续元素的移动,从而影响效率)。

  1. 实体(Entity)

定义:

在软件工程中,实体通常指的是具有特定属性和行为的对象或数据结构的实例。实体可以是简单的数据结构(如字典或列表),也可以是复杂的数据结构(如类实例)。

关系:

实体可以包含字典或列表作为其属性,用于存储和管理相关数据。

例如,一个用户实体可能包含一个字典来存储用户的个人信息(如姓名、年龄等),以及一个列表来存储用户的订单信息。

特点:

封装性:实体通常封装了数据和操作这些数据的方法,提供了对数据的保护和抽象。

可重用性:实体可以被多次实例化,并在不同的上下文中重复使用。

多态性:在面向对象编程中,实体(类实例)可以支持多态性,即不同的实体对象可以响应相同的消息(方法调用)但以不同的方式执行。

综上所述,字典、List(列表)与实体都是重要的数据结构,它们在数据结构驱动的应用开发中扮演着关键角色。字典提供了快速访问和键值对存储的能力;列表提供了有序和动态数据集合的能力;而实体则通过封装、可重用性和多态性为软件系统提供了丰富的数据模型和行为支持。

相关推荐
Leo.yuan几秒前
39页PDF | 华为数据架构建设交流材料(限免下载)
数据结构·华为
半夜不咋不困14 分钟前
单链表OJ题(3):合并两个有序链表、链表分割、链表的回文结构
数据结构·链表
忘梓.1 小时前
排序的秘密(1)——排序简介以及插入排序
数据结构·c++·算法·排序算法
y_m_h3 小时前
leetcode912.排序数组的题解
数据结构·算法
1 9 J3 小时前
数据结构 C/C++(实验三:队列)
c语言·数据结构·c++·算法
921正在学习编程3 小时前
数据结构之二叉树前序,中序,后序习题分析(递归图)
c语言·数据结构·算法·二叉树
毕竟秋山澪4 小时前
岛屿数量 广搜版BFS C#
数据结构·算法·宽度优先
黎明晓月4 小时前
Java之字符串分割转换List
java·windows·list
slp_4 小时前
java list使用基本操作
java·开发语言·list
乔冠宇5 小时前
环状 DNA 序列的最小表示法
java·数据结构·算法