网站内容管理系统

  作者:板桥banq

上页

3.1  域模型设计

从系统需求来看,一个页面划分为页面布局、标题栏、页尾栏、菜单栏和内容栏部分,其中页面布局以及标题栏和页尾栏属于页面模板部分,可以由JSP借助Tiel来实现,每个页面变动的就是菜单栏和内容栏。因此,菜单栏和内容栏是本项目的主要处理对象,可以建立相关基本对象,如图4-6所示。
1
图4-6  系统的基本对象
Body对象是代表页面的内容部分,而Menu是代表菜单。由于一个页面可能有很多菜单,同时可能还有指向上一级页面的菜单,许多菜单组合在一起,可以使用Navlink对象来代表。
对象的创建顺序是:先创建菜单对象Menu,然后是Body对象,最后是模板合成生成页面,其中Menu和Body对象的创建都涉及到数据层的操作。对象创建流程如图4-7所示。
1
图4-7  创建流程

3.2  域模型的实现

模型(Model)的实现类似原来传统的数据库数据表的设计。在面向对象系统分析设计中,常用域模型设计替代以往的数据表结构设计。在本项目中,由于采取数据对象持久化到XML的设计方案,因此,只要设计实现数据对象就可以,如图4-8所示。
1
图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);
    …
}


上面PageFactory类中动态指定了XmlPageFactory作为PageFactory的具体实现者,XmlPageFactory中封装的是数据持久化保存到XML文件系统中的操作过程。如果需要使用数据库系统,那么只要制作一个DatabasePageFactory,将上面程序中的className的值改为 DatabasePageFactory,那么整个系统的存储介质就转换到数据库系统上了。
另外,如果需要访问权限控制,可以创建一个抽象工厂的实现者如PageFactoryProxy,在PageFactoryProxy中对每个数据操作方法都进行一次权限是否允许的检验。
抽象工厂模式实际上是实现了数据层和逻辑层的分离,使得架构设计中的多层概念能够得以真正实现。架构设计中多层分离不能只是一种设计预想,还必须依靠一定的手段去真正落实,而设计模式恰好是有力的实现手段,这也就是所谓Pattern Driven Architecture。
使用模式的目的是增强系统的可重用性,降低耦合性,提高灵活性,掌握这个原则才真正学会使用模式。下面的分析将自然导出委托模式的使用。
继续分析XmlPageFactory类,在这个类中需要实现有关Menu对象和Body对象的XML操作。这就会出现一个问题,在这个类中将可能混合了很多功能,随着系统的扩展,功能的增多,会使得这个类变得庞乱复杂,增加了维护的困难性。
细化是面向对象设计自始至终的目标。具体原则就是:封装和分派。以被操作的对象为归类,将有关Menu对象和Body对象的功能分别委托另外的类来实现,这样XmlPageFactory类就会干净多了,而且如果有关Menu对象再进行修改,将不会直接修改XmlPageFactory这个关键的类,只会去修改委托类。比如是NavlinkManager,降低了修改工作带来的对系统稳定性的冲击。
同样有关Body对象的功能操作,则可以委托给BodyManager来实现,如图4-9所示。
1
图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);
  }
  …
}
至此,在逻辑处理层中使用了抽象工厂模式。这样既能实现系统架构的层次分离,又能实现系统的动态扩展性,为以后扩展到数据库系统做好了准备,将相关代码改动所带来的损失降低到了最低程度,保证了系统的稳定性。

 

 

下页