关于实现C/S与B/s架构功能性通讯的设计方法
2. 在当时的项目背景下,B/S端的项目是先启动的,而A组的开发人员还没有意识到将来需要配合C/S端来做功能协作,因此产生的问题就是,前期的系统架构设 计没有过多地考虑以适应多个平台下的功能适应性。当然,从B/S端的设计角度上看,系统架构还算比较清晰。接着A组的开发人员就在这样的情况下,完成了系 统功能的实现。
3. 接着高层领导告诉项目经理需要做一套C/S架构的软件来配合B/S端平台的使用,而这时候B/S端的功能实现已经基本完成,B组开发人员成立。
4. 在B组架构人员开始设计架构的时候,并没有衍用B/S端的开发架构,很多基础架构(如分层模式、数据库结构、数据实体类等等)都存在很大的差异(C/S端 项目在初期的要求没有那么高,有的功能能削减掉就削减掉),后来B组架构人员发现需求文档上的有个功能和B/S平台上的某个功能是一样的,于是他和A组架 构人员进行交流,希望负责B/S平台上这个功能的开发人员能够帮助C/S平台帮助完成这一功能。于是A组的Leepy就匆匆忙忙地上阵了。
5. 最初Leepy同学因为在B/S平台上也有大量的任务需要完成,任务赶得狠,又收到这样一个“功能复制”的任务,心想:“那么就先把功能复制一份上去,然 后如果B/S平台上的功能有更新,就同步修改C/S平台就好”。于是打开C/S平台的项目,发现和B/S平台项目的差异性比较大,包括数据库结构和数据实 体类等等,更头疼的是这里采用的是.net framework 2.0进行开发,而B/S端采用的是.net framework 3.5进行开发,而且从功能上,Leepy使用大量的3.5的属性。要直接复用是不可能的,还需要调整相应的代码。
6. 于是C/S平台该功能出来了,运行得还行。现在才是郁闷的开始,因为该功能属于平台的核心模块,于是B/S平台上要时刻调整得比较大,所以同步的C/S端 的功能也要相应的调整,然后又运行完好。于是问题出来了,这样反复地修改导致系统(C/S和B/S)维护成本很高,架构间的设计耦合度太大。刚开始 Leepy抱怨为什么C/S端没有和B/S端统一架构,至少底层基础平台能够设计得具有可扩展性,光光抱怨无法解决问题,因为这是项目的人员配置的问题。 于是,Leepy想到了必须对该功能进重构,使用一个通用的组件进行抽象,而实际实现的,如C/S、B/S端具体应用,只要维护相应的业务代码。
设计思路
1. 说完场景,现在说说动手的部分。以一个中学生教育平台591up的网站为例,以及教育平台客户端的辅助软件。
这一功能实现一份Word文档试卷的导入保存并分解文档中的试题,将试题逐个保存入库(解析出来的试题部分还包括很多属性,如答案、知识点、解题关 键点等很多属性)。现在B/S平台和C/S平台都需要这个功能,但是B/S平台和C/S平台下的相关数据库实体类,设计不很统一,导致维护系统的成本很 高。于是,考虑是否能将解析器的设计与业务功能分开,将试卷解析器设计成通用的组件,而与B/S端和C/S端的业务代码彻底分开,对于解析的逻辑代码(基 础代码)在两端都可以引用到,而B/S端和C/S端所需要做得就是调整业务代码,并不需要关解析的基础代码是什么,组件与业务代码解耦。如下图所示:
2. 现在讲讲具体设计思路,先从试卷解析器基础组件开始(为了简化,该范例是削弱版的),创建一个.net 2.0的类库(为了适应客户端.net 2.0的配置)声明一个试卷解析器范型接口:
代码/// 试卷转换器泛型接口
/// </summary>
public interface IPaperConvertor<TIn, TOut>
{
/// <summary>
/// 转换方法
/// </summary>
/// <param name="tIn">转换输入类型</param>
/// <param name="helper">Word处理接口</param>
/// <returns>转换输出类型</returns>
TOut Convert(TIn tIn, IWordHelper helper);
}
其中TIn类型作为输入类型,TOut类型作为输出类型(TIn将来作为业务代码中实际的输入类型,如WordInfo类;TOut作为实际输出 类型,如PaperInfo类;IWordHelper为一个Word处理接口,这里的实现是 Microsoft.Office.Interop.Word)
考虑到转换器在转换过程Convert中,会产生一系列的步骤,首先对于转换这个过程进行细化,分解成各个步骤:
代码where TIn : class, new()
where TOut : class, new()
{
//成员
/// <summary>
/// 输出试卷实体
/// </summary>
protected TOut Paper { get; set; }
/// <summary>
/// 输入Word条件
/// </summary>
protected TIn WordInfo { get; set; }
#region Word操作实体属性
/// <summary>
/// Word操作实体属性
/// </summary>
protected IWordHelper WordHelper { get; set; }
#endregion
//公共方法
/// <summary>
/// 转换方法
/// </summary>
/// <param name="tIn"></param>
/// <returns></returns>
public virtual TOut Convert(TIn tIn, IWordHelper helper)
{
WordHelper = helper;
WordInfo = tIn;
Paper = Initialize(tIn);
if (Prepare())
Execute();
Finished();
return Paper;
}
//抽象方法
/// <summary>
/// 初始化
/// </summary>
/// <param name="tIn"></param>
/// <returns></returns>
protected abstract TOut Initialize(TIn tIn);
/// <summary>
/// 预装载
/// </summary>
/// <param name="tOut"></param>
/// <returns></returns>
protected abstract bool Prepare();
/// <summary>
/// 执行
/// </summary>
/// <param name="tOut"></param>
protected abstract void Execute();
/// <summary>
/// 完成
/// </summary>
protected abstract void Finished();
}
从代码中,我们可以看到Convert方法中调用了一系列的抽象方法,首先对于输入类型进行初始化(Initialize),接着通过输入类型预装载(Prepare),如果预装载成功,并开始执行。最后完成(Finished)所有的工作。
接着,需要定义一个包含Word解析逻辑代码的抽象类,这里使用Microsoft.Office.Interop.Word进行Office编程,于是创建名为
OfficeWordPaperConvertor.cs的类:
OfficeWordPaperConvertor/// 试卷解析器泛型抽象类
/// </summary>
public abstract class OfficeWordPaperConvertor<TIn, TQuestion, TOut> : BasePaperConvertor<TIn, TOut>
where TIn : class, new()
where TQuestion : class, new()
where TOut : class, new()
{
#region 试卷Word结构信息
/// <summary>
/// 试卷Word结构信息
/// </summary>
protected PaperWordInfo PaperWordInfo { get; private set; }
#endregion
#region Word操作辅助类属性
private OfficeWordHelper _OfficeWordHelper;
/// <summary>
/// Word操作辅助类属性
/// </summary>
protected OfficeWordHelper OfficeWordHelper
{
get
{
if (_OfficeWordHelper == null)
_OfficeWordHelper = GetWordHelper();
return _OfficeWordHelper;
}
}
#endregion
#region 预处理试卷
/// <summary>
/// 预处理试卷
/// </summary>
/// <param name="tOut"></param>
/// <returns></returns>
protected override bool Prepare()
{
//过滤试卷无效信息
FilterPaper();
//解析试卷
ParsePaper();
return true;
}
#endregion
#region 执行试卷
/// <summary>
/// 执行试卷
/// </summary>
/// <param name="tOut"></param>
protected override void Execute()
{
for (int i = 0; i < PaperWordInfo.Count; i++)
{
QuestionWordInfo questionWordInfo = PaperWordInfo[i];
//执行试题
ExcuteQuestion(questionWordInfo);
}
}
#endregion
#region 完成时调用
/// <summary>
/// 完成时调用
/// </summary>
protected override void Finished()
{
//这里进行完成时调用的实现
//..
}
#endregion
//虚方法
/// <summary>
/// 过滤试卷无效信息
/// </summary>
protected virtual void FilterPaper()
{
}
/// <summary>
/// 解析试卷
/// </summary>
protected virtual void ParsePaper()
{
PaperWordInfo = new PaperWordInfo();
//通过计算 OfficeWordHelper.Document.Text 得到文本中的题目数,这里省去这段逻辑
PaperWordInfo.AddQuestion(new QuestionWordInfo { StartIndex = 0, EndIndex = 0 });
PaperWordInfo.AddQuestion(new QuestionWordInfo { StartIndex = 1, EndIndex = 1 });
PaperWordInfo.AddQuestion(new QuestionWordInfo { StartIndex = 2, EndIndex = 2 });
}
/// <summary>
/// 执行试题
/// </summary>
/// <param name="questionWordInfo"></param>
protected virtual void ExcuteQuestion(QuestionWordInfo questionWordInfo)
{
string[] array = OfficeWordHelper.Document.Text.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
//创建试题解析器实体
TQuestion question = CreateQuestionConvertor(WordInfo, array[questionWordInfo.StartIndex]);
//将试题添加到试卷中
if (question != null) AddQuestion(question);
}
#region 获取Word工具类
/// <summary>
/// 获取Word工具类
/// </summary>
/// <returns></returns>
protected OfficeWordHelper GetWordHelper()
{
return WordHelper as OfficeWordHelper;
}
#endregion
//抽象方法
/// <summary>
/// 创建试题解析器实体
/// </summary>
/// <param name="subject"></param>
protected abstract TQuestion CreateQuestionConvertor(TIn tIn, string wordContent);
/// <summary>
/// 将试题添加到试卷中
/// </summary>
/// <param name="tPart"></param>
/// <param name="tQuestion"></param>
protected abstract void AddQuestion(TQuestion tQuestion);
}
为何这里没有重写Initialize方法呢?由于这里需要将Initialize暴露于业务代码中,可以通过业务代码来重写该方法,如果业务组件没有调用Initialize,将报错。
这里Prepare方法主要完成一份Word文档的信息过滤,并且将文档中按照试题题号进行拆分试题,形成试题列表。
Execute方法完成一份试卷的执行,通过试题列表将题目逐题入库。
Finshed方法在Execute之后,可通过事件委托告诉用户解析已经完成。
在后面附加的例子中,我会引用OfficeWordHelper.Document.Text 等于“1.试题1\r\n2.试题2\r\n3.试题3”的文本字符串来模拟Word文档中的文字(实际情况更
加复杂,Word文档中包括图片,符号,OLE对象等等,一切为了简化说明,这里省略该步骤),说明它拆分出来的试题有3道。QuestionWordInfo 类的
StartIndex,EndIndex对应试题所在行数索引。
接着注意ExcuteQuestion这个方法,调用了CreateQuestionConvertor和AddQuestion两个抽象方法。该两个抽象方法将在业务组件中实现。
试卷解析器基本设计实现了,现在看下试题解析器该如何实现:
声明一个试题解析器范型接口:
/// 试题转换器泛型接口
/// </summary>
public interface IQuestionConvertor<TIn, TOut>
{
TOut Convert(TIn tIn, string wordContent);
}
其中TIn类型作为输入类型,TOut类型作为输出类型(TIn将来作为业务代码中实际的输入类型,如WordInfo类;TOut作为实际输出类型,如QuestionInfo类)
考虑到转换器在转换过程Convert中,会产生一系列的步骤,首先对于转换这个过程进行细化,分解成各个步骤:
代码{
//成员
#region 输出试卷属性
/// <summary>
/// 输出试卷实体
/// </summary>
protected TOut Question { get; set; }
#endregion
#region 输入Word实体属性
/// <summary>
/// 输入Word实体属性
/// </summary>
protected TIn WordInfo { get; set; }
#endregion
//公共方法
#region 转换方法
/// <summary>
/// 转换方法
/// </summary>
/// <param name="tIn"></param>
/// <param name="helper"></param>
/// <returns></returns>
public virtual TOut Convert(TIn tIn, string wordContent)
{
WordInfo = tIn;
Question = Initialize(tIn);
&nbs
文章转载自:中国研发网 [http://www.yanfaw.com]
本文标题:关于实现C/S与B/s架构功能性通讯的设计方法
| 对本文中的事件或人物打分: | |
| 当前平均分:0.75 (4次打分) | |
| 对本篇资讯内容的质量打分: | |
| 当前平均分:0.67 (3次打分) | |
- [感动最多的] 个人站长现在做什么网站最赚钱-
- [路过最多的] 如何实现Button字体垂直居中CSS方法详解
- [高兴最多的] 从赶集网的发展以及盈利模式看如何做好分类
- [难过最多的] Softing公司新推出的FF通讯DTM产品
- [搞笑最多的] 2010年3D数码相机与高清DVD成消费电子类热
- [愤怒最多的] 如何实现VC利用VFW进行视频采集的方法实现
- [无聊最多的] 英特尔致力于Larrabee图形处理器研发
- [同情最多的] 如何实现VC利用VFW进行视频采集的方法实现
最新报道
- 03-09各大网站架构风格设计
- 03-09软件只复制文化
- 10-09系统架构师之软件建模分析方
- 10-09系统架构师之软件建模分析方
- 10-09系统架构师之软件架构到底是
- 10-09系统架构设计之-系统建模
- 10-09.Net企业架构应用方法示例
- 09-27【GOF设计模式之路】-- Sing
- 09-06如何成为一名合格的架构师
- 09-06UML详细介绍
相关资讯
- 12-15家纺行业龙头老大其研发设计与ERP系统的应用比重加大
- 11-26基于移动电视芯片SC6600V方案设计(CMMB标准芯片)
- 11-26如何使用设计模式之——桥接模式分析
- 11-26设计模式之——桥接模式实例分析(项目经验总结)
- 11-26基于ARM的嵌入式平台最小系统架构设计方法
- 11-18开源软件Discuz!NT 缓存设计架构详细解析
- 11-18Discuz!NT 缓存设计简析(本地缓存+memcached)
- 11-18浅谈领域驱动设计—软件核心复杂性应对之道
- 11-18浅谈领域驱动设计—软件核心复杂性应对之道
- 11-18浅谈领域驱动设计—软件核心复杂性应对之道











