Standup Timer的简单工厂Factory和单件模式
前言
学习android一段时间了,为了进一步了解android的应用是如何设计开发的,决定详细研究几个开源的android应用。从一些开源应用中吸收点东西,一边进行量的积累,一边探索android的学习研究方向。这里我首先选择了jwood的 Standup Timer 项目。
在Standup Timer的 数据访问层net.johnpwood.android.standuptimer.dao使用到了单件模式,我们首先来看一下net.johnpwood.android.standuptimer.dao的项目结构,理解各个类的职责,然后再详细了解其中的设计。
DAO的包的结构
![](https://pic002.cnblogs.com/img/xinsuhui/201009/2010091411010287.jpg)
DAO在Standup Timer中充当数据访问层的职责,数据库采用的是SQLite。我们来逐个了解一下上图中各类的职责,他们是负责干什么的!
CannoUpdateMeetingException.java
它继承自RuntimeException类,负责抛出对应的异常。注:private static final long serialVersionUID = 1L;用来表明类序列化时的不同版本间的兼容性,1L为默认值。DuplicateTeamException.java和InvalidTeamNameException.java相同。
DAOFactory.java
这是有所改变的工厂类(与原本的简单工厂模式不太一样,最大的区别在于多态的使用上),负责生产MeetingDAO 、TeamDAO类。
DAOHelper.java
继承自SQLiteOpenHelper,并且实现了DatabaseConstants接口,负责在第一调用时生成对应表和表字段,以及更新数据库。
DatebaseConstants.java
一个包含数据库名称和版本常量的接口,由DAOHelper实现。将数据库名称和版本分离处理,方便日后的版本升级甚至数据库名变更。将数据库名和版本的管理从DAOHelper中分离处理,降低了耦合度,使DAOHelper职责更单一,符合单一职责原则(其实我很难说明将这两个常量放置于接口当中是否妥当)。
MeetingDAO.java
继承自DAOHelper.java负责会议表的具体数据访问业务。因为DAOHelper.java实现了DatebaseConstants接口,所有在MeetingDAO的构造函数当中可以直接调用DATABASE_NAME和DATABASE_VERSION。如果getWritableDatabase()是第一次调用,系统将自动创建数据库和数据库表字段。
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public MeetingDAO(Context ctx) {
super(ctx, DATABASE_NAME, null, DATABASE_VERSION);
}
public Meeting save(Meeting meeting) {
if (meeting.getId() ==null) {
SQLiteDatabase db = getWritableDatabase();
return createNewMeeting(db, meeting);
} else {
String msg ="Attempting to update an existing meeting. Meeting entries cannot be updated.";
Logger.w(msg);
thrownew CannotUpdateMeetingException(msg);
}
}
super(ctx, DATABASE_NAME, null, DATABASE_VERSION);
}
public Meeting save(Meeting meeting) {
if (meeting.getId() ==null) {
SQLiteDatabase db = getWritableDatabase();
return createNewMeeting(db, meeting);
} else {
String msg ="Attempting to update an existing meeting. Meeting entries cannot be updated.";
Logger.w(msg);
thrownew CannotUpdateMeetingException(msg);
}
}
TeamDAO.java与MeetingDAO的基本一样。
DAOFactroy
我们先来看看在Model对DAO的调用代码:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
privatestatic DAOFactory daoFactory = DAOFactory.getInstance();
public Meeting save(Context context) {
MeetingDAO dao =null;
Meeting meeting =null;
try {
dao = daoFactory.getMeetingDAO(context);
meeting = dao.save(this);
} catch (Exception e) {
Logger.e(e.getMessage());
} finally {
dao.close();
}
return meeting;
}
public Meeting save(Context context) {
MeetingDAO dao =null;
Meeting meeting =null;
try {
dao = daoFactory.getMeetingDAO(context);
meeting = dao.save(this);
} catch (Exception e) {
Logger.e(e.getMessage());
} finally {
dao.close();
}
return meeting;
}
在Model 中 DAOFactory 的实例是由DaoFactory.getMeetingDAO(Context) 方法返回的,而对应的数据库访问类则由 daoFcatroy.getXXXXDAO(Context)方法返回。如果你看过一些有关设计模式的文章就很容易发现,这是单件模式。
下面来分析一下DAOFactory的代码:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
publicclass DAOFactory {
privatestatic DAOFactory instance =null;
publicstatic DAOFactory getInstance() {
if (instance ==null) {
instance =new DAOFactory();
}
return instance;
}
private DAOFactory() {
}
}
privatestatic DAOFactory instance =null;
publicstatic DAOFactory getInstance() {
if (instance ==null) {
instance =new DAOFactory();
}
return instance;
}
private DAOFactory() {
}
}
private DAOFactroy(){} 构造函数私有表明 DAOFactory 无法通过new关键字来实例化。要实例化只能通过静态的方法 getInstance()。在getInstance()方法中首先判断句柄instance是否为null,为空表示DAOFactory的实例尚未创建,然后调用 new DAOFactory() 创建实例(注意!因为是在 DAOFactory内部,所以可以访问private DAOFactory()成员),如果不为null则直接返回已创建的DAOFactory实例。这便是简单的单件模式设计方法(这里并没有考虑到多线程并发的问题,实际上就android的Standup Timer项目本身而言也不存在多线程的并发问题)。单件模式是一种比较简单的设计模式,比较容易理解,大家可以网上搜索一下,有很多相关的文章。
我们再来看看 MeetDAO 和 TeamDAO的相关代码;
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
privateboolean cacheDAOInstances =false;
private TeamDAO cachedTeamDAO =null;
private MeetingDAO cachedMeetingDAO =null;
private TeamDAO cachedTeamDAO =null;
private MeetingDAO cachedMeetingDAO =null;
public TeamDAO getTeamDAO(Context context) {
if (cacheDAOInstances) {
if (cachedTeamDAO ==null) {
cachedTeamDAO =new TeamDAO(getProperDAOContext(context));
}
return cachedTeamDAO;
} else {
returnnew TeamDAO(getProperDAOContext(context));
}
}
public MeetingDAO getMeetingDAO(Context context) {
if (cacheDAOInstances) {
if (cachedMeetingDAO ==null) {
cachedMeetingDAO =new MeetingDAO(getProperDAOContext(context));
}
return cachedMeetingDAO;
} else {
returnnew MeetingDAO(getProperDAOContext(context));
}
}
private Context getProperDAOContext(Context context) {
if (globalContext != null) {
return globalContext;
} else {
return context;
}
}
cacheDAOInstances是布尔值,相当于一个开关,用以判定是否需要对DAO类使用缓存(单件模式)。如果为True则进入单件模式的构建,如果为False 着直接返回一个新的DAO实例。这里通过getMeetingDAO 和getTeamDAO两个方法返回对应的DAO实例。另外getProperDAOContext()方法是负责解决DAOFactory 内的私有变量private Context globalContext 和方法的参数 context 间需要调用哪一个上下文context的冲突。其实我们也可以改写DAOFactory的生成方式:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
privatestatic DAOFactory instance =null;
private Context globalContext =null;
publicstatic DAOFactory getInstance(Context context) {
if (instance ==null) {
instance =new DAOFactory(context);
}
return instance;
}
private DAOFactory() {
globalContext=context;
}
private Context globalContext =null;
publicstatic DAOFactory getInstance(Context context) {
if (instance ==null) {
instance =new DAOFactory(context);
}
return instance;
}
private DAOFactory() {
globalContext=context;
}
这样在其他的方法中就可以省去 context参数。