学习设计模式《十》——代理模式

一、基础概念

        代理模式的本质【控制对象访问】;

        代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问;

        代理模式的功能:代理模式是通过创建一个代理对象,用这个代理对象去代表真实的对象;客户端得到这个代理对象后,对客户端没有什么影响,就跟得到了真实对象一样来使用【当客户端操作整个代理对象的时候,实际上功能最终还是会由真实的对象来完成,只不过是由通过代理操作的,也就是客户端操作代理,代理操作真正的对象】正是因为有代理对象夹在客户端和被代理的真实对象中间,相当于一个中转,那么在中转的时候就有很多花招可以使用了(如:判断权限,若没有足够权限就不给你中转等等)。

代理的分类
序号代理分类说明
1虚代理根据需要来创建开销很大的对象,该对象只有在需要的时候才会被真正创建
2远程代理用来在不同的地址空间上代表同一个对象,这个不同的地址空间可以是在本机,也可以在其他机器上
3Copy-on-Write代理在客户端操作的时候,只有对象改变了,才会真的拷贝(或克隆)一个目标对象,算是虚代理的一个分支
4保护代理控制对原始对象的访问,如果有需要,可以给不同的用户提供不同的访问权限,以控制他们对原始对象的访问
5Cache代理为那些昂贵操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果
6防火墙代理保护对象不被恶意用户访问和操作
7同步代理使多个用户能够同时访问目标对象而没有冲突
8智能指引在访问对象时执行一些附加操作(如:对指向对象的引用计数、第一次引用一个持久对象时,将它装入内存等)

        何时选用代理模式?      
                1、需要为一个对象在不同的地址空间提供局部代表的时候,可以使用远程代理;
                2、需要按照需要创建开销很大的对象的时候,可以使用虚代理;
                3、需要控制对原始对象的访问的时候,可以使用保护代理;
                4、需要在访问对象执行一些附加操作的时候,可以使用智能指引代理。

二、代理模式示例

        业务需求:在HR项目中,客户提出当选择一个部门或分公司的时候,要把该部门或分公司下的所有员工都显示出来(且只需要显示用户的名称即可),而且不要翻页,方便他们进行业务处理;但是当点击某个员工时,可查看该员工的详细信息。

  2.1、不使用任何模式的示例

        直接使用sql语句将指定部门下关联的所有员工信息都获取出来即可:

        2.1.1、准备工作:

        (为了方便获取某个部门或者某个分公司下的所有员工信息,设计部门编号的时候,就是按照层级来进行编码,如:母公司编码为01,下面的分公司就是0101、0102、0103以此类推,在下一级的公司部门编号就是:010101、010102、010103,...;010201、010202、010203,...这样的部门编码设计虽然不优雅,但是实用,像这种获取某个部门或某个分公司下的所有员工信息功能,就不用递归查找,直接使用like匹配部门编号开头即可)。

在sqlserver中创建部门表与用户信息表:

--创建部门表
CREATE TABLE [dbo].[Depment]([ID] [nvarchar](10) PRIMARY KEY, NOT NULL,[DepmentName] [nvarchar](10) NOT NULL,[DepmentDesc] [nvarchar](100) NULL
);INSERT INTO [dbo].[Depment]([ID], [DepmentName], [DepmentDesc]) VALUES (N'01', N'总公司', N'这是公司总部,管理旗下所有公司');
INSERT INTO [dbo].[Depment]([ID], [DepmentName], [DepmentDesc]) VALUES (N'0101', N'一分公司', N'这是公司在上海的第一个分公司 ');
INSERT INTO [dbo].[Depment]([ID], [DepmentName], [DepmentDesc]) VALUES (N'0102', N'二分公司', N'这是公司在苏州的第二个分公司');
INSERT INTO [dbo].[Depment]([ID], [DepmentName], [DepmentDesc]) VALUES (N'0103', N'三分公司', N'这是公司在深圳的第三个分公司');
INSERT INTO [dbo].[Depment]([ID], [DepmentName], [DepmentDesc]) VALUES (N'010101', N'信息部', N'这是公司在上海的第一个分公司的下属信息部,主要复杂公司技术相关的所有事物(如软件产品研发、实施部署运维等相关工作)');
INSERT INTO [dbo].[Depment]([ID], [DepmentName], [DepmentDesc]) VALUES (N'010102', N'人力资源部', N'这是公司在上海的第一分公司的下属人力资源部门,主要负责公司所需人才的招聘、管理、薪酬、岗位职责、培训、离职等相关工作');
INSERT INTO [dbo].[Depment]([ID], [DepmentName], [DepmentDesc]) VALUES (N'010201', N'行政部', N'这是公司在苏州的第二个分公司的下属行政部门,主要负责公司的行政事务(如:商务接待、会议安排、食堂管理、安保管理等相关工作)');
INSERT INTO [dbo].[Depment]([ID], [DepmentName], [DepmentDesc]) VALUES (N'010202', N'销售部', N'这是公司在苏州的第二个分公司的下属销售部门,主要负责公司产品的销售推广(如:对外推广公司产品、扩大公司产品的受众、让公司产品打开销路等相关工作)');
--创建用户信息表
CREATE TABLE [dbo].[UserInfo]([ID] [nvarchar](10) PRIMARY KEY,NOT NULL,[UserID] [nchar](18) NOT NULL,[UserName] [varchar](50) NOT NULL,[UserSex] [char](2) NOT NULL,[UserAge] [int] NOT NULL,[UserTelnumber] [nchar](11) NOT NULL,[UserHeight] [int] NULL,[UserWeight] [int] NULL,[UserAddress] [nchar](30) NULL,[DepmentId] [varchar](50) NOT NULL
);INSERT INTO [dbo].[UserInfo]([ID], [UserID], [UserName], [UserSex], [UserAge], [UserTelnumber], [UserHeight], [UserWeight], [UserAddress], [DepmentId]) VALUES (N'001', N'522020199001124561', '张三', '男', 35, N'14236598541', 168, 70, NULL, '010101');
INSERT INTO [dbo].[UserInfo]([ID], [UserID], [UserName], [UserSex], [UserAge], [UserTelnumber], [UserHeight], [UserWeight], [UserAddress], [DepmentId]) VALUES (N'002', N'522020199101124452', '李四', '男', 34, N'14238765541', 170, 72, NULL, '010101');
INSERT INTO [dbo].[UserInfo]([ID], [UserID], [UserName], [UserSex], [UserAge], [UserTelnumber], [UserHeight], [UserWeight], [UserAddress], [DepmentId]) VALUES (N'003', N'00201020000606345X', '王茜', '女', 25, N'13522687451', 166, 50, NULL, '010102');
INSERT INTO [dbo].[UserInfo]([ID], [UserID], [UserName], [UserSex], [UserAge], [UserTelnumber], [UserHeight], [UserWeight], [UserAddress], [DepmentId]) VALUES (N'004', N'00201020010507234X', '思雨', '女', 24, N'14587653241', 165, 50, NULL, '010201');
INSERT INTO [dbo].[UserInfo]([ID], [UserID], [UserName], [UserSex], [UserAge], [UserTelnumber], [UserHeight], [UserWeight], [UserAddress], [DepmentId]) VALUES (N'005', N'222021199506013756', '王五', '男', 30, N'18623597461', 169, 71, NULL, '010202');

              2.1.2、正式不用任何模式实现业务示例

1、创建用户信息对象

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading.Tasks;namespace ProxyPattern
{/// <summary>/// 用户对象/// </summary>internal class UserModel{//用户编号public string? Id { get; set; }//用户身份证编号public string? UserId { get; set; }//用户名称public string? UserName { get; set; }//用户性别public string? UserSex { get; set; }//用户年龄public string? UserAge { get; set; }//联系电话public string? UserTelnumber { get; set; }//用户身高public string? UserHeight { get; set; }//用户体重public string? UserWeight { get; set; }//用户地址public string? UserAddress { get; set; }//用户所属部门编号public string? DepmentId { get; set; }public override string ToString(){string str = $"用户的身份证编号【{UserId}】姓名【{UserName}】性别【{UserSex}】年龄【{UserAge}】" +$"联系电话【{UserTelnumber}】身高【{UserHeight}】体重【{UserWeight}】地址【{UserAddress}】所属部门编号【{DepmentId}】";return str;}}//Class_end
}

2、创建用户管理对象操作数据库中的用户信息

/***
*	Title:"WinFormClient" 项目
*		主题:通用的SQLServer数据库操作类
*	Description:
*		功能:实现数据库的基础增、删、查、改操作
*	Date:2025
*	Version:0.1版本
*	Author:Coffee
*	Modify Recoder:
*/using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;namespace ProxyPattern
{public class SqlServerHelper{private string connectionString;/// <summary>/// 数据库连接定义/// </summary>//private SqlConnection dbConnection;private SqlConnection dbConnection;/// <summary>/// SQL命令定义/// </summary>private SqlCommand dbCommand;/// <summary>/// 数据读取定义/// </summary>private SqlDataReader dataReader;/// <summary>/// 设置数据库连接字符串/// </summary>public string ConnectionString{set { connectionString = value; }}/// <summary>/// 构造函数/// </summary>/// <param name="connectionString">数据库连接字符串</param>public SqlServerHelper(string connectionString){this.connectionString = connectionString;}/// <summary>/// 执行一个查询,并返回结果集/// </summary>/// <param name="sql">要执行的查询SQL文本命令</param>/// <returns>返回查询结果集</returns>public DataTable ExecuteDataTable(string sql){return ExecuteDataTable(sql, CommandType.Text, null);}/// <summary>/// 执行一个查询,并返回查询结果/// </summary>/// <param name="sql">要执行的SQL语句</param>/// <param name="commandType">要执行的查询语句的类型,如存储过程或者SQL文本命令</param>/// <returns>返回查询结果集</returns>public DataTable ExecuteDataTable(string sql, CommandType commandType){return ExecuteDataTable(sql, commandType, null);}/// <summary>/// 执行一个查询,并返回查询结果/// </summary>/// <param name="sql">要执行的SQL语句</param>/// <param name="commandType">要执行的查询语句的类型,如存储过程或者SQL文本命令</param>/// <param name="parameters">Transact-SQL 语句或存储过程的参数数组</param>/// <returns></returns>public DataTable ExecuteDataTable(string sql, CommandType commandType, SqlParameter[] parameters){DataTable data = new DataTable();//实例化DataTable,用于装载查询结果集using (SqlConnection connection = new SqlConnection(connectionString)){using (SqlCommand command = new SqlCommand(sql, connection)){command.CommandType = commandType;//设置command的CommandType为指定的CommandType//如果同时传入了参数,则添加这些参数if (parameters != null){foreach (SqlParameter parameter in parameters){command.Parameters.Add(parameter);}}//通过包含查询SQL的SqlCommand实例来实例化SqlDataAdapterSqlDataAdapter adapter = new SqlDataAdapter(command);adapter.Fill(data);//填充DataTablecommand.Dispose();}connection.Dispose();}return data;}/// <summary>/// /// </summary>/// <param name="sql">要执行的查询SQL文本命令</param>/// <returns></returns>public SqlDataReader ExecuteReader(string sql){return ExecuteReader(sql, CommandType.Text, null);}/// <summary>/// /// </summary>/// <param name="sql">要执行的SQL语句</param>/// <param name="commandType">要执行的查询语句的类型,如存储过程或者SQL文本命令</param>/// <returns></returns>public SqlDataReader ExecuteReader(string sql, CommandType commandType){return ExecuteReader(sql, commandType, null);}/// <summary>/// /// </summary>/// <param name="sql">要执行的SQL语句</param>/// <param name="commandType">要执行的查询语句的类型,如存储过程或者SQL文本命令</param>/// <param name="parameters">Transact-SQL 语句或存储过程的参数数组</param>/// <returns></returns>public SqlDataReader ExecuteReader(string sql, CommandType commandType, SqlParameter[] parameters){SqlConnection connection = new SqlConnection(connectionString);SqlCommand command = new SqlCommand(sql, connection);//如果同时传入了参数,则添加这些参数if (parameters != null){foreach (SqlParameter parameter in parameters){command.Parameters.Add(parameter);}}//0表示永久,默认是30command.CommandTimeout = 240;connection.Open();//CommandBehavior.CloseConnection参数指示关闭Reader对象时关闭与其关联的Connection对象return command.ExecuteReader(CommandBehavior.CloseConnection);}/// <summary>/// /// </summary>/// <param name="sql">要执行的查询SQL文本命令</param>/// <returns></returns>public Object ExecuteScalar(string sql){return ExecuteScalar(sql, CommandType.Text, null);}/// <summary>/// /// </summary>/// <param name="sql">要执行的SQL语句</param>/// <param name="commandType">要执行的查询语句的类型,如存储过程或者SQL文本命令</param>/// <returns></returns>public Object ExecuteScalar(string sql, CommandType commandType){return ExecuteScalar(sql, commandType, null);}/// <summary>/// /// </summary>/// <param name="sql">要执行的SQL语句</param>/// <param name="commandType">要执行的查询语句的类型,如存储过程或者SQL文本命令</param>/// <param name="parameters">Transact-SQL 语句或存储过程的参数数组</param>/// <returns></returns>public Object ExecuteScalar(string sql, CommandType commandType, SqlParameter[] parameters){object result = null;using (SqlConnection connection = new SqlConnection(connectionString)){using (SqlCommand command = new SqlCommand(sql, connection)){command.CommandType = commandType;//设置command的CommandType为指定的CommandType//如果同时传入了参数,则添加这些参数if (parameters != null){foreach (SqlParameter parameter in parameters){command.Parameters.Add(parameter);}}//0表示永久,默认是30command.CommandTimeout = 240;connection.Open();//打开数据库连接result = command.ExecuteScalar();command.Dispose();}connection.Dispose();}return result;//返回查询结果的第一行第一列,忽略其它行和列}/// <summary>/// 对数据库执行增删改操作/// </summary>/// <param name="sql">要执行的查询SQL文本命令</param>/// <returns></returns>public int ExecuteNonQuery(string sql){return ExecuteNonQuery(sql, CommandType.Text, null);}/// <summary>/// 对数据库执行增删改操作/// </summary>/// <param name="sql">要执行的SQL语句</param>/// <param name="commandType">要执行的查询语句的类型,如存储过程或者SQL文本命令</param>/// <returns></returns>public int ExecuteNonQuery(string sql, CommandType commandType){return ExecuteNonQuery(sql, commandType, null);}/// <summary>/// 对数据库执行增删改操作/// </summary>/// <param name="sql">要执行的SQL语句</param>/// <param name="commandType">要执行的查询语句的类型,如存储过程或者SQL文本命令</param>/// <param name="parameters">Transact-SQL 语句或存储过程的参数数组</param>/// <returns></returns>public int ExecuteNonQuery(string sql, CommandType commandType, SqlParameter[] parameters){int count = 0;using (SqlConnection connection = new SqlConnection(connectionString)){using (SqlCommand command = new SqlCommand(sql, connection)){command.CommandType = commandType;//设置command的CommandType为指定的CommandType//如果同时传入了参数,则添加这些参数if (parameters != null){foreach (SqlParameter parameter in parameters){command.Parameters.Add(parameter);}}//0表示永久,默认是30command.CommandTimeout = 240;connection.Open();//打开数据库连接count = command.ExecuteNonQuery();command.Dispose();}connection.Dispose();}return count;//返回执行增删改操作之后,数据库中受影响的行数}/// <summary>/// 返回当前连接的数据库中所有由用户创建的数据库/// </summary>/// <returns></returns>public DataTable GetTables(){DataTable data = null;using (SqlConnection connection = new SqlConnection(connectionString)){connection.Open();//打开数据库连接data = connection.GetSchema("Tables");connection.Dispose();}return data;}/// <summary>/// 执行多条SQL语句,实现数据库事务。/// </summary>/// <param name="SQLStringList">多条SQL语句</param>        public int ExecuteSqlTran(List<String> SQLStringList){using (SqlConnection connection = new SqlConnection(connectionString)){connection.Open();SqlCommand cmd = new SqlCommand();cmd.Connection = connection;SqlTransaction tx = connection.BeginTransaction();cmd.Transaction = tx;try{int count = 0;for (int n = 0; n < SQLStringList.Count; n++){string strsql = SQLStringList[n];if (strsql.Trim().Length > 1){cmd.CommandText = strsql;count += cmd.ExecuteNonQuery();}}tx.Commit();return count;}catch{tx.Rollback();return 0;}finally{cmd.Dispose();connection.Dispose();}}}/// <summary>/// 执行带一个存储过程参数的的SQL语句。/// </summary>/// <param name="SQLString">SQL语句</param>/// <param name="content">参数内容,比如一个字段是格式复杂的文章,有特殊符号,可以通过这个方式添加</param>/// <returns>影响的记录数</returns>public int ExecuteSql(string SQLString, string content){using (SqlConnection connection = new SqlConnection(connectionString)){SqlCommand cmd = new SqlCommand(SQLString, connection);SqlParameter myParameter = new SqlParameter("@content", SqlDbType.NText);myParameter.Value = content;cmd.Parameters.Add(myParameter);try{connection.Open();int rows = cmd.ExecuteNonQuery();return rows;}catch (Exception e){throw e;}finally{cmd.Dispose();connection.Dispose();}}}/// <summary>/// 执行带一个存储过程参数的的SQL语句。/// </summary>/// <param name="SQLString">SQL语句</param>/// <param name="content">参数内容,比如一个字段是格式复杂的文章,有特殊符号,可以通过这个方式添加</param>/// <returns>影响的记录数</returns>public object ExecuteSqlGet(string SQLString, string content){using (SqlConnection connection = new SqlConnection(connectionString)){SqlCommand cmd = new SqlCommand(SQLString, connection);SqlParameter myParameter = new SqlParameter("@content", SqlDbType.NText);myParameter.Value = content;cmd.Parameters.Add(myParameter);try{connection.Open();object obj = cmd.ExecuteScalar();if ((Object.Equals(obj, null)) || (Object.Equals(obj, System.DBNull.Value))){return null;}else{return obj;}}catch (Exception e){throw e;}finally{cmd.Dispose();connection.Dispose();}}}/// <summary>/// 向数据库里插入图像格式的字段(和上面情况类似的另一种实例)/// </summary>/// <param name="strSQL">SQL语句</param>/// <param name="fs">图像字节,数据库的字段类型为image的情况</param>/// <returns>影响的记录数</returns>public int ExecuteSqlInsertImg(string strSQL, byte[] fs){using (SqlConnection connection = new SqlConnection(connectionString)){SqlCommand cmd = new SqlCommand(strSQL, connection);SqlParameter myParameter = new SqlParameter("@fs", SqlDbType.Image);myParameter.Value = fs;cmd.Parameters.Add(myParameter);try{connection.Open();int rows = cmd.ExecuteNonQuery();return rows;}catch (Exception e){throw e;}finally{cmd.Dispose();connection.Dispose();}}}/// <summary>/// 执行查询语句,返回DataSet/// </summary>/// <param name="SQLString">查询语句</param>/// <returns>DataSet</returns>public DataSet Query(string SQLString){using (SqlConnection connection = new SqlConnection(connectionString)){DataSet ds = new DataSet();try{connection.Open();SqlDataAdapter command = new SqlDataAdapter(SQLString, connection);command.Fill(ds, "ds");command.Dispose();}catch (Exception ex){throw new Exception(ex.Message);}finally{connection.Dispose();}return ds;}}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ProxyPattern
{/// <summary>/// 用户管理对象/// </summary>internal class UserManager{public static List<UserModel> GetUserByDepmentId(string depmentId){string conStr = $"server=.;database=Test;uid=test;pwd=123456";SqlServerHelper sqlServerHelper = new SqlServerHelper(conStr);string sql = $"select uo.* from UserInfo uo left join Depment dt on uo.depmentId=dt.ID where dt.ID like '{depmentId}%'";DataTable dt = sqlServerHelper.ExecuteDataTable(sql);使用参数内容时周围不能有特殊修符号如单引号等//SqlParameter[] sqlParameters ={//    new SqlParameter("@dd",SqlDbType.VarChar){Value=depmentId}//}//;//DataTable dt = sqlServerHelper.ExecuteDataTable(sql, System.Data.CommandType.Text, sqlParameters);if (dt.Rows.Count>0){List<UserModel> userModels = new List<UserModel>(); foreach (DataRow dr in dt.Rows){UserModel userModel = new UserModel();userModel.Id = dr["ID"].ToString();userModel.UserId = dr["UserID"].ToString();userModel.UserName = dr["UserName"].ToString();userModel.UserSex = dr["UserSex"].ToString();userModel.UserAge = dr["UserAge"].ToString();userModel.UserTelnumber = dr["UserTelnumber"].ToString();userModel.UserHeight = dr["UserHeight"].ToString();userModel.UserWeight = dr["UserWeight"].ToString();userModel.UserAddress = dr["UserAddress"].ToString();userModel.DepmentId = dr["DepmentId"].ToString();userModels.Add(userModel);}return userModels;}return null;}}//Class_end
}

3、编写客户端示例

namespace ProxyPattern
{internal class Program{static void Main(string[] args){GetDepmentUsersTest();Console.ReadLine();}/// <summary>/// 测试获取部门人员/// </summary>private static void GetDepmentUsersTest(){Console.WriteLine("---测试获取部门人员---");string depmentId = "0101";Console.WriteLine($"{depmentId} 部门的所有人员如下");List<UserModel> userModels = UserManager.GetUserByDepmentId(depmentId);foreach (UserModel userModel in userModels){Console.WriteLine(userModel.ToString());}depmentId = "0102";Console.WriteLine($"\n{depmentId} 部门的所有人员如下");List<UserModel> userModels2 = UserManager.GetUserByDepmentId(depmentId);foreach (UserModel userModel in userModels2){Console.WriteLine(userModel.ToString());}}}//Class_end
}

4、运行结果

如上所示我们已经不使用任何模式实现了获取指定部门的所有用户信息;但是当我们一次性访问的数据条数很多,且每条数据量很大的情况下,那么会十分消耗我们宝贵的内存空间;并且从客户使用的角度来说,查看用户信息具有很大的随机性,客户有可能访问每一条数据,也有可能一条都不访问;也就是说,一次性访问很多条数据,消耗了大量内存,但是很可能是浪费的,因为客户根本不会查看这么多数据;对于每条数据用户只查看姓名而已【那么,我们该怎么实现,才能既把多条用户数据的姓名展示出来,而又能节省内存空间?只有当用户想要查看指定用户更多数据的时候才显示对应用户的详细数据?】

  2.2、使用代理模式的示例

        使用代理模式就能解决如上的问题:代理模式解决的思路是:①由于客户开始访问的时候只查看用户姓名,,因此开始的时候就只从数据库中查询返回所有的用户编号与姓名数据;②当客户要查看某个用户相信信息的时候,再根据该用户编号从数据库中查询到该用户的详细数据信息;这样一来再满足用户需求的前提下,还减少了对内存的消耗,只是每次需要重新查询一下数据库【可以看做是以时间换空间的做法】。

1、定义用户数据对象接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ProxyPattern.Proxy
{/// <summary>/// 用户数据对象接口/// </summary>internal interface IUserModel{//用户编号string? Id { get; set; }//用户身份证编号public string? UserId { get; set; }//用户名称public string? UserName { get; set; }//用户性别public string? UserSex { get; set; }//用户年龄public string? UserAge { get; set; }//联系电话public string? UserTelnumber { get; set; }//用户身高public string? UserHeight { get; set; }//用户体重public string? UserWeight { get; set; }//用户地址public string? UserAddress { get; set; }//用户所属部门编号public string? DepmentId { get; set; }}//Interface_end
}

2、用户对象模型

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading.Tasks;namespace ProxyPattern.Proxy
{/// <summary>/// 用户对象/// </summary>internal class UserModel{//用户编号public string? Id { get; set; }//用户身份证编号public string? UserId { get; set; }//用户名称public string? UserName { get; set; }//用户性别public string? UserSex { get; set; }//用户年龄public string? UserAge { get; set; }//联系电话public string? UserTelnumber { get; set; }//用户身高public string? UserHeight { get; set; }//用户体重public string? UserWeight { get; set; }//用户地址public string? UserAddress { get; set; }//用户所属部门编号public string? DepmentId { get; set; }}//Class_end
}

3、创建代理对象继承接口并实现

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Text;
using System.Threading.Tasks;namespace ProxyPattern.Proxy
{internal class Proxy : IUserModel{//持有被代理的具体目标对象private UserModel userModel;//是否已经重新装载过数据标识private bool loaded = false;/// <summary>/// 构造函数/// </summary>/// <param name="userModel">被代理的具体目标对象</param>public Proxy(UserModel userModel){this.userModel = userModel;}public string? Id { get=>this.userModel.Id ; set =>this.userModel.Id=value; }public string? UserId { get => this.userModel.UserId; set => this.userModel.UserId=value; }public string? UserName { get => this.userModel.UserName; set => this.userModel.UserName=value; }public string? UserSex { get => this.userModel.UserSex; set => this.userModel.UserSex=value; }public string? UserAge { get => this.userModel.UserAge; set => this.userModel.UserAge=value; }public string? UserTelnumber { get => this.userModel.UserTelnumber; set => this.userModel.UserTelnumber=value; }public string? UserHeight { get => this.userModel.UserHeight; set => this.userModel.UserHeight=value; }public string? UserWeight { get => this.userModel.UserWeight; set => this.userModel.UserWeight=value; }public string? UserAddress { get => this.userModel.UserAddress; set => this.userModel.UserAddress=value; }public string? DepmentId {get {if (!this.loaded){//从数据库重写加载数据Reload();//重新设置加载标识为truethis.loaded = true;}return this.userModel.DepmentId;}set {this.userModel.DepmentId = value;}     }private void Reload(){Console.WriteLine($"重新查询数据库获取完整的用户数据,UserId={userModel.UserId}");string conStr = $"server=.;database=Test;uid=test;pwd=123456";SqlServerHelper sqlServerHelper = new SqlServerHelper(conStr);string sql = "select ID,UserAge,UserSex,UserTelnumber,UserHeight,UserWeight,UserAddress,DepmentId from UserInfo where UserID=@userId";//使用参数内容时周围不能有特殊修符号如单引号等SqlParameter[] sqlParameters ={new SqlParameter("@userId",SqlDbType.VarChar){Value=userModel.UserId}};DataTable dt = sqlServerHelper.ExecuteDataTable(sql, System.Data.CommandType.Text, sqlParameters);if (dt.Rows.Count > 0){foreach (DataRow dr in dt.Rows){userModel.Id = dr["ID"].ToString();userModel.UserSex = dr["UserSex"].ToString();userModel.UserAge = dr["UserAge"].ToString();userModel.UserTelnumber = dr["UserTelnumber"].ToString();userModel.UserHeight = dr["UserHeight"].ToString();userModel.UserWeight = dr["UserWeight"].ToString();userModel.UserAddress = dr["UserAddress"].ToString();userModel.DepmentId = dr["DepmentId"].ToString();}}}public override string ToString(){string str = $"完整用户信息——用户的身份证编号【{UserId}】姓名【{UserName}】性别【{UserSex}】年龄【{UserAge}】" +$"联系电话【{UserTelnumber}】身高【{UserHeight}】体重【{UserWeight}】地址【{UserAddress}】所属部门编号【{DepmentId}】";return str;}}//Class_end
}

4、创建用户管理对象

《1》此时的用户管理对象查询的时候不需要全部获取数据,只需要查询用户编号与姓名就可以了;

《2》从数据库获取到用户编号与姓名数据后也只用将这两个内容转换为用户对象的属性赋值,其他内容不用设置。

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ProxyPattern.Proxy
{/// <summary>/// 用户管理对象/// </summary>internal class UserManager{/// <summary>/// 根据部门编号来获取该部门下的所有人员/// </summary>/// <param name="depmentId">部门编号</param>/// <returns>返回该部门下的所有人员</returns>public List<IUserModel> GetUserByDepmentId(string depmentId){string conStr = $"server=.;database=Test;uid=test;pwd=123456";SqlServerHelper sqlServerHelper = new SqlServerHelper(conStr);//只需要查询UserId与UserName两个值就可以了string sql = $"select uo.UserId,uo.UserName from UserInfo uo left join Depment dt on uo.depmentId=dt.ID where dt.ID like '{depmentId}%'";DataTable dt = sqlServerHelper.ExecuteDataTable(sql);if (dt.Rows.Count>0){List<IUserModel> userModels = new List<IUserModel>();foreach (DataRow dr in dt.Rows){//这里是只创建代理对象,而不是直接创建UserModel对象Proxy proxy = new Proxy(new UserModel());proxy.UserId = dr["UserID"].ToString();proxy.UserName = dr["UserName"].ToString();userModels.Add(proxy);}return userModels;}return null;}}//Class_end
}

5、创建客户端测试

using ProxyPattern.Proxy;namespace ProxyPattern
{internal class Program{static void Main(string[] args){GetUserInfoProxyTest();Console.ReadLine();}/// <summary>/// 测试用户代理客户端/// </summary>private static void GetUserInfoProxyTest(){Proxy.UserManager userManager = new Proxy.UserManager();List<IUserModel> userModels = userManager.GetUserByDepmentId("0101");//若只是显示用户名称,则不需要重新查询数据库foreach (var item in userModels){string str = $"用户的身份证编号是【{item.UserId}】姓名是【{item.UserName}】";Console.WriteLine(str);}Console.WriteLine();//若要访问非用户身份证编号和姓名外的属性内容,那就需要重新查询数据库foreach (var item in userModels){string str = $"用户的身份证编号是【{item.UserId}】姓名是【{item.UserName}】部门是【{item.DepmentId}】";Console.WriteLine(str);Console.WriteLine(item.ToString());}}}//Class_end
}

6、运行结果

        如上的代理模式实现了开始获取所有的用户编号与用户姓名数据;只有当访问到这两个数据外的数据时才需要重新查询数据获得完整用户数据信息。但如上示例也存在一个问题,就是如果客户对每条数据都要求查看详细数据的话,那么总的查询数据库的次数会达到【1+N】次;这种代理模式最合适的场景是:大多数情况下只查看用户编号与姓名数据,只有少量情况查看个人详情数据。

 2.3、保护代理

保护代理是一种控制对原始对象访问的代理,多用于对象应该有不同的访问权限的情况。

        业务需求:现有一个订单系统业务,要求一旦订单被创建,只有订单的创建人才可以修改订单数据,其他人则不能修改。

1、创建订单接口规范行为

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ProxyPattern.ProtectProxy
{/// <summary>/// 订单对象接口/// </summary>internal interface IOrder{//获取订单订购的产品名称string GetProductName();//设置订单订购的产品名称与人员void SetProductName(string productName,string user);//获取订购订单的数量int GetOrderNumber();//设置订购订单的数量与人员void SetOrderNumber(int orderNumber,string user);//获取创建订单的人员string GetOrderUser();//设置创建订单的人员与人员void SetOrderUser(string orderUser, string user);}//Interface_end
}

2、创建订单对象

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ProxyPattern.ProtectProxy
{/// <summary>/// 订单对象/// </summary>internal class Order : IOrder{//订单订购的产品名称private string productName=string.Empty;//订单的订购数量private int orderNumber=0;//创建订单的人员private string orderUser=string.Empty;/// <summary>/// 构造函数/// </summary>/// <param name="productName">订单订购的产品名称</param>/// <param name="orderNumber">订单数量</param>/// <param name="orderUserName">创建订单的人员</param>public Order(string productName,int orderNumber,string orderUser){this.productName = productName;this.orderNumber = orderNumber;this.orderUser=orderUser;}public int GetOrderNumber(){return this.orderNumber;}public string GetOrderUser(){return this.orderUser;}public string GetProductName(){return this.productName;}public void SetOrderNumber(int orderNumber, string user){this.orderNumber=orderNumber;}public void SetOrderUser(string orderUser, string user){this.orderUser=orderUser;}public void SetProductName(string productName, string user){this.productName=productName;}}//Class_end
}

3、创建订单对象的代理并继承接口实现具体功能

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ProxyPattern.ProtectProxy
{/// <summary>/// 订单代理对象/// </summary>internal class OrderProxy : IOrder{//持有被代理的具体目标对象private Order order = null;/// <summary>/// 构造函数/// </summary>/// <param name="realSubject">被代理的具体目标对象</param>public OrderProxy(Order realSubject){this.order = realSubject;   }public int GetOrderNumber(){return this.order.GetOrderNumber();}public string GetOrderUser(){return this.order.GetOrderUser();}public string GetProductName(){return this.order.GetProductName();}public void SetOrderNumber(int orderNumber, string user){//控制访问权限,只有创建订单的人员才能够修改if (!string.IsNullOrEmpty(user) && user.Equals(this.GetOrderUser())){order.SetOrderNumber(orderNumber, user);}else{Console.WriteLine($"抱歉【{user}】,您无权修改订单中的产品数量");}}public void SetOrderUser(string orderUser, string user){//控制访问权限,只有创建订单的人员才能够修改if (!string.IsNullOrEmpty(user) && user.Equals(this.GetOrderUser())){order.SetOrderUser(orderUser, user);}else{Console.WriteLine($"抱歉【{user}】,您无权修改订单中的创建人员");}}public void SetProductName(string productName, string user){//控制访问权限,只有创建订单的人员才能够修改if (!string.IsNullOrEmpty(user) && user.Equals(this.GetOrderUser())){order.SetProductName(productName, user);}else{Console.WriteLine($"抱歉【{user}】,您无权修改订单中的产品名称");}}public override string ToString(){string str = $"订单订购的产品名称是【{GetProductName()}】数量是【{GetOrderNumber()}】创建订单的人员是【{GetOrderUser()}】";return str;}}//Class_end
}

4、客户端测试

using ProxyPattern.ProtectProxy;
using ProxyPattern.Proxy;namespace ProxyPattern
{internal class Program{static void Main(string[] args){OrderProxyTest();Console.ReadLine();}/// <summary>/// 订单代理测试/// </summary>private static void OrderProxyTest(){//张三先登录系统创建一个订单IOrder order = new OrderProxy(new Order("设计模式",666,"张三"));Console.WriteLine($"初始创建订单的信息是\n{order.ToString()}\n\n");//李四想要修改,此时应该有报错提示order.SetOrderNumber(999,"李四");Console.WriteLine($"李四尝试修改订单数量后,订单信息是\n{order.ToString()}");//创建者张三修改订单,可正常修改且不会报错order.SetOrderNumber(888,"张三");Console.WriteLine($"\n\n张三修改订单数量后,订单信息是\n{order.ToString()}");}}//Class_end
}

5、运行结果

对于代理模式某些情况下还可以使用继承的方式取代掉接口:

1、创建订单对象并将需要设置权限控制的方法设置为虚方法

using ProxyPattern.ProtectProxy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ProxyPattern.ModifyProxy
{/// <summary>/// 订单对象/// </summary>internal class Order : IOrder{//订单订购的产品名称private string productName=string.Empty;//订单的订购数量private int orderNumber=0;//创建订单的人员private string orderUser=string.Empty;/// <summary>/// 构造函数/// </summary>/// <param name="productName">订单订购的产品名称</param>/// <param name="orderNumber">订单数量</param>/// <param name="orderUserName">创建订单的人员</param>public Order(string productName,int orderNumber,string orderUser){this.productName = productName;this.orderNumber = orderNumber;this.orderUser=orderUser;}public int GetOrderNumber(){return orderNumber;}public string GetOrderUser(){return orderUser;}public string GetProductName(){return productName;}public virtual void SetOrderNumber(int orderNumber, string user){this.orderNumber=orderNumber;}public virtual void SetOrderUser(string orderUser, string user){this.orderUser=orderUser;}public virtual void SetProductName(string productName, string user){this.productName=productName;}}//Class_end
}

2、创建一个订单代理对象继承该订单对象重新虚方法

using ProxyPattern.ProtectProxy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ProxyPattern.ModifyProxy
{/// <summary>/// 订单代理对象/// </summary>internal class OrderProxy : Order{public static OrderProxy instance;//单例public OrderProxy(string productName, int orderNumber, string orderUser) : base(productName, orderNumber, orderUser){if (instance == null){instance = this;}else{Console.WriteLine($"{GetType()}/OrderProxy()函数不允许重复实例化!!!");}}public override void SetOrderNumber(int orderNumber, string user){//控制访问权限,只有创建订单的人员才能够修改if (!string.IsNullOrEmpty(user) && user.Equals(this.GetOrderUser())){base.SetOrderNumber(orderNumber, user);}else{Console.WriteLine($"抱歉【{user}】,您无权修改订单中的产品数量");}}public override void SetOrderUser(string orderUser, string user){//控制访问权限,只有创建订单的人员才能够修改if (!string.IsNullOrEmpty(user) && user.Equals(this.GetOrderUser())){base.SetOrderUser(orderUser, user);}else{Console.WriteLine($"抱歉【{user}】,您无权修改订单中的创建人员");}}public override void SetProductName(string productName, string user){//控制访问权限,只有创建订单的人员才能够修改if (!string.IsNullOrEmpty(user) && user.Equals(this.GetOrderUser())){base.SetProductName(productName, user);}else{Console.WriteLine($"抱歉【{user}】,您无权修改订单中的产品名称");}}public override string ToString(){string str = $"订单订购的产品名称是【{GetProductName()}】数量是【{GetOrderNumber()}】创建订单的人员是【{GetOrderUser()}】";return str;}}//Class_end
}

 3、客户端测试

using ProxyPattern.ProtectProxy;
using ProxyPattern.Proxy;namespace ProxyPattern
{internal class Program{static void Main(string[] args){OrderProxyTest2();Console.ReadLine();}/// <summary>/// 订单代理测试【继承方式】/// </summary>private static void OrderProxyTest2(){//张三先登录系统创建一个订单ModifyProxy.Order order = new ModifyProxy.OrderProxy("设计模式", 666, "张三");Console.WriteLine($"初始创建订单的信息是\n{order.ToString()}\n\n");//李四想要修改,此时应该有报错提示order.SetOrderNumber(999, "李四");Console.WriteLine($"李四尝试修改订单数量后,订单信息是\n{order.ToString()}");//创建者张三修改订单,可正常修改且不会报错order.SetOrderNumber(888, "张三");Console.WriteLine($"\n\n张三修改订单数量后,订单信息是\n{order.ToString()}");}}//Class_end
}

 4、运行结果

三、项目源码工程

kafeiweimei/Learning_DesignPattern: 这是一个关于C#语言编写的基础设计模式项目工程,方便学习理解常见的26种设计模式https://github.com/kafeiweimei/Learning_DesignPattern

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/80711.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云web端直播(前端部分)

阿里云&#xff1a;Web播放器快速接入_视频点播(VOD)-阿里云帮助中心 import Aliplayer from aliyun-aliplayerimport aliyun-aliplayer/build/skins/default/aliplayer-min.css<div id"J_prismPlayer" style"width: 300px; height: 300px;" />var …

深入解析OrientDB:多模型数据库的技术优势与实际应用

OrientDB 是一款开源的多模型 NoSQL 数据库&#xff0c;融合了文档数据库、图数据库和对象数据库的特性。它不仅支持灵活的数据建模&#xff0c;还提供了高性能的查询能力&#xff0c;适用于社交网络、物联网、内容管理等场景。本文详细探讨 OrientDB 的核心特性、应用场景&…

STM32控制电机

初始化时钟&#xff1a;在 STM32 的程序中&#xff0c;初始化系统时钟&#xff0c;一般会使用 RCC&#xff08;Reset and Clock Control&#xff09;相关函数来配置时钟。例如&#xff0c;对于 STM32F103 系列&#xff0c;可能会使用 RCC_APB2PeriphClockCmd 函数来使能 GPIO 和…

(05)数字化转型之生产制造:从通常的离散制造到柔性化生产的全景指南

当今制造业正经历着前所未有的数字化变革&#xff0c;从传统的离散制造到流程制造&#xff0c;再到新兴的项目制造和柔性制造&#xff0c;各种生产模式都在加速向智能化方向演进。本文将系统性地介绍制造业生产管理的完整体系&#xff0c;为企业数字化转型提供全面的方法论和实…

龙虎榜——20250520

上证指数今天缩量向上&#xff0c;个股涨多跌少&#xff0c;大盘股和小盘股总体表现都还可以。 深证同样缩量上涨&#xff0c;向上补缺口的概率增大。 2025年5月20日龙虎榜行业方向分析 宠物经济&#xff08;消费升级政策催化&#xff09; • 代表标的&#xff1a;天元宠物、…

CVE-2022-22978源码分析与漏洞复现

漏洞概述 CVE-2022-22978 是 Spring Security 框架中的一个高危认证绕过漏洞&#xff0c;影响版本包括 Spring Security 5.5.x < 5.5.7、5.6.x < 5.6.4 及更早的不受支持版本。攻击者可通过构造包含换行符&#xff08;如 %0a&#xff09;的 URL 路径&#xff0c;绕过正则…

PostGIS实现栅格数据入库【raster2pgsql】

raster2pgsql使用与最佳实践 一、工具概述 raster2pgsql是PostGIS提供的命令行工具,用于将GDAL支持的栅格格式(如GeoTIFF、JPEG、PNG等)导入PostgreSQL数据库,支持批量加载、分块切片、创建空间索引及金字塔概览,是栅格数据入库的核心工具。 二、核心功能与典型用法 1…

Redis企业级开发实战:核心应用场景与最佳实践

引言 Redis&#xff08;Remote Dictionary Server&#xff09;作为一款高性能的内存数据库&#xff0c;在企业级开发中扮演着至关重要的角色。无论是缓存加速、分布式锁、实时统计&#xff0c;还是消息队列&#xff0c;Redis都能以极低的延迟和极高的吞吐量满足业务需求。本文…

深入解析Spring Boot与Spring Cloud在微服务架构中的实践

深入解析Spring Boot与Spring Cloud在微服务架构中的实践 引言 随着云计算和分布式系统的快速发展&#xff0c;微服务架构已成为现代软件开发的主流模式。Spring Boot和Spring Cloud作为Java生态中微服务开发的核心框架&#xff0c;为开发者提供了强大的工具和组件&#xff0…

AI量化交易是什么?它是如何重塑金融世界的?

第一章&#xff1a;证券交易的进化之路 1.1 从喊价到代码&#xff1a;交易方式的革命性转变 在电子交易普及之前&#xff0c;证券交易依赖于交易所内的公开喊价系统。交易员通过手势、喊话甚至身体语言传递买卖信息&#xff0c;这种模式虽然直观&#xff0c;但效率低下且容易…

芯驰科技与安波福联合举办技术研讨会,深化智能汽车领域合作交流

5月15日&#xff0c;芯驰科技与全球移动出行技术解决方案供应商安波福&#xff08;Aptiv&#xff09;在上海联合举办以“芯智融合&#xff0c;共赢未来”为主题的技术研讨会。会上&#xff0c;双方聚焦智能座舱与智能车控的发展趋势&#xff0c;展开深入交流与探讨&#xff0c;…

大数据Spark(五十九):Standalone集群部署

文章目录 Standalone集群部署 一、节点划分 二、搭建Standalone集群 1、将下载好的Spark安装包上传解压 2、配饰spark-env.sh 3、配置workers 4、将配置好的安装包发送到node2、node3节点上 5、启动Standalone集群 三、提交任务测试 Standalone集群部署 Standalone 模…

Feign异步模式丢失上下文问题

Feign异步模式丢失上下文问题 问题描述 当我们使用异步对我们代码进行操作优化时&#xff0c;代码中使用了RequestContextHolder去获取上下文的数据&#xff0c;当我们执行原来可以执行的业务时发现报了空指针异常或数据为空&#xff0c;这是为什么呢&#xff1f; 原理解释 …

JavaScript作用域和作用域链

在JavaScript中&#xff0c;作用域和作用域链是理解代码执行和变量访问的关键概念。它们决定了变量和函数在代码中的可见性和生命周期。 一、作用域&#xff08;Scope&#xff09; &#xff08;一&#xff09;什么是作用域&#xff1f; 作用域是在运行时代码中的某些特定部分…

人工智能的“歧视”:“她数据”在算法运行中隐形

纵观人类的发展史&#xff0c;每一次科技进步都将对性别平等产生深刻影响。尤其是当下&#xff0c;人们对于借助人工智能技术快速发展来弥合性别不平等寄予厚望。 但很多人没想过&#xff0c;人工智能技术本身是客观中立、不存在“算法歧视”“性别偏见的吗&#xff1f; 弗吉…

设备全生命周期管理:从采购到报废的数字化闭环方案

在当今数字化时代&#xff0c;企业对设备的管理已不再局限于简单的维护与修理&#xff0c;而是追求从采购到报废的全生命周期数字化闭环管理。易点易动设备管理系统&#xff0c;正是这一趋势下的佼佼者&#xff0c;它为企业提供了一套高效便捷的设备管理解决方案。 采购阶段&a…

React中useState中更新是同步的还是异步的?

文章目录 前言一、useState 的基本用法二、useState 的更新机制1. 内部状态管理2. 状态初始化3. 状态更新 三、useState 的更新频率与异步行为1. 异步更新与批量更新2. 为什么需要异步更新&#xff1f; 四、如何正确处理 useState 的更新1. 使用回调函数形式的更新2. 理解异步更…

FEKO许可证与其他电磁仿真软件的比较

在电磁仿真领域&#xff0c;众多软件工具竞相争艳&#xff0c;而FEKO软件及其许可证制度在其中独树一帜。本文将对比FEKO许可证与其他电磁仿真软件&#xff0c;突出FEKO在许可证方面的卓越性能与独特优势&#xff0c;帮助您做出明智的选择。 一、许可证成本与价值比较 相较于其…

绿色云计算:数字化转型与可持续发展的完美融合

目录 引言 绿色云计算的概念与定义 云计算的环境影响与绿色云计算的重要性 绿色云计算的技术实践与策略 绿色云计算的案例研究与最佳实践 绿色云计算的挑战与限制 绿色云计算的未来趋势与预测 结论与展望 引言 随着云计算技术的迅猛发展和广泛应用&#xff0c;其环境影…

在innovus中如何设置让信号线打上双孔

知识星球【芯冰乐】入口 为了让设计的芯片良率能得到显著提升,一般在绕线资源允许的情况下,我们会在尽可能多的signal线上打上双孔,然而在进行某个项目的时候,小编惊讶的发现,在数字的layout上一个双孔都没出现,这是为什么呢?今天就让小编分享一下这次新奇的发现; 经…