[]{#_bookmark1 .anchor}设计模式综合应用实例
设计模式综合应用实例
多人联机射击游戏中的设计模式应用(一)
为了方便大家更加系统地学习和掌握各种常用的设计模式,下面通过一个综合实例------“多人联机射击游戏”来学习如何在实际开发中综合使用设计模式。
反恐精英(Counter-Strike,CS)、三角洲部队、战地等多人联机射击游戏广受玩家欢迎,在多人联机射击游戏的设计中,可以使用多种设计模式。下面我选取一些较为常用的设计模式进行分析:
(1) 抽象工厂模式
在联机射击游戏中提供了多种游戏场景,不同的游戏场景提供了不同的地图、不同的背景音乐、不同的天气等,因此可以使用抽象工厂模式进行设计,类图如图1所示:
{width=“6.566386701662292in” height=“6.11in”}
图1 抽象工厂模式实例类图
在图1中,SceneFactory充当抽象工厂,其子类SceneAFactory等充当具体工厂,可以创建具体的地图(Map)、背景音乐(Music)和天气(Weather)等产品对象,如果需要增加新场景,只需增加新的具体场景工厂类即可。
(2) 建造者模式
在联机射击游戏中每一个游戏人物角色都需要提供一个完整的角色造型,包括人物造型、服装、武器等,可以使用建造者模式来创建一个完整的游戏角色,类图如图2所示:
{width=“6.583570647419073in” height=“3.6922911198600175in”}
图2 建造者模式实例类图
在图2中,PlayerCreatorDirector充当指挥者角色,PlayerBuilder是抽象建造者,其子类PlayerBuilderA和PlayerBuilderB是具体建造者,用于创建不同的游戏角色,Player是所创建的完整产品,即完整的游戏角色,它包含形体(body)、服装(costume)和武器(weapon)等组成部分。
(3) 工厂方法模式
在射击游戏中,AK47冲锋步枪、狙击枪、手枪等不同武器(Weapon)的外观、使用方法和杀伤力都不相同,玩家可以使用不同的武器,而且游戏升级时还可以增加新的武器,无需对现有系统做太多修改,可使用工厂方法模式来设计武器系统,类图如图3所示:
{width=“6.5130336832895885in” height=“2.910207786526684in”}
图3 工厂方法模式实例类图
在图3中,WeaponFactory接口表示抽象武器工厂,其子类AK47GunFactory生产AK47Gun,
SniperRifleFactory生产SniperRifle,不同的武器的display()、use()和fire()等方法有不同的实现。
(4) 迭代器模式
在射击游戏中,一个玩家可以拥有多种武器,如既可以拥有AK47冲锋枪,还可以拥有手枪和匕首,因此系统需要定义一个弹药库(武器的集合),在游戏过程中可以遍历弹药库(Magazine),选取合适的武器,在遍历弹药库时可使用迭代器模式,如类图如图4所示:
{width=“6.57207239720035in” height=“1.5166655730533682in”}
图4 迭代器模式实例类图
在类Magazine中,可以通过迭代器遍历弹药库,Magazine类的代码片段如下所示:
public class Magazine { private ArrayList weapons; private Iterator iterator;
public Magazine() {
weapons = new ArrayList(); iterator = weapons.iterator();
}
public void display() { while(iterator.hasNext()) {
((Weapon)iterator.next()).display();
}
}
…
}
除了遍历弹药库外,迭代器模式还可以用于遍历战队盟友等聚合对象。
(5) 命令模式
在射击游戏中,用户可以自定义快捷键,根据使用习惯来设置快捷键,如”W”键可以设置
为”开枪”的快捷键,也可以设置为”前进”的快捷键,可通过命令模式来实现快捷键设置,类图如图5所示:
{width=“6.5965529308836395in” height=“2.7318744531933508in”}
图5 命令模式实例类图 在图5中,ShortcutKey充当请求调用者,在其press()方法中将判断用户按的是哪个按键,再调用命令对象的execute()方法,在具体命令对象的execute()方法中将调用接收者如ShotHandler、GoAheadHandler的action()方法来执行具体操作。在实现时可以将具体命令类类名和键盘按键的键码(Keycode)存储在配置文件中,配置文件格式如下所示:
…
…
如果需要更换快捷键,只需修改键码和具体命令类的映射关系即可;如果需要在游戏的升级版本中增加一个新功能,只需增加一个新的具体命令类,可通过修改配置文件来为其设置对应的按键,原有类库代码无需任何修改,很好地符合开闭原则。
[]{#_bookmark4 .anchor}多人联机射击游戏中的设计模式应用(二)
多人联机射击游戏中的设计模式应用(二)
(6) 观察者模式
联机射击游戏可以实时显示队友和敌人的存活信息,如果有队友或敌人阵亡,所有在线游戏玩家将收到相应的消息,可以提供一个统一的中央角色控制类(CenterController)来实现消息传递机制,在中央角色控制器中定义一个集合用于存储所有的玩家信息,如果某玩家角色(Player)阵亡,则调用CenterController的通知方法notifyPlayers(),该方法将遍历用户信息集 合,调用每一个Player的display()方法显示阵亡信息,队友阵亡和敌人阵亡的提示信息有所不同,在使用notifyPlayers()方法通知其他用户的同时,阵亡的角色对象将从用户信息集合中被删除。可使用观察者模式来实现信息的一对多发送,类图如图6所示:
{width=“6.582461723534558in” height=“4.615in”}
图6 观察者模式实例类图
在图6中,CenterController充当观察目标,Observer充当抽象观察者,Player充当具体观察者。在Player类中,name属性表示角色名,type属性表示角色类型,如”战队A”或”战队B”等。Player的die()方法执行时将调用CenterController的notifyPlayers()方法,在notifyPlayers()方法中调用其他Player对象的提示方法,如果是队友阵亡则调用displayTeam(),如果是敌人阵亡则调用displayEnemy();还将调用detach()方法删除阵亡的Player对象,其中CenterController类的notifyPlayers()方法代码片段如下所示:
for(Object player : players) { if(player.getName().equals(name)) {
this.detach(player); //删除阵亡的角色
}
else {
if(player.getType().equals(type)) { player.displayTeam(name); //队友显示提示信息
}
else {
player.displayEnemy(name); //敌人显示提示信息
}
}
}
(7) 单例模式
为了节约系统资源,在联机射击游戏中可以使用单例模式来实现一些管理器(Manager),如场景管理器(SceneManager)、声音管理器(SoundManager)等,如图7所示的场景管理器SceneManager类:
{width=“5.494806430446194in” height=“3.12in”}
图7 单例模式实例类图
SceneManager类的实现代码片段如下所示【注:以下代码未考虑多线程访问的问题】:
class SceneManager {
private static SceneManager sManager = null;
private SceneManager() {
//初始化代码
}
public synchronized static SceneManager getInstance() { if(sManager==null) {
sManager = new SceneManager();
}
return sManager;
}
public void manage() {
//业务方法
}
}
(8) 状态模式
在射击游戏中,游戏角色存在几种不同的状态,如正常状态、暂停状态、阵亡状态等,在不同状态下角色对象的行为不同,可使用状态模式来设计和实现角色状态的转换,类图如图8所示:
{width=“6.578851706036746in” height=“3.4584372265966756in”}
图8 状态模式实例类图
在图8中,游戏角色类Player充当环境类,State充当抽象状态类,其子类NormalState、PauseState和DeathState充当具体状态类,在具体状态类的pause()、start()、beAttacked()等方法中可实现状态转换,其中NormalState类的代码片段如下所示:
class NormalState extends State
{
public void pause() //游戏暂停
{
//暂停代码省略
player.setState(new PauseState(this)); //转为暂停状态
}
public void start() //游戏启动
{
//游戏程序正在运行中,该方法不可用
}
public void beAttacked() //被攻击
{
//其他代码省略
if(lifeValue⇐0)
{
player.setState(new DeathState(this)); //转为阵亡状态
}
}
public void shot() //射击
{
//代码省略
}
public void move() //移动
{
//代码省略
}
}
(9) 适配器模式
为了增加游戏的灵活性,某些射击游戏还可以通过游戏手柄来进行操作,游戏手柄的操作程序和驱动程序由游戏手柄制造商提供,为了让当前的射击游戏可以与游戏手柄兼容,可使用适配器模式来进行设计,类图如图9所示:
{width=“6.513587051618548in” height=“2.9966666666666666in”}
图9 适配器模式实例类图
在图9中,GamepadsAdapter充当适配器,它将游戏手柄中按键(GamepadsKey)的方法适配到现有系统中,在其move()方法中可以调用MoveKey类的handle()方法,在其shot()方法中可以调用ShotKey的handle()方法,从而实现通过手柄来控制游戏运行。
[]{#_bookmark5 .anchor}数据库同步系统
数据库同步系统
[设计模式综合实例分析之数据库同步系统(一)设计模式综合实例分析之数据库同步系统(二)设计模式综合实例分析之数据库同步系统(三)]{.underline}
[]{#_bookmark6 .anchor}设计模式综合实例分析之数据库同步系统
(一)
设计模式综合实例分析之数据库同步系统
(一)
最近有很多朋友跟我聊到关于”在软件项目开发中如何合理使用设计模式”的问题,希望我能够给出一些相对比较完整的真实项目实例,为了满足大家的要求,在后续文章中,我将拿出几个较为复杂的实例与大家一起分享,有些项目是我参与开发的,有些项目是在我的指导下开发的,希望能给大家带来帮助!在此我也希望大家能够分享自己的一些设计模式使用心得和好的设计模式应用实例,可以整理一份给我(可发送到邮箱:[email protected]),在下一本设计模式图书(有计划明年写一本《设计模式案例剖析》,暂定名)中我将选取部分实例加入其中,如有入选者,Sunny承诺送签名图书两本,选择范围包括已经出版的《设计模 式》、《设计模式实训教程》、《设计模式的艺术》,还包括马上要出版的《C#设计模式》和正在编写的《UML建模实训教程》,任君挑选,正版保证,假一罚十!
从本文开始,我将介绍一个数据库同步系统的设计方案,该系统是我在2010年给某软件为了在数据库发生故障的情况下不影响核心业务的运行,需要将生产数据库定期备份到应
系统目前需求仅要求支持Oracle数据库的同步,但系统设计时需要考虑以后可以方便地支
{width=“6.524765966754155in” height=“4.68in”}
图1 数据库同步流程图
数据库同步系统界面如图2所示:
{width=“4.196023622047244in” height=“6.24in”}
图2 数据库同步系统界面
用户在操作界面指定源数据库、目标数据库、控制数据库(用于读取配置信息)的数据库连接串,同时选取需要同步的数据库对象类型,对象类型存储在配置文件database_syn_config.xml中,通过输入SQL语句可以获取需要同步的表数据。
数据库对象同步的处理逻辑描述如下:
(1) 对于一般的数据库对象,同步时先取出源数据库与目标数据库该类数据库对象进行对比, 然后将对象更新到目标数据库。
(2) 对于DBLink对象,由于数据库环境发生变化,需要手工调整,同步过程只记录新增的
DBLink信息,而不执行创建操作。
(3) 表的同步处理由于其包含数据,因此较为特殊,需先对表结构变化进行分析,再同步数据。表数据的同步有三种方式:增量同步、先Delete后Insert方式、临时表方式。
(I) 增量同步。适用于可确定最后修改时间戳字段的情况。
(II) 先Delete后Insert方式。即先删除表的数据,再将源数据库的该表数据插入到目标数据库, 为确保数据安全,要求在一个事务内完成。
(III) 临时表方式。用于最大限度保证数据的完整性,是一种在发生意外情况时,不丢失数据而使用的较为复杂的方式。
由于对数据库结构修改无法做事务回滚,因此如果后面的步骤发生异常,需要通过手工编码方式来实现目标数据库结构变化的回滚。
在本系统实现过程中使用了多种设计模式,下面对其进行简要分析(为了简化代码和类图, 省略了关于包的描述,在实际应用中已将不同的类封装在不同包中):
- 建造者模式
在本系统实现时提供了一个数据库同步流程管理器DBSynchronizeManager类,它用于负责控制数据库同步的具体执行步骤。用户在前台界面可以配置同步参数,程序运行时,需要根据这 些参数来创建DBSynchronizeManager对象,创建完整DBSynchronizeManager对象的过程由类DBSynchronizeManagerBuilder负责,此时可以使用建造者模式来一步一步构造一个完整的复杂对象,类图如图3所示:
{width=“6.513288495188101in” height=“3.871874453193351in”}
图3 建造者模式实例类图
在图3中省略了抽象建造者,DBSynchronizeManagerDirector充当指挥者类,
DBSynchronizeManagerBuilder充当建造者,DBSynchronizeManager充当复杂产品。
- 简单工厂模式
DBSynchronizeManagerBuilder类的buildLife()方法可以创建一个初始的DBSynchronizeManager 实例,再一步一步为其设置属性,为了保证在更换数据库时无须修改DBSynchronizeManagerBuilder类的源代码,在此处使用简单工厂模式进行设计,将数据库类型存储在配置文件中,如下片段代码所示:
…
<dbSynchronizeManager dbType=“oracle” class=“com. chinacreator.dbSyn
…
类图如图4所示:
{width=“6.553800306211723in” height=“3.255in”}
图4 简单工厂模式实例类图
使用简单工厂模式设计的工厂类DBSynchronizeManagerFactory代码如下所示:
public class DBSynchronizeManagerFactory {
public static DBSynchronizeManager factory(String dbType) throws String className = DBSynConfigParser.getSynchronizeManagerCl return (DBSynchronizeManager)Class.forName(className).newIns
}
}
其中DBSynConfigParser类用于读取配置文件,在图4中,DBSynchronizeManagerFactory类充当数据库同步流程管理器的简单工厂,DBSynchronizeManager是抽象产品,而OracleDBSynchronizeManager为具体产品。
[]{#_bookmark7 .anchor}设计模式综合实例分析之数据库同步系统
(二)
设计模式综合实例分析之数据库同步系统
(二)
接”设计模式综合实例分析之数据库同步系统(一)”。
- 享元模式和单例模式
在数据库同步系统中,抽象类DBObjectSynchronizer表示需要同步的数据库对象,对于不同的数据库对象类型,提供了不同的子类实现,在数据库同步时可能有多个线程在同时进行同步工作,为了节省系统资源,可以使用享元模式来共享DBObjectSynchroizer对象,提供了享元工厂类DBObjectSynchronizerFlyweightFactory,且享元工厂类使用单例模式实现,类图如图5所示:
{width=“6.5228958880139984in” height=“4.535416666666666in”}
图5 享元模式和单例模式实例类图
在图5中,DBObjectSynchronizerFlyweightFactory充当数据库对象同步执行者的享元工厂,同步对象执行类DBObjectSynchronizer充当抽象享元,其间接子类OracleDBlinkDBSynchronizer、OracleTableDBSynchronizer等充当具体享元(由于篇幅问题,未将所有数据库对象类一一列 出)。
在实现DBObjectSynchronizerFlyweightFactory时使用了单例模式(饿汉式单例),其代码片段
如下所示:
public class DBObjectSynchronizerFlyweightFactory {
private static DBObjectSynchronizerFlyweightFactory instance = n private Map<String, DBObjectSynchronizer> map = new HashMap<Stri private DBObjectSynchronizerFlyweightFactory(){ }
public static DBObjectSynchronizerFlyweightFactory getInstance() return instance;
…
}
- 观察者模式
在数据库同步系统中,用户可以自行决定需要同步哪些对象,需要同步的DBObjectSynchronizer子类对象将注册到DBSynchronizeManager中,DBSynchronizeManager类的代码片段如下所示:
public abstract class DBSynchronizeManager{
…
public void attachDBSynchronizer(DBObjectSynchronizer dbSynchron synchronizers.add(dbSynchronizer);
}
public void detachDBSynchronizer(DBObjectSynchronizer dbSynchron synchronizers.remove(dbSynchronizer);
}
public abstract void executeSyn() throws Exception;
}
其中attachDBSynchronizer(DBObjectSynchronizerdbSynchronizer)为注册方法, detachDBSynchronizer(DBObjectSynchronizerdbSynchronizer)为注销方法,executeSyn()为抽象的通知方法,其子类OracleDBSynchronizeManager为executeSyn()方法提供了具体实现,类图如图6所示:
{width=“6.522437664041995in” height=“4.389583333333333in”}
图6 观察者模式实例类图
在数据库同步时,如果DBSynchronizeManager的executeSyn()方法被调用,将遍历观察者集 合,调用每一个DBObjectSynchronizer对象的executeSyn()方法和compileDBObject()方法,此时DBSynchronizeManager充当抽象观察目标,OracleDBSynchronizeManager充当具体观察目标, DBObjectSynchronizer充当抽象观察者,OracleTableDBSynchronizer充当具体观察者。
- 模板方法模式
在执行同步时,OracleDBSynchronizeManager的executeSyn()方法将依次调用synDBObject()和compileDBObject()方法,并在这两个方法中分别调用DBObjectSynchronizer的processSyn()和compile()方法,在OracleDBSynchronizeManager的子类中可以覆盖synDBObject()和compileDBObject()方法,如图7所示:
{width=“3.3020209973753283in” height=“3.81in”}
图7 模板方法模式实例类图
在图7中,OracleDBSynchronizeManager充当抽象父类,其中定义了模板方法executeSyn(), NewOracleDBSynchronizeManager充当具体子类,其中OracleDBSynchronize Manager的代码片段如下所示:
public class OracleDBSynchronizeManager extends DBSynchronizeManager public void executeSyn() throws Exception {
synDBObject(); compileDBObject();
}
protected void synDBObject(){
for (DBObjectSynchronizer dbSynchronizer : synchronizers) { try {
dbSynchronizer.processSyn(this);
} catch (Exception e) { e.printStackTrace();
}
}
}
protected void compileDBObject(){
for (DBObjectSynchronizer dbSynchronizer : synchronizers) { try {
dbSynchronizer.compile(this);
} catch (Exception e) { e.printStackTrace();
}
}
}
}
由于Oracle数据库对象类型较多,而大部分对象的处理逻辑大同小异,只有少部分对象类型同步结构后需要重新编译,因此在设计DefaultOracleObjectSynchronizer类时也可以使用模板方法
模式,在其中定义一个钩子方法getCompileable(),由子类决定是否要调用编译逻辑,代码片段如下所示:
public class DefaultOracleObjectSynchronizer extends DBObjectSynchro
…
public void compile(DBSynchronizeManager dbSynchronizeManager) throws Exception {
if (getCompileable()){
Database destDB = dbSynchronizeManager.getDestDB(); String[] compileObjs = findAllObjects(destDB);
int iLen = compileObjs.length; for (int i = 0; i < iLen; i++) {
compileObject(destDB, compileObjs[i]);
}
}
}
…
}
[]{#_bookmark8 .anchor}设计模式综合实例分析之数据库同步系统
(三)
设计模式综合实例分析之数据库同步系统
(三)
接”设计模式综合实例分析之数据库同步系统(二)”。
- 策略模式
由于表数据的同步方式有三种,分别是增量同步、先Delete后Insert方式、临时表方式,因此可以定义一个同步策略接口DataSynStrategy,并提供三个具体实现类:IncSynStrategy、DelAndInsSynStrategy和TempTableSynStrategy。类图如图8所示:
{width=“6.574165573053368in” height=“2.415in”}
图8 策略模式实例类图
在图8中,Oracle表同步对象类OracleTableDBSynchronizer充当环境类,DataSynStrategy充当抽象策略类,其子类IncSynStrategy、DelAndInsSynStrategy和TempTableSynStrategy充当具体策略类。
在OracleTableDBSynchronizer中将DataSynStrategy作为方法synSingleTable()的局部变量,因此
OracleTableDBSynchronizer与DataSynStrategy为依赖关系,如果为全局变量,则为关联关系。
- 组合模式、命令模式和职责链模式
在使用临时表方式实现表同步时可以定义一系列命令对象,这些命令封装对数据库的操作, 由于有些操作修改了数据库结构,因此传统的JDBC事务控制起不到作用,需要自己实现操作失败后的回滚逻辑。此时可以使用命令模式进行设计,在设计时还可以提供宏命令MacroCommand,用于将一些具体执行数据库操作的命令组合起来,形成复合命令。类图如图9所示(由于原始类图比较复杂,考虑到可读性,图9进行了适当简化,不过简化了之后还是挺复杂的):
{width=“5.97780949256343in” height=“10.12in”}
图9 组合模式、命令模式和职责链模式实例类图
(由于涉及到多个模式的联用,此图有点点复杂)
在图9中,TempTableSynCommand充当抽象命令,MacroCommand充当宏命令类, RenameTableCommand、SynTableDataCommand和RenameTableConstraintCommand充当具体命令,TempTableSynStrategy充当请求调用者,DataSynHelper充当请求接收者,在DataSynHelper 中定义了辅助实现临时表方式同步的一些方法,在命令类中将调用这些方法。在TempTableSynCommand中声明了公共的execute()方法,并提供了回滚方法undo(),其子类实现具体的执行操作与恢复操作。DataSynHelper接口声明了进行数据库操作的方法,在其子类DataSynHelperImpl中实现了这些方法。
在TempTableSynCommand中还定义了两个子类型的变量previousCommand、nextCommnad用于保存前一个命令和后一个命令,其中nextCommnad用于在执行完当前命令的业务逻辑后,再执行下一个命令的业务逻辑;而previousCommand用于在出现异常时,调用上一个命令的undo() 方法实现恢复操作。此时使用了职责链模式,nextCommnad.execute()实现正向职责链,而previousCommand.undo()加上Java的异常处理机制实现反向职责链。
MacroCommand是宏命令,其代码片段如下所示:
public class MacroCommand extends TempTableSynCommand { TempTableSynCommand lastCommand = this;
public void add(TempTableSynCommand tempTableSynCommand) { tempTableSynCommand.setPreviousCommand(lastCommand);
lastCommand = tempTableSynCommand; //创建命令链
}
protected void execute() throws Exception {
…
}
protected void undo() throws Exception {
…
}
}
在请求调用者类TempTableSynStrategy中通过如下代码片段来调用宏命令对象的execute()方法:
public class TempTableSynStrategy extends DataSynStrategy { public String processSyn() {
//其他代码省略
String tempTableName = generateTempTableName();
String backupTableName = “BAK_” + tempTableName; DataSynHelper dataSynHelper = new DataSynHelperImpl(); MacroCommand marcoCommand = new MacroCommand(); marcoCommand.add(new RenameTableConstraintCommand(dataSynHel marcoCommand.add(new SynTableDataCommand(dataSynHelper, tabl marcoCommand.add(new RenameTableCommand(dataSynHelper, table marcoCommand.add(new RenameTableCommand(dataSynHelper, tempT try{
marcoCommand.execute(); try {
//其他代码省略
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e){ e.printStackTrace();
}
//其他代码省略
}
}
【本实例分析到此全部结束,希望能给各位带来帮助!】