好久没写程式设计的文章了。
前几份工作与其他的软件工程师共事,
才发现其实大家都搞不太清楚系统架构到底在做什么。
特别趁着手边的案子整理了心得。
共勉之。
图文网址
http://wp.me/p16AXN-f5
资料读取与运作分开,从简到繁
# 在我不同的专案,我都会面临到同样的问题,也就是如何将我的架构作良好的切割。
# 我们秉持着物件导向的观念,使用继承与封装。
# 但组件式的观念逐渐受到重视后,继承似乎终将会走到深度继承的问题。
# 深度继承就是说当一个继承树必须处理过多繁杂的功能时,若非根部庞大,就是各分支
出现重复无法共用的程式码
# 若将读取资料装载在类别上,ParseXML()这个函式是用来读入XML的资料,传入一个
XMLNode。
# 因此类别中就会include或知道XML的资料结构。
# 若有一天我们改使用JSon或其他种类的资料读取方式,那么这个类别就必须新增加
ParseJSon()这个功能。
# 若此功能在继承树的根部,那么所有类别都必须增加此功能,若是使用纯虚拟(Pure
Virtual),情况就更糟糕,必须修正所有的子类别等到都完成实作之后才能上传程式
码(否则专案会编译失败)。
# 因此面对这个状况我们通常只有几个方案
## 勤奋的重构
## 想办法阻止规格变动
## 吸取重构的经验,在规格还不清楚之前舍弃先用物件导向的作法,亦即是提早体认到
这种必然性。在功能与资料输出入上分别做继承,解构我们的继承树。
# 在这个例子中是这样的架构:运作-资料-输出入,与其将全部做一个继承树,不如将
运作-资料,资料-输出入作解构。甚至将运作/资料都切割开。
## 以此例,我们的运作FunctionClass倚赖资料,比如DataStruct(或是使用该程式语言
的基础资料结构)。而传入资料的方式不采用ParseXML,而统一使用
SetupDataStruct( DataStruct _src )。
如此一来运作的类别都使用统一的接口来设定。
## 第二部分则是分析资料,例如使用
ParseXMLToDataStruct( XMLNode _node , DataStruct _src ),
然后当资料读取完毕再设定给运作物件FunctionClass。
# 若是有一天不使用XML时,我们要修改的部份就是另外制作
ParseJSonToDataStruct( JSon _node , DataStruct _src )这个函式(群组),
然后将原本使用ParseXMLToDataStruct()的部份修改为使用ParseJSonToDataStruct()。
这时只要ParseJSonToDataStruct()的实作正确,FunctionClass就完全不需要修改,
也不需要知道外界的读取媒介已经换掉了。
# 承上,为了要达到更好的切换,我们还必须实作
ParseToDataStruct( DataStruct _src )这个接口,让外界完全不用知道内部使用的
媒介的,只需要在初始化的时候切换设定
ParseSystem pSys = new ParseSystem_XML,或是
ParseSystem pSys = new ParseSystem_JSon。
# ParseSystem_XML::ParseToDataStruct()导到ParseXMLToDataStruct() 这个函式。
# ParseSystem_JSon::ParseToDataStruct()导到ParseJSonToDataStruct() 这个函式。
# 因此这个系统的演进会像这样
Type 1
http://tinyurl.com/adhcgbs
God使用FunctionClassBase1::ParseXML()
FunctionClassBase1知道XML
Type 2
http://tinyurl.com/aql66w6
God必须改使用FunctionClassBase2::ParseJSon()
FunctionClassBase2知道XML而且还知道JSon
Type 3
http://tinyurl.com/aow4snz
God必须切换使用ParseXMLToData()到ParseJSonToData()
FunctionClassBase3不用改变,也不需要知道现在用的是XML或JSon。
Type 4
http://tinyurl.com/bh7ynd3
God只需要修改初始化的地方改使用不同的ParseSystem,执行时不需要知道现在用的是
XML或JSon。