网站内容管理系统
作者:板桥banq
上页
3.1 域模型设计
从系统需求来看,一个页面划分为页面布局、标题栏、页尾栏、菜单栏和内容栏部分,其中页面布局以及标题栏和页尾栏属于页面模板部分,可以由JSP借助Tiel来实现,每个页面变动的就是菜单栏和内容栏。因此,菜单栏和内容栏是本项目的主要处理对象,可以建立相关基本对象,如图4-6所示。
图4-6 系统的基本对象
Body对象是代表页面的内容部分,而Menu是代表菜单。由于一个页面可能有很多菜单,同时可能还有指向上一级页面的菜单,许多菜单组合在一起,可以使用Navlink对象来代表。
对象的创建顺序是:先创建菜单对象Menu,然后是Body对象,最后是模板合成生成页面,其中Menu和Body对象的创建都涉及到数据层的操作。对象创建流程如图4-7所示。
图4-7 创建流程
3.2 域模型的实现
模型(Model)的实现类似原来传统的数据库数据表的设计。在面向对象系统分析设计中,常用域模型设计替代以往的数据表结构设计。在本项目中,由于采取数据对象持久化到XML的设计方案,因此,只要设计实现数据对象就可以,如图4-8所示。
图4-8 数据模型的实现
在MenuModel中有5个属性,分别是:
Id:菜单的Id。
Name:菜单的名称,比如“关于我们”或“产品服务”。
Icon:菜单的图标,有时菜单可能不直接是菜单名的文本,而是特定图标。
Link:菜单指向的页面,是一个JSP页面。
Datalink:菜单指向的内容。
下一步根据Castor XML要求,需要设计数据对象和XML文本结构的映射,以Menu和Navlink为例:
<?xml version="1.0" encoding="UTF-8"?>
<mapping>
<description>a map file for our new template system</description>
<class name="com.jdon.cms.model.MenuModel">
<map-to xml="menu"/>
<field name="id" type="integer">
<!-- 表明id是XML元素的一个属性 -->
<bind-xml name="id" node="attribute" />
</field>
<!-- 对应MenuModel对象中的name属性 -->
<field name="name" type="string" />
<!-- 对应MenuModel对象中的icon属性 -->
<field name="icon" type="string" />
<!-- 对应MenuModel对象中的link属性 -->
<field name="link" type="string" />
<!-- 对应MenuModel对象中的datalink属性 -->
<field name="dataLink" type="string" />
</class>
<class name="com.jdon.cms.model.NavlinkModel">
<map-to xml="navlink"/>
<field name="id" type="integer" />
<field name="count" type="integer" />
<!-- 表明这个Navlink中包含menu节点集合 -->
<field name="menus"
type="com.jdon.cms.model.MenuModel" collection="collection" >
<xml name="menu" node="element" />
</field>
</class>
</mapping>
通过调用CastorHandler可以实现MenuModel对象到XML文件的序列化,其他数据对象也如此。
由此,数据对象的实现基本完成。当然,数据对象建模不是一次性就能成功,可能要经过反复斟酌修改。数据对象建模可以反映设计者对应用系统的真正认知能力,因此也是非常重要的一个步骤。
下一步将进入逻辑处理层的具体实现。逻辑处理层是本项目的核心功能层,设计方式将采取模式驱动设计(Pattern Driven Design)的方式。
3.3 抽象工厂模式
在本项目中,使用了XML文件系统作为数据存储介质,但是如果将来系统发展到一定规模,使用关系数据库作为存储介质,那么就需要替换存储介质。这种替换工作力求修改最小,不影响原有系统的稳定性,最好能无缝过渡到数据库系统,也就是说为了让系统具备灵活的扩展性和伸缩性。
为了达到这个目的,需要将有关XML数据操作的细节封装起来,使得外界根本不知道系统内部是采取XML文件还是使用数据库来实现数据持久化的。
还有一个目的,目前本项目没有加入用户权限控制,只有一个角色“管理员”可以操作数据。如果将来需要增加新的角色,这个角色只有修改页面内容的权限,没有创建新页面或删除页面的权限。这就需要在访问数据层之间加一个网关Proxy,用来进行权限访问控制。
抽象工厂模式可以满足这些要求,抽象工厂主要是提供创建系列对象的接口,使得外界无需知道创建这系列对象的具体细节。
实现抽象工厂,需要有下面4个参与者:
抽象工厂:主要是提供创建对象的接口,外界直接和这个抽象工厂交互。
抽象工厂的具体实现者: 主要是实现创建对象的具体细节,将有关XML文件具体实现封装在这个类中,将来可以再做一个使用关系数据的具体实现者。
抽象产品:产品的抽象接口。
抽象产品的具体实现者:详细定义上述抽象产品的细节。
下面将分别编写上述几个参与者的接口或类。首先,定义一个抽象工厂类PageFactory:
/**
* Page工厂
* <p>Copyright: Jdon.com Copyright (c) 2003</p>
*
* @author banq
* @version 1.0
*/
public abstract class PageFactory {
private static Object initLock = new Object();
//定义抽象工厂的具体实现类的名称
private static String className =
"com.jdon.cms.xml.XmlPageFactory";
private static PageFactory factory = null;
//用Singleton模式建立统一调用接口
public static PageFactory getInstance() {
if (factory == null) {
synchronized (initLock) {
if (factory == null) {
try {
//使用动态类装载机制将抽象工厂具体实现者激活
Class c = Class.forName(className);
//类似 PageFactory pageFactory = new XmlPageFactory
factory = (PageFactory) c.newInstance();
}
catch (Exception e) {
Debug.logError(" get factory instance error:" + e, module);
return null;
}
}
}
}
//其实是返回 XmlPageFactory的实例
return factory;
}
//获得导航条对象
public abstract Navlink getNavlink();
//创建新的菜单
public abstract Menu createMenu(Integer Id);
//创建新的内容
public abstract Body createBody(Integer Id);
…
}
另外,如果需要访问权限控制,可以创建一个抽象工厂的实现者如PageFactoryProxy,在PageFactoryProxy中对每个数据操作方法都进行一次权限是否允许的检验。
抽象工厂模式实际上是实现了数据层和逻辑层的分离,使得架构设计中的多层概念能够得以真正实现。架构设计中多层分离不能只是一种设计预想,还必须依靠一定的手段去真正落实,而设计模式恰好是有力的实现手段,这也就是所谓Pattern Driven Architecture。
使用模式的目的是增强系统的可重用性,降低耦合性,提高灵活性,掌握这个原则才真正学会使用模式。下面的分析将自然导出委托模式的使用。
继续分析XmlPageFactory类,在这个类中需要实现有关Menu对象和Body对象的XML操作。这就会出现一个问题,在这个类中将可能混合了很多功能,随着系统的扩展,功能的增多,会使得这个类变得庞乱复杂,增加了维护的困难性。
细化是面向对象设计自始至终的目标。具体原则就是:封装和分派。以被操作的对象为归类,将有关Menu对象和Body对象的功能分别委托另外的类来实现,这样XmlPageFactory类就会干净多了,而且如果有关Menu对象再进行修改,将不会直接修改XmlPageFactory这个关键的类,只会去修改委托类。比如是NavlinkManager,降低了修改工作带来的对系统稳定性的冲击。
同样有关Body对象的功能操作,则可以委托给BodyManager来实现,如图4-9所示。
图4-9 抽象工厂的流程图
XmlPageFactory的代码如下:
public class XmlPageFactory extends PageFactory {
public final static String module = XmlPageFactory.class.getName();
private final static XmlUtil xmlUtil = XmlUtil.getInstance();
//初始化Cache
public static UtilCache contentCache = new UtilCache(5, 0);
public static UtilCache navlinkCache = new UtilCache();
public static UtilCache bodyCache = new UtilCache();
//初始化被委托者
private NavlinkManager navlinkManager = null;
private BodyManager bodyManager = null;
private ContentManager contentManager = null;
private ContentFilter contentFilter = null;
public XmlPageFactory() {
navlinkManager = new NavlinkManager();
bodyManager = new BodyManager();
contentManager = new ContentManager();
contentFilter = new HTMLFilter();
}
public Navlink getNavlink() {
NavlinkModel navlink = (NavlinkModel) navlinkCache.get(Navlink.NAME);
if ( (navlink == null) || (navlink.isModified())) {
navlink = navlinkManager.getNavlinkFromFile();
navlinkCache.put(Navlink.NAME, navlink);
}
return navlink;
}
public Integer getId() {
Integer newId = null;
Navlink navlink = getNavlink();
if (navlink.getCount() == null)
newId = new Integer(1);
else{
newId = new Integer(navlink.getCount().intValue() + 1);
}
navlink.setCount(newId);
return newId;
}
public Menu createMenu(Integer Id) {
return navlinkManager.createMenu(Id);
}
public Menu getMenu(Integer Id, Navlink navlink) {
Menu menu = null;
try {
menu = navlinkManager.findByPrimaryKey(Id, navlink);
} catch (Exception ex) {
Debug.logError(" getMenu error:" + ex, module);
}
return menu;
}
public Menu updateMenu(Menu menu, Page page) {
return navlinkManager.updateMenu(menu, page);
}
public void deleteMenu(Menu menu, Navlink navlink) {
navlinkManager.deleteMenu(menu, navlink);
}
…
}
至此,在逻辑处理层中使用了抽象工厂模式。这样既能实现系统架构的层次分离,又能实现系统的动态扩展性,为以后扩展到数据库系统做好了准备,将相关代码改动所带来的损失降低到了最低程度,保证了系统的稳定性。
下页