前言
相信大家对接口是不陌生的,但是你真的理解什么是接口吗?真的能用好吗?我们口口声声说按接口编程,到底如何接口编程呢?接口编程的意义在哪呢?...对于接口的迷茫,经常在三层结构里面看到的,千篇一律的把每一个Dao都写一个接口,每个Service再写一个接口,因为他们看的例子就是这样的,网上很多例子都是这样的,这就叫按接口编程了?!心里没底,到下次自己写项目自己设计的时候再加上赶进度怕是没这么勤奋的复制粘贴了,原因还是没有明白接口到底有什么用!甚着感觉接口这玩意就像脱裤子放屁——多此一举!真的是这样么?那么,接下来我和大家一起来探讨关于接口的种种...
正文
一、什么是接口、接口有什么用
我们先看看别人是怎么说接口的,我收集总结了一下,仅列出以下五种说法:
1. 接口的意义在于顶替多重继承。
2. 接口的作用,一言以蔽之,就是标志类的类别(type of class)。把不同类型的类归于不同的接口,可以更好的管理他们。
3. 接口简单理解就是一种约定,使得实现接口的类或结构在形式上保持一致,使用接口可以使程序更加清晰和条理化。
4. 接口就是定义了一个合同,实现这个接口的类都保证自己符合这个合同要求。
5. 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
这里我就不评价这几种说法了,下面说说我理解的接口是什么样子的。这里拿电脑里的主板来讲,主板上有USB总线接口、基本外设接口(用来连接键盘、鼠标、打印机等传统外设)、驱动器接口(用来连接硬盘驱动器、光盘驱动器和软盘驱动器等)...很眼熟吧!!这些东西都是带接口两字的,我们称之为硬件接口或接口类型,在翻阅关于这些硬件借口资料的时候你经常会发现这些接口都是由许多有名的公司如Microsoft、IBM、Intel、Apple等公司共同约定、开发的一种标准!!例如:USB是Compaq、DEC、IBM、Intel、Microsoft、NEC(日本)、Nothern Telecom(加拿大)等7家公司与1994年11月联合开发的计算机串行接口总线标准;IEEE 1394是1986年由Apple公司和TI(德克萨克仪器)公司开发的高速串行接口标准,命名为“火线”(Fire Wire)等。那么为什么要制定这些标准呢?包括现在都在争的3G标准,更有专门的标准组织和标准委员会。全世界硬件厂商多不胜数,随便列几个:
CPU: Intel、AMD
内存: 金士顿、黑金刚、宇瞻
硬盘: 日立、希捷
显示器: 飞利浦、三星、LG、明基、优派等,这么多厂商,这么多品牌,我们没有因为把飞利浦的显示器换成三星的电脑就不能用了,任意换硬盘、换内存,加显卡,接不同牌子的鼠标,用不同牌子的键盘,为什么没有问题?关键就在这里了——他们都遵循了标准,这些硬件都是按标准生产出来的!!所以我们用盗版的硬件(如 鼠标)也可以很爽,因为盗版他也遵循了标准!!可以说没有这些硬件标准——个人电脑也不能像今天如此普及!!现在我们再回过头来看接口,请告诉我你有什么感觉?我的感觉就是接口就是标准,或者称之为标准接口!!在硬件里面是,软件里面也是同样如此。好处是显而易见的,下面我们将上面的硬件接口“转换”成下面的软件接口的代码:
#region CPU接口
public interface CPU接口 { }
public interface 针脚式 : CPU接口 { }
public interface 卡式 : CPU接口 { }
public interface 触点式 : CPU接口 { }
public interface 针脚式 : CPU接口 { }
public interface Socket478 : 针脚式 { }
public interface Socket754 : 针脚式 { }
public interface Socket940 : 针脚式 { }
#endregion
#region 内存接口
public interface 内存接口 { }
public interface I144Pin : 内存接口 { }
public interface I168Pin : 内存接口 { }
public interface I240Pin : 内存接口 { }
#endregion
#region 硬盘接口
public interface 硬盘接口 { }
public interface IDE : 硬盘接口 { }
public interface SCSI : 硬盘接口 { }
public interface SATA : 硬盘接口 { }
#endregion
public class 精英A780GM
{
/// <summary>
/// 构造一块主板
/// </summary>
/// <param name="cpu">Socket AM2/AM2+</param>
/// <param name="hd">SATA接口</param>
/// <param name="ddr">DDR2</param>
public 精英A780GM(Socket940 cpu, SATA hd, I240Pin ddr)
{
}
}
这款精英A78GM主板是我随便从中关村在线里面找的一块板子,而上面的接口就是许多厂商坐在一起约定出来的标准接口,当然这里只是例举了主板的部分组件,但是可以看到,主板厂商都是按标准来进行制造的,他们生产不担心你插什么样的牌子CPU、硬盘、内存到主板上,只要你符合这个标准接口就行!!需要说明的是,上面五种对于接口的说法都是有一定道理的,而这里,我认为接口可以是标准,接口的意义更大体现在制定标准上面!!
二、如何使用标准
1. 标准接口
在上面的例子中,制定标准体现出良好的兼容性,有效降低了组合成本,更促进厂商按照标准专注本身等,下面我们再从软件编程中找更加贴切的例子来说明这一点。在跨数据库或数据库切换的时候我们可以用标准的接口来约束和规范数据库操作,以达到无缝切换(实际中可能有部分需要特殊处理)和跨数据库应用。下面给出一段无缝切换数据库的例子:
public interface IDAL
{
/// <summary>
/// 根据主键删除数据
/// </summary>
/// <param name="pk">主键</param>
void Delete(string pk);
}
public class SqlDAL : IDAL
{
#region IDAL 成员
public void Delete(string pk)
{
SqlHelper.ExecuteNonQuery(string.Concat("DELETE TABLE [TEST] WHERE ID = ", pk));
}
#endregion
}
public class OracleDAL : IDAL
{
#region IDAL 成员
public void Delete(string pk)
{
OracleHelper.ExecuteNonQuery(string.Concat("DELETE TABLE [TEST] WHERE ID = ", pk));
}
#endregion
}
public class MySqlDAL : IDAL
{
#region IDAL 成员
public void Delete(string pk)
{
MySqlHelper.ExecuteNonQuery(string.Concat("DELETE TABLE [TEST] WHERE ID = ", pk));
}
#endregion
}
public class Business
{
#region 变量
private static Type dalType;
private IDAL dal;
#endregion
#region 构造函数
public Business()
{
dal = Activator.CreateInstance(dalType) as IDAL;
}
static Business()
{
//web.config: <add key="DatabaseType" value="sqlserver" />
string DatabaseType = ConfigurationManager.AppSettings["DatabaseType"];
if (!string.IsNullOrEmpty(DatabaseType))
{
switch (DatabaseType.ToLower())
{
case "sqlserver":
dalType = typeof(SqlDAL);
break;
case "mysql":
dalType = typeof(MySqlDAL);
break;
case "oracle":
dalType = typeof(OracleDAL);
break;
default:
dalType = typeof(SqlDAL);
break;
}
}
else
dalType = typeof(SqlDAL);
}
#endregion
/// <summary>
/// 删除一笔数据
/// </summary>
/// <param name="id"></param>
public void Remove(string id)
{
dal.Delete(id);
}
}
注意:这段代码不考虑SQL语句安全、效率等问题,关键是体现接口的作用。
说明:好处是显而易见的,可维护性高,这段业务代码在切换数据库时是不需要更改任何代码的,只需轻松的把web.config的DatabaseType指定为其他的数据库类型就行了。这得益于SqlDAL、MySqlDAL、OracleDAL都是按标准的方式来实现的数据库操作的。便于分工,我们可以把这三个类分别交给三个人精通各自数据库的人来编写,这样同时也将业务层和数据层解耦了,只要标准一出,数据层和业务层的员工就可以同时开始编写代码,业务层员工只管按标准调用,而数据层员工只管按标准来编写。
2. 参数传递
对于接口的用途我最早是在java里面用于参数传递的,Java和C#都是强类型语言,也就是你传一个参数过来的时候需要明确指定一个类型。但是有一个类型非常特别,那就是如果我将参数的类型指定为object的时候,你不管传什么参数都可以,因为所有类型都继承自object!而将接口用于参数传递实现方式同object是一样的,只要你继承了你就可以被传输,所以大家经常能看到空的接口。接下来也会贴Java下使用Hibernate的一个例子 ,也是我第一次认识到接口作用的例子:
DaoBase.java
public class DaoBase extends HibernateDaoSupport {
public boolean add(IModel model) throws MyException {
try {
this.getHibernateTemplate().save(model);
return true;
} catch (Exception e) {
throw new MyException(e);
}
}
public boolean modified(IModel model)throws MyException {
try {
this.getHibernateTemplate().update(model);
return true;
} catch (Exception e) {
throw new MyException(e);
}
}
}
IModel.java
public interface IModel extends java.io.Serializable {
}
Account.java
public class Account implements IModel {
// Fields
private Integer id;
private String password;
// Constructors
/** default constructor */
public Account() {
}
// Property accessors
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
}
说明:DaoBase中HibernateDaoSupport在这里就不介绍了,主要是this.getHibernateTemplate()的两个方法save和update,这两个方法的所需参数均是Object,以前的做法就是每一个表写一个Dao,每个Dao里面写一个add方法,然后参数为特定Model或者说是VO,极其繁琐,经过这样改装后就可以有一个通用的Dao了,也减少了许多代码量,而且比起直接用Object参数更加安全,因为它帮助save和update明确指定了只有继承了这个接口的VO才能传递进来!
3. 身份
在我的一篇文章中曾今探讨过这种用法:IHttpHandler中使用Session实现原理[ASP.NET | IHttpHandler |IRequiresSessionState],为什么叫这种用法叫身份呢,主要是便于解说, 以下示例取材于“广东公务员考试黑幕事件”:
public interface 公务员考生身份 { }
/// <summary>
/// 打过招呼的
/// </summary>
public interface 备注 { }
public interface 警院万安中副院长 : 备注 { }
public interface 中警院人事处咸逢清处长亲戚 : 备注 { }
public interface 编办吴青川副处长 : 备注 { }
public interface 司法厅吕恩处长转 : 备注 { }
public interface 徐晓霞转省委办公厅信息处熊 : 备注 { }
public interface 王小平转陈育生亲戚 : 备注 { }
public interface 张新达委员转省财厅_办公厅等有关部门 : 备注 { }
public interface 阳春监狱警察梁志忠儿子 : 备注 { }
enum 考试结果
{
没考上 = 0,
普通狱警,
狱警组长,
狱警大队长,
狱警副主任,
其他职位
}
class 政府机关
{
/// <summary>
/// 考试
/// </summary>
/// <param name="ks">必须已经报考了公务员考试</param>
public void 考试(公务员考生身份 ks)
{
//.
}
public 考试结果 审核(公务员考生身份 ks)
{
//有“备注”的61个考生,一个没落,全部录取进入体检阶段。
if (ks is 备注)
{
if (ks is 警院万安中副院长)
return 考试结果.普通狱警;
if (ks is 中警院人事处咸逢清处长亲戚)
return 考试结果.普通狱警;
if (ks is 编办吴青川副处长)
return 考试结果.普通狱警;
if (ks is 司法厅吕恩处长转)
return 考试结果.狱警组长;
if (ks is 张新达委员转省财厅_办公厅等有关部门)
return 考试结果.狱警副主任;
if (ks is 徐晓霞转省委办公厅信息处熊)
return 考试结果.狱警大队长;
if (ks is 王小平转陈育生亲戚)
return 考试结果.普通狱警;
if (ks is 阳春监狱警察梁志忠儿子)
return 考试结果.普通狱警;
//.
return 考试结果.其他职位;
}
//正常程序审理
return (考试结果)(new Random().Next(2));
}
}
为了体现取材的严谨性,给出两个链接:
a).http://hi.baidu.com/pol1ce/blog/item/046e7adacf6ea8deb7fd4831.html
b).http://news.xinhuanet.com/local/2008-06/03/content_8303411.htm
说明:从上面例子可以看出,同样是具有考生身份的人,当他具有别的身份的时候比如备注过的身份,程序执行结果是不一样的,对于考生结果是横不等于没考上的!!is的作用很大,他能辨别得出来具有什么样身份的人,今年公务员考试你备注了吗?如果没有!赶紧继承吧O(∩_∩)O哈哈~
结束
写的时候苦于找不到合适的例子来说服自己,一直努力的阐述关于接口的所见所闻和所想,希望能带给你多一份关于接口的收获,热烈欢迎交流心得!!
--------------------------
新闻:
雅虎推搜索簿产品 模仿Google抛弃的Notebook导航:
博客园首页 知识库 新闻 招聘 社区 小组 博问 网摘 找找看文章来源:
http://www.cnblogs.com/over140/archive/2009/02/05/1383493.html