当前位置: 首页 > news >正文

转:Discuz!NT前台模型架构(MVC)

来自:http://www.cnblogs.com/daizhj/archive/2008/01/02/1023366.html

通过前几个月的“外围清剿”,我介绍了一些在项目中边边角角的类和项目。当然这种介绍只是前期热身准备。因为从这篇文章开始,本人将跟大家聊一聊关于这个产品架构上面的东西,以备大家对这个产品有一个总体上的认识,从而为后面的文章做一下铺垫。费话少说,开始正文吧:)

   首先请大家看一下官方提供的“前台页面层次图”如下: http://nt.discuz.net/doc/Default.aspx?cid=4

  

   当然这张图是简化了许多,但相信看过我们代码的朋友应该很容易明白,我们之前开源的代码就是按这种架构模式
开发的。因为园子里有些朋友可能是最近几个月才开始关注我们的这个产品,所以这里不妨也将我关于上面这张图的理
解一并发上来,请大家留意下面这张图,这是我个人对目前源码(dll)功能划分在MVC模式下的对应位置: 


 

    其实这只是一张缩略图。只是想让大家心里先有个数。而图中的discuz.config.dll(配置项),
discuz.data.dll(数据访问项)以及discuz.aggragation.dll(论坛聚合项)是在以前做过介绍的,详情见链接。
这里要重点说明的是discuz.forum.dll,因为它起的作用是承上启下,一方面它要实现做DTO (Data Transfer 
Object,数据转换对象), 另一方向也是真真正正的核心功能区(如积分,在线,用户,发贴,短消息,广告,公
告等)。所以如果要进行二次开发的话,了解它里面的代码越多越好。当然这一层我们是不建议开发人员做太大修
改的,因为我们为了方便调用和新功能的开发,做了很多的封装,就目前而言,完全可以在不修改代码的前提下实
现用户(注册,登陆,获取相关信息)整合和一些简单的二次开发。

    详见官访说明: http://nt.discuz.net/showtopic-36265.html ,

    文档:http://nt.discuz.net/download/doc/dnt_2_userapidoc.zip,

    如下图:

   

   需要大家投入更多注意力的应该是前台的aspx.cs页面。因为如果进行二次开发,以此为切入点是一个“投入少见效
快”的捷径。而如何做这方面的整合大家可以看一下官方文档。当然如果大家认为有必要,我也会在今后的文章中侧重聊
一聊这方面的话题。

   另一个discuz.entity.dll其实只是一个实体类项目,当然它是非常干净的那种。甚至大部分构造函数都是以系统默
认方式提供的。因为只是用于DTO这样的数据对象绑定封装,这样做的好处相信大家也会猜出来。除了代码生成方便,便于
分布式系统下进行传输(只有对象数据),当然还有最重要的就是让开发人员不用过多关注数据对象上的操作(也就是逻辑)
。当然有人会说这样做会导致模型没有了逻辑,甚至连最基本的操作(CRUD:Create(创建)、Read(读取)、Update(更新)
和Delete(删除))都被拿走了。凭心而论(虽然我是个充血模型的fans,大家对域模型和UML感兴趣,可以看一下我的
ICONIX系列文章),但开发的现实告诉我,如果用充血模型开发,最糟的情况可能会是为了什么样的逻辑应该放在域对象
中,什么样的逻辑应该放在Business Logic中,以及对象之间的关系(如继承,包含等)和位置摆放(在那个名空间和DLL
中)就要争论半天,对于我们这个产品而言实在是得不尝失(时间紧,任务重呀)。

    而我们这样架构的好处是:

    1.各层单向依赖,结构清楚,易于实现和维护(可以看一下各项目相互的关系) 
    2.设计简单易行(走群众路线,降低开发门槛),底层架构相对稳定 
    ......

    相应的,我们产品“贫血模型”下的架构,如下图所示: 

   

   通过采用以数据(表)结构为导向(discuz.forum中的类基本上是按数据库中的表名进行命名)。这样即使是新来的
同事也能够很快理解并进行开发。因为架构很简单,只要将操作某个表的函数方法放在相应的类中即可。虽然将来可能会
导致类里的方法数量增加过快,业务逻辑趋于复杂,但完全可以通过重构(Refactor)方式来解决这个问题。

   相信看到这里,很多人又要开始评头论足了! 请相信,我并不想将大家再次拖到几个月前那场关于域模型和OO的“讨论
泥潭”中去,因为讨论到最后也不可能说出个所以然了。我也不想再用本文去“煽风点火”,因为这并不是我写本文的最终
目的。因为关于“贫血”还是“充血”一直都是一个让人头痛的问题(就像现实和理想的差距)。

   聊了一些题外话,下面言归正传,再看几个园子里朋友反映的问题.

   请大家看一下Discuz.Data项目下的DbProvider文件夹里的IDataProvider(discuz.data.IDataProvider)接口,
这个接口本身定义了将近900个接口函数,而这些接口函数分别在Discuz.Data.SqlServer, Discuz.Data.MySql, 
Discuz.Data.Access这几个项目的DataProvider类中加以实现,其中基本上都是SQL语句和存储过程调用(SQLSERVER)。
这些函数是针对整个产品与数据库进行数据访问才实现的。当然如果以后需要支持别的类型数据库时,还会加入如Discuz.
Data.(数据库名称)(如:Oracle等)这样的项目。而总体上这样设计的好处就是将SQL语句统一进行管理,便于维护和扩
展。同时也使访问数据库的写法趋于规范,避免SQL语句像“野草”一样在产品中“四处横行”:)

    当然有些朋友认为,将所有接口函数放在一个接口类中加以声明显得过于臃肿。其实这个问题之前我也想到过,而我的
意见是将这个函数按功能(论坛空间相册聚合等)分别定义形如:    

 1  interface ISpaceDataProvider
 2    {
 3          .//空间数据访问操作函数
 4    }

 5
 6    interface IForumDataProvider
 7    {
 8          .//论坛数据访问操作函数
 9    }

10
11    interface IAlbumDataProvider
12    {
13          .//相册数据访问操作函数
14    }

15
16    .
17
18

    而最后的IDataProvider的定义就会是:

1     public interface IDataProvider : ISpaceDataProvider, IForumDataProvider, IAlbumDataProvider
2    {
3               //除空间,论坛,相册,聚合以外的所有函数方法
4    }

    
    这样做的好处应该就是便于开发小组成员对各自的“开发边界”对一个共性的认识,便于分工和并行开发。

    但因为作用没有那么明显,所以在开源之前一直没有这样划分,我也只是私下与小组成员聊过这个问题,并未向上面进
行反映。

    另外一个我之前考虑的问题就是如果“第三方”来进行二次开发,可能会出现许多新的数据操作方法,而已有的功能是
不能满足的。而通过在IDataProvider 中添加相应方法又会使将来的更新升级(因为 3.0可能会有变化)变得非常麻烦。
所以目前我的一个想法就是添加第三方订制接口,形如:   

1  public interface IThirdParty 
2    
3           // 在此加入第三方数据访问操作接口函数,用户只要定义并实现这些函数即可。
4    }

5

    而IDataProvider 中相应添加一个字段,形如:

   

1  public interface IDataProvider
2    {
3         public  IThirdParty  {set;get;}  //第三方数据访问接口实例
4    }

5

    
    这样就可以把第三方新加的功能函数与系统本身的数据访问操作方法解耦,同时也提供了统一的接口调用方式,形如:

    

1 DatabaseProvider.GetInstance().IThirdParty.自定义的数据操作方法
2


   当然这些只是我私下的设想,我会在适当的时候将这个意见反映给开发小组。如果大家有什么别的建议或意见不妨在回
复中交流一下。


   另外要说的一个项目就是 discuz.common了,当然这个项目的活最杂也最底层,相当于“打零工”。里面基本上都是整
个产品所要使用的基础类。当然还有一部分是对.net框架自身函数的“再度封装”,目的就是为了优化代码结构,减少“因
不了解.net函数”而造成的使用上的错误等等。

   如discuz.common项目下的Xml文件夹里XmlDocumentExtender.cs,XmlVisitor.cs类就是这样的东东。

   同时我还继承了 Generic.List,Generic.Queue等几个泛型类(Generic目录下),并用“访问类模式”封装了几个
常用的访问操作。如:计数,累加等。    

 1 public interface IDiscuzVisitor
 2    {
 3        /// 
 4        /// 是否已运行
 5        /// 

 6       bool HasDone get; }
 7
 8        /// 
 9        /// 访问指定的对象
10        /// 

11        void Visit(T obj);
12    }

13
14    /// 
15    /// 累加数访问类
16    /// 

17    public sealed class SumVisitor : IDiscuzVisitor
18    {
19
20        private int sum;
21
22        /// 
23        /// 构造函数
24        /// 

25        public SumVisitor() 
26        { }
27
28        /// 
29        /// 访问指定的对象
30        /// 

31        public void Visit(int obj)
32        {
33            sum += obj;
34        }

35
36        /// 
37        /// 是否已运行
38        /// 

39        public bool HasDone
40        {
41            get
42            {
43                return false;
44            }

45        }

46
47        /// 
48        /// 返加累加数
49        /// 

50        public int Sum
51        {
52            get
53            {
54                return sum;
55            }

56        }

57    }

58
59

   而在相应的泛型类中调用如下(摘自DiscuzList.cs):    

 1   
 2        /// 
 3        /// 接受指定的访问方式(访问者模式)
 4        /// 
 5        /// 

 6        public void Accept(IDiscuzVisitor visitor)
 7        {
 8            if (visitor == null)
 9            {
10                throw new ArgumentNullException("访问器为空");
11            }

12
13
14            System.Collections.Generic.List.Enumerator enumerator = this.GetEnumerator();
15
16            while (enumerator.MoveNext())
17            {
18                visitor.Visit(enumerator.Current);
19
20                if (visitor.HasDone)
21                {
22                    return;
23                }

24            }

25        }

26
27    
28
29


    目前除了discuz.web.dll, discuz.space.dll, discuz.web.ui.dll之外,我们基本上了解了大多数dll文件的作用。
而这几个.dll的主要任务就是负责前台显示和后台设置的。如果大家觉得有必要的话,我会在后面的文章中做一下介绍。而有关
数据库(字段)的解释,我想在介绍 discuz.entity.dll时顺便作一下说明,请关心这个话题的朋友留意下周的文章。

转载于:https://www.cnblogs.com/songsh96/archive/2010/10/27/1862207.html

相关文章:

  • [VSX.001]深入VS SDK
  • 提供第三种代码生成方式——通过自定义BuildProvider为ASP.NET提供代码生成
  • 一个很简单的Win32汇编程序
  • Silverlight如何跨域访问没有策略文件的服务
  • Linux内核代码学习笔记(2.6.21.7 ARM) -- 内核启动函数start_kernel
  • 将DataTable转换成类的方法
  • NOKIA C5-00怎么连接电脑??
  • 2010 支付宝DevDiv移动开发者大会[上海]
  • Java王者归来 6月Tiobe编程语言排行榜公布
  • 关于 在JQuery dialog里的服务器控件 事件失效问题
  • jquery的uploadify在 firefox 上传出问题,怎么解决啊
  • 从客户端(TexContent=rty内容)中检测到有潜在危险的Request.Form 值 解决方案
  • Linux--打包压缩 tar,gzip,bzip2
  • DataRow的RowState属性
  • HTML a 标签的 target 属性说明
  • 〔开发系列〕一次关于小程序开发的深度总结
  • Codepen 每日精选(2018-3-25)
  • GraphQL学习过程应该是这样的
  • Java多态
  • NLPIR语义挖掘平台推动行业大数据应用服务
  • oldjun 检测网站的经验
  • PermissionScope Swift4 兼容问题
  • php面试题 汇集2
  • Python 反序列化安全问题(二)
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • 闭包,sync使用细节
  • 漂亮刷新控件-iOS
  • 让你的分享飞起来——极光推出社会化分享组件
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 算法系列——算法入门之递归分而治之思想的实现
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 自定义函数
  • python最赚钱的4个方向,你最心动的是哪个?
  • TPG领衔财团投资轻奢珠宝品牌APM Monaco
  • ​LeetCode解法汇总1276. 不浪费原料的汉堡制作方案
  • !!java web学习笔记(一到五)
  • # MySQL server 层和存储引擎层是怎么交互数据的?
  • #我与Java虚拟机的故事#连载12:一本书带我深入Java领域
  • ( 10 )MySQL中的外键
  • (Java数据结构)ArrayList
  • (react踩过的坑)antd 如何同时获取一个select 的value和 label值
  • (Redis使用系列) Springboot 实现Redis消息的订阅与分布 四
  • (安全基本功)磁盘MBR,分区表,活动分区,引导扇区。。。详解与区别
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (南京观海微电子)——COF介绍
  • (一)Thymeleaf用法——Thymeleaf简介
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • .NET CLR Hosting 简介
  • .NET CORE 3.1 集成JWT鉴权和授权2
  • .NET4.0并行计算技术基础(1)
  • .net6Api后台+uniapp导出Excel
  • .NET开发人员必知的八个网站
  • .net使用excel的cells对象没有value方法——学习.net的Excel工作表问题