写点什么

我们在 SqlSugar 开发框架中,用到的一些设计模式

  • 2024-02-22
    福建
  • 本文字数:4344 字

    阅读完需:约 14 分钟

我们在《SqlSugar开发框架》中,有时候都会根据一些需要引入一些设计模式,主要的目的是为了解决问题提供便利和代码重用等目的。而不是为用而用,我们的目的是解决问题,并在一定的场景下以水到渠成的方式处理。不过引入任何的设计模式,都会增加一定的学习难度,除非是自己本身领会比较好了,就会显得轻松一些。本篇随笔抽取一些应用场景来介绍相关设计模式,有些地方如列举有一定的偏颇之处,还请告知以便斧正。


1、Winform 的本地访问和基于 Web API 的访问方式


Winform 中的界面展示,以及数据处理,都需要具体实现的支撑,由于本身 IOC 控制反转的接口设计,我们对具体数据的访问,也是基于特定的接口层进行调用的,具体的实现,则是在程序启动的时候,注入对应的接口实现即可。



以上的业务接口层和数据处理层分开,数据处理层会根据配置信息采用不同的数据库实现方式,如可能是基于 SQLServer、Oracle、Mysql、SQLite、PostgreSQL 等不同的数据库,这种方式是软件开发中常见的一种原则——接口与实现分离的原则,也称为接口隔离原则(Interface Segregation Principle,ISP)。


接口隔离原则是面向对象设计中的一项原则,它主张一个类不应该强迫其用户依赖于它们不需要的方法。简单来说,就是应该将一个接口拆分为多个较小的接口,这样客户端只需要知道与其相关的接口即可,而不需要了解其他接口的细节。


接口与实现分离的设计模式有很多,常见的包括:


  1. 工厂模式(Factory Pattern):工厂模式通过定义一个创建对象的接口,但是让子类决定实例化哪个类。这样,一个类的实例化延迟到其子类。

  2. 抽象工厂模式(Abstract Factory Pattern):抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

  3. 适配器模式(Adapter Pattern):适配器模式允许将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。

  4. 代理模式(Proxy Pattern):代理模式为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用。


在我们有些情况下,不是直接访问具体的本地数据库,有可能是间接调用 Web API 的接口服务的,而我们使用的时候,为了方便,可能需要进行一定的封装处理,如下图示。


如果我们这里增加一个对 Web API 的调用,那么在这里注册的时候,切换向 Web API 代理的注册接口就可以,如下图所示。



因此原来的 Winform 界面上的调用代码,不需要任何变化,只需要注入不同的接口实现,就能获得不同的方式:普通访问数据库方式,还是分布式获取服务 WebAPI 的处理方式。


通过切换开关变量的方式,客户可以非常方便的自由切换不同的数据访问方式。数据提供服务,可以是直接访问数据库的方式,也可以是远端的 Web API 服务方式,从而实现更加广泛的业务需求。



2、基于接口和基类的继承方式简化重复代码


基于接口和基类的继承方式不是特定的设计模式,而是一种面向对象设计的基本原则和实践。


这种方式通常用于实现多态性(Polymorphism)、抽象化(Abstraction)和代码重用。在面向对象的编程语言中,通过定义接口(Interface)和基类(Base Class),可以实现一种规范化的编程模式,让代码更加灵活、可扩展和易于维护。


这种方式的优点包括:


  1. 多态性: 通过接口和基类,不同的子类可以实现相同的接口或继承相同的基类,从而可以以统一的方式对待不同的对象。

  2. 代码重用: 可以将通用的功能和行为定义在接口或基类中,从而使得子类可以重用这些功能和行为,减少重复代码的编写。

  3. 解耦和模块化: 接口和基类可以帮助解耦不同模块之间的依赖关系,提高代码的模块化程度,使得系统更易于理解和维护。


虽然基于接口和基类的继承方式不是一个独立的设计模式,但它是很多设计模式的基础,比如工厂模式、策略模式、模板方法模式等都会使用到接口和基类的继承方式。


在随笔《基于SqlSugar的开发框架循序渐进介绍(5)-- 在服务层使用接口注入方式实现IOC控制反转 》我们介绍过具体实现类的继承关系,一般都是构建相应的基类和接口,然后才是具体的业务实现和接口,这样处理可以重用基类的很多接口,提高代码的重用效率。



而对应 Web API 的代理调用类,那么为了极大的重用常规的接口处理,我们需要类似的继承关系。



而在 Web API 的服务端中,我们为了重用,也是以基类和接口的方式来统一处理相关的逻辑。如我们根据项目的需要,定义了一些 Web API 控制器的基类,用于实现不同的功能。



同样,BS 的前端和移动端,我们根据框架后端的接口进行前端 JS 端的类的封装处理,引入了 ES6 类的概念实现业务基类接口的统一封装,简化代码。


权限模块我们涉及到的用户管理、机构管理、角色管理、菜单管理、功能管理、操作日志、登录日志等业务类,那么这些类继承 BaseApi,就会具有相关的接口了,如下所示继承关系。



按照这个思路,我们在 BaseApi 的 ES6 类里面定义了对应 Web API 基类里面的操作方法,如下所示。



 这样,我们在创建一个业务类的时候,如果没有特殊的自定义接口,只需要继承基类 BaseApi 即可具有所有的常规基类方法了。

 

3、简单工厂设计模式


简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,它提供了一个统一的接口来实例化一组相关或相似的对象,而无需暴露对象的创建逻辑给客户端。


简单工厂模式包含以下几个角色:


  1. 工厂(Factory): 负责创建具体产品的类。它通常包含一个或多个静态方法,根据客户端的参数来决定创建并返回哪种具体产品的实例。

  2. 产品接口(Product Interface): 声明了具体产品类的共同接口,客户端通过这个接口与具体产品类进行交互。

  3. 具体产品(Concrete Products): 实现了产品接口的具体类,是工厂创建的目标对象。


简单工厂模式的核心思想是将对象的创建和使用进行分离,客户端只需要知道使用工厂提供的接口来获取所需的产品,而无需了解产品是如何被创建的。这样的设计使得系统更加灵活,可以随时更改具体产品的创建方式而不影响客户端的代码。


在基于《SqlSugar开发框架》中,我们设计了一些系统服务层的基类,在基类中会有很多涉及到相关的数据处理操作的,如果需要跟踪具体是那个用户进行操作的,那么就需要获得当前用户的身份信息,包括在 Web API 的控制器中也是一样,需要获得对应的用户身份信息,才能进行相关的身份鉴别和处理操作。


为了方便获取用户身份的信息,我们定义一个接口 IApiUserSession 如下所示。


/// <summary>/// API接口授权获取的用户身份信息-接口/// </summary>public interface IApiUserSession{    /// <summary>    /// 用户登录来源渠道,0为网站,1为微信,2为安卓APP,3为苹果APP       /// </summary>    string Channel { get; }
/// <summary> /// 用户ID /// </summary> int? Id { get; }
/// <summary> /// 用户名称 /// </summary> string Name { get; }
/// <summary> /// 用户邮箱(可选) /// </summary> string Email { get; }
/// <summary> /// 用户手机(可选) /// </summary> string Mobile { get; }
/// <summary> /// 用户全名称(可选) /// </summary> string FullName { get; }
/// <summary> /// 性别(可选) /// </summary> string Gender { get; }
/// <summary> /// 所属公司ID(可选) /// </summary> string Company_ID { get; }
/// <summary> /// 所属公司名称(可选) /// </summary> string CompanyName { get; }
/// <summary> /// 所属部门ID(可选) /// </summary> string Dept_ID { get; }
/// <summary> /// 所属部门名称(可选) /// </summary> string DeptName { get; }
/// <summary> /// 把用户信息设置到缓存中去 /// </summary> /// <param name="info">用户登陆信息</param> /// <param name="channel">默认为空,用户登录来源渠道:0为网站,1为微信,2为安卓APP,3为苹果APP </param> void SetInfo(LoginUserInfo info, string channel = null);}
复制代码


IApiUserSession 的一个空白接口定义,它需要依赖于具体的接口实现,我们具体会使用基于 Principal 或者缓存方式实现记录用户身份的信息实现,如下是它们的类关系。



在客户端和 Web API 的交换信息过程中,通过 JWT 的令牌方式,可以携带一些相关的用户身份信息。


在登录授权的这个时候,控制器会把相关的 Claim 信息写入到 token 中的,我们在客户端发起对控制器方法的调用的时候,这些身份信息会转换成对象信息。


在监视窗口中查看 IApiUserSession 对象,可以查看到对应的信息。



简单工厂的设计模式,是比较经常用到的一种设计模式,如我在随笔《基于SqlSugar的开发框架循序渐进介绍(26)-- 实现本地上传、FTP上传、阿里云OSS上传三者合一处理》中介绍到,根据配置信息来确定上传的处理路径选择,就是一种简单的工厂设计模式。



文件上传处理应该由程序进行配置,决定使用那种方式,那么这里面我们为了弹性化处理, 在文件上传模块中采用选项模式【Options】处理常规上传和 FTP 文件上传的配置参数信息。


微软引入选项模式,它是用于配置框架服务使用的设置. 选项模式由Microsoft.Extensions.OptionsNuGet 包实现,除了 ASP.NET Core 应用,它还适用于任何类型的应用程序,如果需要了解,微软的文档详细解释了选项模式。


选项模式的限制之一是你只能解析(注入) IOptions <MyOptions> 并在依赖注入配置完成(即所有模块的ConfigureServices方法完成)后获取选项值。如果你正在开发一个模块,可能需要让开发者能够设置一些选项,并在依赖注入注册阶段使用这些选项. 你可能需要根据选项值配置其他服务或更改依赖注入的注册代码。IOptions<>是单例,因此一旦生成了,除非通过代码的方式更改,它的值是不会更新的。

 

在本地文件处理过程中,如果是 Web API 方式调用服务层,那么就在 Web API 所在的文件系统中,如果是 Winform 界面直接调用服务层,那么就是在当前系统中处理文件,这种方式可以有效的管理我们的文件信息。


在 FTP 文件处理过程中,则是根据选项参数的信息,调用 FluentFTP 类库进行文件的上传操作。


在 OSS 对象存储处理过程中,我们一般基于阿里云、腾讯云等这些云服务 OSS 的处理方式,一般它们会提供相应开发语言的 SDK,我们引用并进行整合即可。


对于阿里云的 OSS 来说,对应的 AccesKey 和 SecretKey 自己可以通过查看账户的信息可以获取到。



以上就是一些场景的应用设计模式,当前开发框架里面,有很多其他的场景也同样会引入一些不同的处理方法,不过主旨都是希望采用较小的代价和难度,来解决复杂的问题的思路。


文章转载自:伍华聪

原文链接:https://www.cnblogs.com/wuhuacong/p/18025300

体验地址:http://www.jnpfsoft.com/?from=001

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
我们在SqlSugar开发框架中,用到的一些设计模式_数据库_不在线第一只蜗牛_InfoQ写作社区