谈谈到底是否要坚持Model和Service分离的模式?

本文讨论的内容仅针对小型项目或个人独立开发的项目,对于大型及团队合作的项目恐怕不适合。

因接触到的项目多数都是内部使用,而且所有项目几乎都是单兵作战(从最初的项目需求分析到最终的上线验收),所以也形成了自己的一个模式。

看过JFinal-demo代码的朋友都知道,注释一栏中非常醒目的提示SQL语句的存放规则,建议都在Service中进行管理,或者放到SQL的模板中。

在实际的开发过程中发现(并非只在JFinal的环境),Model+Service+Controller的模式,可以缩略为Model+Controller,本人把Service直接融入到Model中。在使用JFinal时,也并没采用其ActiveRecord中的Model,而是完全采用其Db+Record的模式(个人认为灵活性更强)。

或许会有疑问,为什么把Service融入到Model中,恐怕不太符合规范,接下来就解释一下原因(基于一个房地产项目)。

首先,我们会遇到这样一种情况,比如某个地产项目,大体结构包括了项目、预销售许可证、楼栋、房屋,依次都是1:N的关系。那么在加载项目时会有一个已获批许可证数量的属性,以及楼栋中会有一个房屋数量的属性,并且房屋中有部分属性是通过其它的数据表中进行计算后得到的。相当于在加载某个对象时仅通过一个SELCET从对应的数据表中去取数据是满足不了的,必要时需要使用多条SELECT从若干个其它数据表中去取值并计算后才可得到结果。

针对上面的问题,我的解决思路是在Model时就一次性把需要的属性加载完成,示例代码如下

public Project{

    private int PJID;
    private String PJName;
    private int PJType;        //项目类型(这里采用数据字典形式)
    private String PJLocation;
    
    private int CertCount;     //获批许可证数量
    private int HouseCount;    //当前项目下所有获批的房屋总数
    //这里是其它类似的需要通过计算其它数据表才能得出的属性
    //因为多数情况下不能改动数据库的任何参数,所以增加自定义函数或存储过程的方法行不通。
    
    public Project(){}
    public Project(int id){
        Record rs = Db.findFirst("SELECT * FROM Project WHERE ID="+id);
        if(rs!=null){
            this.PJID = rs.getInt("PJID");
            this.PJName = rs.getStr("PJName");
            this.PJLocation = rs.getStr("PJLocation");
        }
        
        rs = Db.findFirst("SELECT COUNT(*) AS HouseCount FROM House WHERE PJID="+id);
        if(rs!=null){
            this.HouseCount = rs.getInt("HouseCount");
        }
    }
    
    
    public Page getPageList(){
        //这里是用于分页的方法
    }
    
    public List<> getList(Project project){
        //这里是根据实际需求单独的List
    }
    
    
    
    //这里是geter和seter方法,但有一个比较特殊的地方,因为数据字典相对比较固定,字典的对应信息以XML形式存储,
    //这样可以避免频繁的数据库读取,同时又支持热编辑。
   public getPJType(){
       return this.PJType;
   }
   public getPJTypeName(){
       return ComKits("PJTYPE", this.PJType).getValue();
       //这里是通过一个自定义的方法把数据字典对应的文字描述加载出来,比如数据库中PJType的值为1,这里按字典值就可以显示为“纯住宅”类似的值。
       //在View时只要通过如${project.PJTypeName}这样的形式即可显示到页面。
   }
    
    
}


以上代码都归在了Model中,这样在Controller中调用,效果与Service其实并没什么太大的区别。好处是,Model中任何属性都可完全掌控自如,缺点是自己动手写的代码量会大幅增多。

因为实际开发中对JFinal自动生成的Model改动还是比较大的,故干脆就改用了上面这种模式,结合IDE工具的部分自动化代码,工作量差异并不大。说实话,形成了固有的模式,不论代码量是多是少,反倒是上手最快的。

本人搞编程算是半路出道,JFinal算是自学的最正规的一个框架,并已经运用到了实践中,原先除了一个Struts几乎不用什么框架,感觉框架就是累赘,很多时候都是被牵着鼻子走,而且如今的框架都是越来越庞大,开发阶段经常出现依赖包的异常排查。只在遇到JFinal后,才发现jar包的清爽真的不是一丁半点,至于本人上述的这种模式,是否合理,或存在哪些不足,还望各位看过后留下些自己的看法和指教。

评论区

JFinal

2019-06-19 22:55

无论多小的项目都要有业务层

首先添加一个业务层是顺手的事情,并没有多少工作量,因为 jfinal 的业务层在不需要抽象的时候,不建议创建接口抽象类之类的东东,十分轻量级

其次,业务层在添加之初你可能并没有感到什么好处,甚至会感到工作量反而增加,但是随着开发的推进,你会发现后续的开发工作可以不断调用以前开发好的业务层的 API 来组合式实现功能。从而大大减少工作量,提升开发效率

最后,数据结构是应用的底层核心,业务层是应用的上层核心,而应用其它的部分全都只是辅助作用,包括 model 也只是起一个承载数据的作用。所以当业务发生变化时,业务层的存在可以提升可维护性

JFinal

2019-06-19 22:55

建议加入俱乐部,获取本社区源码,这里头的代码是最佳实践。你会发现有了业务层,其它业务之间是穿插调用的,逻辑十分清晰

没有业务层是做不到这一点的

山东小木

2019-06-20 00:57

业务层是必须的 而且Model有PUT方法 有的场景查询不需要额外带着子表 有的需要 这些都可以在service里处理,需要的就putItems 不需要就不调用 这样 灵活自如 而且有些数据需要fastJson去toJson的时候,但是不需要子表数据 可是fastJson会默认调用get方法 使用按需put的方式 挺好的

DreamPeter

2019-06-20 05:55

@JFinal @山东小木 一并感谢两位的指教,尤其是小木提到的带着子表的情况,确实也是采用这种方式比较头疼的问题,有时一个分页列表可能只需要几个核心的属性显示即可,但加载Model时确实全数据读取的,资源很多情况下被消耗在了附属属性的统计计算中。计算机并非本人专业,之前是爱好每个章法勉强算是能理解,目前需要上正轨,单位内部很多老系统说实话都还不如培训班学员的质量,现在要接手,领导给予了充分的权限,只要不影响业务,哪怕是全部推翻重建,也会给予最大的支持。

JFinal

2019-06-20 10:29

@DreamPeter 以你这种钻研探索的精神,提升会很快,很快能掌握最佳实践的用法

快乐的蹦豆子

2019-06-20 12:31

service划分的越细致,以后维护越轻松

nwangwei

2019-06-20 22:09

model不用动,都是自动生成的就行了。

所以你的那些代码写在Service层就行了,可能Service类数量还更少吧。

热门分享

扫码入社