A闪的 BLOG 技术与人文
书接上文,我们要开始对模型进行细节设计,并编写代码。所有代码使用Egret做示例。
注意:所有模块对外都应该为黑盒,数据需要一进一出。
将新手引导环节抽象为一个考试模型,有利于我们定义整个新手引导过程,即出题与奖励的过程。在整个工作模型中,再将不同环节进行拆分,每个环节可以进行扩展自定义,保证我们工作模型不变的情况下,能够适应更多的需求。
1、题库部分
问题:
题目如何定义? 题目从何而来? 题目如何存储?
我们将其流程进行细分,最终得到下面的模型。 转换为代码,我们可以做如下抽象。
在HTML5中,数据源可以使用JSON数据格式进行外部配置,这也是我们推荐的方式,而JSON将被解析为一个Object对象。根据需求得变更,我们的数据内容也会发生变化。所以要为这部分数据接口保留可扩展性。
题目作为最原子元素需要进行扩展,所以,我们允许进行继承扩展操作。我们创建题目的接口,实际实现,根据需求来进行扩展。
/*
author: mebius
date: 2016.2.5
des:topic数据接口
*/
interface ITopic
{
id:number; //当前topic的唯一ID编号
type:number; //当前topic类型
}
这里我们先设计一个暂定的数据格式,json内容如下:
{
"topic":[
{"id":"0","type":"tips","ui":"0","text":"这是新手引导的提示功能1"},
{"id":"1","type":"tips","ui":"0","text":"这是新手引导的提示功能2"},
{"id":"2","type":"action","ui":"1","text":"开启功能,领取奖励","award":["2","2","3","4","5","1"]}
]
}
假如要对这个数据进行解析,我们需要实现ITopic接口。类如下:
/*
author: mebius
date: 2016.2.8
des:topic数据
type类型包含如下2种:
"tips":纯提示
"action":需要执行多做
*/
enum TopicType
{
tips,action
}
class GameTopic implements ITopic
{
private _id:number = 0;
private _type:number = 0;
public get id():number
{
return this._id;
}
public get type():number
{
return this._type;
}
public constructor(id:number,type:TopicType)
{
this._id = id;
this._type = type;
}
private _ui:number = 0;
public setUI(ui:number)
{
this._ui = ui;
}
public get ui():number
{
return this._ui;
}
private _text:string="";
public setText(str:string)
{
this._text = str;
}
public get text():string
{
return this._text;
}
private _award:number[];
private _awardNum:number[];
private _awardsNum:number = 0;
public setAward(award:number[])
{
//console.log(award);
this._award = [];
this._awardNum = [];
if( award.length%2 == 1 )
{
throw( new Error("奖励数据错误!") );
}
else
{
this._awardsNum = award.length/2;
for(var i=0;i<this._awardsNum;i++)
{
this._award.push( award[i]*2 );
this._awardNum.push( award[i]*2+1 );
}
}
}
public get awardNum():number
{
return this._awardsNum;
}
public getAwardByIndex(index:number):number
{
return this._award[index];
}
public getAwardNumByIndex(index:number):number
{
return this._awardNum[index];
}
}
解析器需要留有数据输入与输出的接口,但对于不同数据格式,我们需要进行扩展,所以,解析器需要进行接口设计,与实现进行分离。我们先定义解析器接口ITopicParse。代码如下:
/*
author: mebius
date: 2016.2.5
des:当前接口定义了一个topic数据解析器所必须的动作
*/
interface ITopicParse
{
parse(data:any):void; //解析数据
getTopics():ITopic[]; //获取所有topic
dispose():void; //处理内存资源
}
针对我们上面的例子,解析器实现如下:
/*
author: mebius
date: 2016.2.8
des:topic数据解析器
*/
class GameTopicParse implements ITopicParse
{
private _topics:ITopic[];
public parse(data:any):void
{
this._topics = [];
var len:number = data.topic.length;
for(var i:number=0;i<len;i++)
{
var t:any = data.topic[i];
var topic:GameTopic;
switch( t.type )
{
case "tips":
topic = new GameTopic( t.id, TopicType.tips );
break;
case "action":
topic = new GameTopic( t.id, TopicType.action );
topic.setAward(t.award);
break;
}
topic.setUI(t.ui);
topic.setText(t.text);
this._topics.push(topic);
}
}
public getTopics():ITopic[]
{
return this._topics;
}
public dispose():void
{
this._topics = null;
}
}
最终的题目会直接进入道题库,但题库的变化较小,如需要扩展,我们允许继承扩展操作。目前我们还没有遇到一些特殊需求,所以,定义了题目录接口ITopicLib后,我们编写它的实现BaseTopicLib。
/*
author: mebius
date: 2016.2.5
des:topic库接口
*/
interface ITopicLib
{
addTopics(topics:ITopic[]):void; //添加topic
getTopicByID(id:number):ITopic; //通过ID获取topic
dispose():void; //清除内存资源
}
实现类
/*
author: mebius
date: 2016.2.5
des:基本topic库
*/
class BaseTopicLib implements ITopicLib
{
private _topics:ITopic[];
public constructor()
{
this._topics = [];
}
public addTopics(topics:ITopic[]):void
{
if( this._topics )
{
this._topics = topics;
}
else
{
throw(new Error("BaseTopicLib中_topics为null") );
}
}
/*
* 通过ID获取topic
* 如果ID无匹配项,则返回null
*/
public getTopicByID(id:number):ITopic
{
if( this._topics )
{
var len:number = this._topics.length;
for(var i:number=0;i<len;i++)
{
if(this._topics[i].id == id)
{
return this._topics[i];
}
}
return null;
}
else
{
throw(new Error("BaseTopicLib中_topics为null") );
}
}
public dispose():void
{
this._topics = null;
}
}
上面所讲的内容,是根据设计的工作模型所编写代码。后面,我们讲解后续的模型拆解方法。