The Adapter Pattern is a software design pattern that allows the interface of an existing class to be used from another interface. It's often used to mak existing classes work with others without modifying their source code.
From http://en.wikipedia.org/wiki/Adapter_pattern
定义
适配器模式的目标是改变接口,是将一组接口适配成用户期待的接口。
当引用的外部库的API发生改变的时候,如何适合这种改变?如何改变对象和类的接口,使之能够为现在的系统所兼容,这就是适配器模式的意义。
简单的来说,可以通过对象传入,然后做委托来实现。但是其实可以在做委托的过程中做更多的工作来丰富适配器本身
需求
有一个数据模型DataModel,它的来源可能是不同的数据库,MySQL/MongoDB等等。DataModel有一组CURD的接口,来读写数据库。
需求是可以适配不同的数据库来进行数据的读写。
这里扯出来说一说。在.net里面有一个ADO.NET,就是为数据库的访问提供多个统一的接口和基类就是DataAdapter,用来连接DataSet与Database。
有兴趣可以看一下:http://msdn.microsoft.com/zh-cn/library/h43ks021(v=vs.110).aspx
类图
前端的数据库,额,不能说数据库吗,就说能存存东西的地方:LocalStorage和IndexDB。
LocalStorage就不说了,这个比较大众了,twitter还因为LocalStorage爆出过XSS攻击的问题
关于IndexDB的知识,UML图没有给的很清楚,参考这里:
- https://developer.mozilla.org/zh-CN/docs/IndexedDB
- http://code.tutsplus.com/tutorials/working-with-indexeddb--net-34673
这里用LocalStorage来做例子,而且会模仿redis加入一个时间戳,未来可以用来标记是否数据过期
适配器模式这里仔细说一下,在传统的实现中,适配器模式有两种实现方式:类适配器模式和对象适配器模式
- 对象适配就是这里给出的例子,在adapter中含有adaptee的实例,然后再调用
- 类适配需要继承adaptee和adapter,然后内部做接口适配,这个需要用多继承,不建议在javascript中使用
角色
- AbstractDataAdapter (Target) 系统适应的接口组
- LsDataAdapter (Adapter) 适配器 负责接口转换
- LsDataAdaptee (Adaptee)
实现
var prototype = require('prototype');
var AbstractDataAdapter = prototype.Class.create({
create: function(key, data) {
throw new Error('method must be override!');
},
update: function(key, olddata, newdata) {
throw new Error('method must be override!');
},
read: function(key) {
throw new Error('method must be override!');
},
delete: function(key) {
throw new Error('method must be override!');
}
});
var LsAdaptee = function () {
// @todo 模拟判断是nodejs环境还是浏览器环境
if (require) {
var info = {};
return {
removeItem: function (key) {
return delete info[key];
},
setItem: function (key, data) {
console.log('setItem -- key:'+key+', data:'+data)
return info[key] = data
},
getItem: function (key) {
console.log('getItem -- key:'+key+', data:'+info[key])
return info[key]
}
}
}else{
return window.localStorage;
}
}
var LsDataAdapter = prototype.Class.create(AbstractDataAdapter, {
initialize: function(options) {
this.adaptee = new LsAdaptee();
},
create: function(key, data) {
var already = this.adaptee.getItem(key);
if (already) {
return false;
}
if (data) {
var insert = {
data: data,
jointime: Date.now()
}
this.adaptee.setItem(key, JSON.stringify(insert));
return true;
}else{
return false;
}
},
update: function(key, olddata, newdata) {
if (newdata) {
var insert = {
data: newdata,
jointime: Date.now()
}
}
this.adaptee.setItem(key, JSON.stringify(insert));
},
read: function(key) {
var data = this.adaptee.getItem(key);
if (data) {
var info = JSON.parse(data);
return info.data;
}else{
return false;
}
},
delete: function(key) {
this.adaptee.remove(key);
},
parse: function(source) {
// @todo 将datamodel转为viewmodel
return source;
}
});
var Model = prototype.Class.create({
initialize: function(options) {
this.adapter = options.adapter;
this.data = {};
},
set: function(key, data) {
this.data[key] = data;
this.adapter.update(key, null, data);
},
get: function(key) {
return this.data[key] = this.adapter.read(key);
}
})
var Main = function () {
var lsDataAdapter = new LsDataAdapter();
var model = new Model({ adapter: lsDataAdapter});
model.set('USER_INFO', {TOKEN: 'a627991dd0e5441be9fdd6f88746148a'});
var data = model.get('USER_INFO', {TOKEN: 'a627991dd0e5441be9fdd6f88746148a'});
console.log('---------------------------')
console.log(data);
}
Main();
参考
- http://sourcemaking.com/design_patterns/adapter
- http://blog.csdn.net/hguisu/article/details/7527842
- http://www.cnblogs.com/Terrylee/archive/2006/02/18/333000.html