OOP原则改善ADO.NET

基于OOP原则优化

原因:在程序中只要有哪个程序功能需要对数据库进行访问操作,那么必须要有之前学习的四个步骤:(创建数据库连接对象-创建数据库命令对象-针对不同的命令执行结果是否选择使用另外两个对象对结果进行处理)

因此:决定使用面向对象的原则对数据库的访问操作功能进行单独提取

通用数据访问类

实现代码的复用

  1. 代码复用的基本形式:编写一个通用的方法

  2. 代码复用技术的要求:

    1. 原则:提取不变的,封装改变的

    2. 技巧:不变的作为“方法体”,变化的作为方法的“参数”。

DBHelper/SQLHelper类的封装

   
 /// <summary>
    /// 数据库访问操作类,该对象中提供了对数据库进行各种操作(增、删、改、查)的方法
    /// </summary>
    class SQLHelper
    {
        static string con = "Server=.;DataBase=InLettDB;UID=sa;Pwd=123;";
        /// <summary>
        /// 该方法可以对SQL语句执行完成之后返回结果为受影响行数的SQL语句进行命令执行(增加、删除、修改数据操作);
        /// </summary>
        /// <param name="sql">要执行的SQL语句</param>
        /// <returns></returns>
        public static int ExecuteNonQuery(string sql)
        {
            SqlConnection sqlCon = new SqlConnection(con);
            SqlCommand cmd = new SqlCommand(sql, sqlCon);
            sqlCon.Open();
            int res = cmd.ExecuteNonQuery();
            sqlCon.Close();
            return res;
        }
​
        /// <summary>
        ///  该方法可以对SQL语句执行完成之后返回结果中的首行首列数据,适用于SQL语句查询单一结果值
        /// </summary>
        /// <param name="sql">查询的SQL语句</param>
        /// <returns></returns>
        public static object ExecuteScalar(string sql)
        {
            SqlConnection sqlCon = new SqlConnection(con);
            SqlCommand cmd = new SqlCommand(sql, sqlCon);
            sqlCon.Open();
            object res = cmd.ExecuteScalar();
            sqlCon.Close();
            return res;
        }
    }

基于对象职责明确原则优化

回顾之前的代码:数据展示代码和数据库访问代码混杂编写在一起

面向对象的基本原则:

  1. 单一职责原则(对象职责明确原则)

    要求:一个对象只需要做好一件事情,必须专注,职责过多容易引起变化的原因就越多,程序就不稳定(高内聚,低耦合)

  2. 开放封闭原则(核心原则)

    要求:需要变化时尽量少的修改类的设计,而是通过扩展类来完成。即封闭修改,开放扩展

  3. 依赖倒置原则(OOP精髓)

    要求:基于接口编程,高层模块调用接口,底层模块实现接口,防止底层变化直接影响高层

  4. 接口隔离原则

    要求:尽可能多的使用专用的小接口,而不是总接口,避免接口过于复杂

  5. 里氏替换原则

    要求:在继承关系子类可以替换父类,虚拟机可以根据父类变量动态的找到具体的子类对象,从而实现子类

问题分析与解决

前后台代码混编的缺点

  1. 程序编写人员必须非常熟悉后台数据的设计

  2. 业务逻辑负责时很难查找错误,并且不利于后期的维护

  3. 不符合面向对象的设计思想-》违反对象职责明确原则

解决方案

  1. 将数据展示代码和数据访问代码进行分离

  2. 根据当前需要访问的后台实体,创建一个对应的数据访问类

  3. 将该实体操作的方法封装到对应的数据访问类型

对象职责明确原则总结

原则

分离“界面代码”和“数据访问代码”

好处

  1. 不管是什么类型的应用程序(Winform/WPF/WebForm/...)当界面法生变化的时候,数据访问部分一般不需要任何改变

  2. 同时前台设计人员和后台编写人员可以很好的分离

注意

当写程序时,界面中不能出现任何SQL语句,数据访问后台代码中也不应该有其他的业务逻辑代码

扩展

对象职责明确原则对后续“三层架构”、“工厂模式”、“MVC模式”...打下基础

OOP实体对象优化

为什么要使用实体对象

方法参数多的缺点

  1. 定义和使用不方便,很容易把参数写错

  2. 当对象的属性变化时,方法的参数必须改变

  3. 参数的改变,造成对象接口不稳定,降低了可维护性、可扩展性和安全性,与面向对象设计原则相悖

  4. 不符合面向对象中“低耦合,高内聚”的要求

  5. 后台方法编写依赖数据库完成

  6. 前台代码实现依赖后台代码方法的完成,团队中无法并行开发

问题解决思路

  1. 为类的设计提供一个规范,稳定对象的接口

  2. 不同开发人员只需要按照规范接口即可同步开发

问题解决办法

使用“实体类”作为方法参数,稳定对外接口

实体类的设计

概念

  1. 只包含属性和构造方法的类称为实体类

  2. 实体类属性和数据库实体属性一一对应(实体类中的属性和数据表中的字段的名称和数据类型保持一致)

数据库中有多少张表,程序中有多少实体类;每张表有多少列,对应的实体类有多少属性。

实体类

   class Student
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Sex { get; set; }
        public int Age { get; set; }
        public int SubID { get; set; }
    }

实体表

 

实体类对应的数据访问类

  
  class StudentServer
    {
        public int AddStudent(Student stu)
        {
            string sql = string.Format("INSERT INTO Student VALUES('{0}','{1}',{2},{3})", stu.Name,stu.Sex,stu.Age,stu.SubID);
            return SQLHelper.ExecuteNonQuery(sql);
        }
        public int DeleteStudent(Student stu)
        {
            string sql = string.Format("DELETE FROM Student WHERE ID={0}", stu.ID);
            return SQLHelper.ExecuteNonQuery(sql);
        }
    }
优点
  1. 方法变得简洁,对象属性化,不影响接口的稳定性

  2. 解析对象的属性,组合SQL语句

实体类总结

深入理解实体类

  1. 实体类除了和数据表对应之外,通常都有对应的数据访问类

  2. 实体类和对应的数据访问类,其实是一个对象的属性和方法的分离,这种分离是为了更好的体现系统可维护性

  3. 实体类的使用使得程序设计人员可以完全脱离对数据库的依赖

  4. 同时界面开发人员和后台数据访问类的开发人员可以明确分工

实体类的主要作用

  1. 封装数据:将用户输入的数据或后台查询的数据,封装为实体对象,简化接口

  2. 传递数据:在用户界面和数据访问类之间传递信息

  3. 在之后面向对象设计中有其他应用

实体类在面向对象设计中的地位

使用List集合封装多个同类型对象

      
  public List<Subject> GetSubjectList()
        {
            string sql = "SELECT * FROM Subject";
            SqlDataReader reader=SQLHelper.GetReader(sql);
            List<Subject> list = new List<Subject>();
            while (reader.Read())
            {
                Subject subject = new Subject() 
                {
                    SubID=Convert.ToInt32(reader["SubID"]),
                    SubName=reader["SubName"].ToString()
                };
                list.Add(subject);
            }
            reader.Close();
            return list;
        }

扩展实体的应用

为什么要对实体扩展

原因

问题:查询所有的学生信息,同时包括:姓名、性别、年龄、学习的课程

分析:查询结果是属于多个不同实体对象的信息重组

当前问题:我们没有定义能够封装不同实体对象信息的实体类

结论

  1. 不可能根据用户查询的要求进行单独设计查询实体类

  2. 如果单独设计的实体类面对用户的不同需求,经不起考验

组合扩展实体

组合扩展实体的要求

组合扩展实体是为了满足用户查询而设计的实体类,一般由目前存在的业务实体类重新组合而成

封装组合扩展实体

  
  class StudentAddSubject
    {
        public Student ObjStudent { get; set; }
        public Subject ObjSubject { get; set; }
    }
使用组合扩展实体封装多个对象的信息

        public List<StudentAddSubject> GetStudentInforSub()
        {
            string sql = "SELECT ID,Name,Sex,Age,SubName FROM Student INNER JOIN Subject ON Student.SubID=Subject.SUBID ";
            SqlDataReader reader = SQLHelper.GetReader(sql);
            List<StudentAddSubject> list = new List<StudentAddSubject>();
            while (reader.Read())
            {
                StudentAddSubject stu = new StudentAddSubject();
                stu.ObjStudent = new Student()
                {
                    ID = Convert.ToInt32(reader["ID"]),
                    Name = reader["Name"].ToString(),
                    Sex = reader["Sex"].ToString(),
                    Age=Convert.ToInt32(reader["Age"])
                };
                stu.ObjSubject = new Subject()
                {
                    SubName = reader["SubName"].ToString()
                };
                list.Add(stu);
            }
            reader.Close();
            return list;
        }

简单扩展实体

简单扩展实体要求

主要通过继承定义扩展实体,继承原有实体对象,并扩展自己新加的属性

封装简单扩展实体

   
 class TeacherInforSub:Teacher
    {
        public string SubName { get; set; }
    }
        public List<TeacherInforSub> GetTeacherInfors()
        {
            string sql = "SELECT ID,NAME,TEL,SubName FROM Teacher INNER JOIN Subject ON Teacher.SUBID=Subject.SUBID";
            SqlDataReader reader = SQLHelper.GetReader(sql);
            List<TeacherInforSub> list = new List<TeacherInforSub>();
​
            while (reader.Read())
            {
                TeacherInforSub teacherInfor = new TeacherInforSub()
                {
                    ID = Convert.ToInt32(reader["ID"]),
                    Name = reader["Name"].ToString(),
                    Tel = reader["TEL"].ToString(),
                    SubName = reader["SubName"].ToString()
                };
                list.Add(teacherInfor);
            }
            reader.Close();
            return list;
        }

面向对象查询总结

单对象查询

查询结果是查询的目标表中的一行数据,这一行数据相当于是数据表中的一个实体对象

实体类实例化一个对象进行封装数据

多对象查询

同类型多对象

查询结果是查询的目标表中的多行数据,一行数据相当于数据表中的一个实体对象,多行相当于多个实体对象

解决办法

使用List< T >泛型集合封装,“一般”实体封装。

不同类型多对象

查询结果是对于多张表进行联合查询的结果,一行数据相当于一个由联合表组成的一个新的实体对象

解决办法

采取“组合”扩展实体封装及“简单”扩展实体封装两种方案备选

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值