抛开系统架构不说,基础的代码架构是一定要掌握的,好的代码架构是程序可维护的基础。
有可能你上一个月写的代码这个月就看不懂,不知道当时怎么写的,因此我记录一下最近学习的代码架构。
关于boot函数的编写。
先来看分层架构。
参考 《软件体系结构与设计模式》 第 159 页 图 7-3
优点:
1、分层结构将应用系统正交地划分为若干层,每一层只解决问题的一部分,通过各层的协作提供整体解决方案。大的问题被分解为一系列相对独立的子问题,局部化在每一层中,这样就有效的降低了单个问题的规模和复杂度,实现了复杂系统的第一步也是最为关键的一步分解。
2、分层结构具有良好的可扩展性,为应用系统的演化增长提供了一个灵活的框架,具有良好的可扩展性。增加新的功能时,无须对现有的代码做修改,业务逻辑可以得到最大限度的重用。同时,层与层之间可以方便地插入新的层来扩展应用。
3、分层架构易于维护。在对系统进行分解后,不同的功能被封装在不同的层中,层与层之间的耦合显著降低。因此在修改某个层的代码时,只要不涉及层与层之间的接口,就不会对其他层造成严重影响。
缺点:
1、不容易划分层次。
2、找不到合适的、正确的层次抽象方法。
比起缺点优点属实有点多。
下面是一个分层的实例:
逻辑架构
用户接口层 apis
- http server
- PRC 框架
应用层 service 接口
- 定义软件要完成的所有任务
- 业务流程控制逻辑
- 各种领域模型相互协作
- 事务日志安全
- 通常指service
领域层
- 表达业务概念,状态信息,规则
- 业务核心层
- 主要包含,实体,值对象 ,领域对象
基础设施层
- 提供统一的技术能力
- 应用层提供消息,领域层提供持久化机制,用户界面提供组件
- 统一算法
- 各层之间的通信
- 和其他服务器交互
物理架构
- PO 持续化对象
- DAO 操作数据库
- VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
- DTO(Data Transfer Object):数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。
- DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。
- PO(Persistent Object):持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。
现在基本了解了什么是代码架构。下面说一下boot函数的编程思想。我用伪代码实例一下
1、需要一个boot类
class bootApplication { 属性; //... 构造函数() pubilc Start(){ //1.初始化starter init() //2.安装starter setup() //3.启动starter start()
//... //等待其余线程结束 Wait() } private init(){} private setup() private start()
private Wait()
}
2、需要一个基础starter接口,这也是每个子系统的生命周期。
//资源启动器,每个应用少不了依赖其他资源,比如数据库,缓存,消息中间件等等服务
//启动器实现类,不需要实现所有方法,只需要实现对应的阶段方法即可,可以嵌入@BaseStarter
//通过实现资源启动器接口和资源启动注册器,友好的管理这些资源的初始化、安装、启动和停止。
//Starter对象注册器,所有需要在系统启动时需要实例化和运行的逻辑,都可以实现此接口
//注意只有Start方法才能被阻塞,如果是阻塞Start(),同时StartBlocking()要返回true
type Starter interface { //资源初始化和,通常把一些准备资源放在这里运行 Init(StarterContext) //资源的安装,所有启动需要的具备条件,使得资源达到可以启动的就备状态 Setup(StarterContext) //启动资源,达到可以使用的状态 Start(StarterContext) //说明该资源启动器开始启动服务时,是否会阻塞 //如果存在多个阻塞启动器时,只有最后一个阻塞,之前的会通过goroutine来异步启动 //所以,需要规划好启动器注册顺序 StartBlocking() bool //资源停止: // 通常在启动时遇到异常时或者启用远程管理时,用于释放资源和终止资源的使用, // 通常要优雅的释放,等待正在进行的任务继续,但不再接受新的任务 Stop(StarterContext) PriorityGroup() PriorityGroup Priority() int }
3、一个注册器函数和一个容器
// 接口类型
starterRegister [] starter
// 把接口添加到列表数组里
Register( intrerface starter ){
append(starterRegister , starter)
}
4、需要实现接口的类。然后调用注册函数,添加到starterRegister 。
然后问哦们回到 boot函数的编写
例如initi()函数。
遍历 starterRegister 执行每一个实现结构的类的init函数。
func init(){ for starterRegister { starterRegister[i].init() } }
剩下的 start().... 都一样了。
然后这是我写的Golang代码。其实吧啥都不重要,好看最重要。这就是整个项目,按着 Ctrl 点就能找到每个结构体的在哪。也好修改。