Programming Erlang第四章中有个题目是使用表模拟数据库操作db.erl,定义的操作有
% 初始化一个新的Db
new()
% 清空Db
delete(Db)
% 把数据写入库
write(Key, Element, Db)
% 搜索Db,返回对应元素
read(Key, Db)
% 返回值为Element的Key列表
match(Element, Db)
实现比较好实现,不过因为Erlang是函数式语言,缺少状态机制,因此每个操作都需要传入Db参数。这种函数就像传统的C语言函数一样,需要传入数据结构。
Programming Erlang的第六章讲解了并发编程的几个模式,使用Server-Client模式对上述问题进行封装,得到my_db.erl。使用my_db.erl,我们就可以这么写代码:
my_db:start(),
my_db:write(foo, bar),
my_db:read(foo),
my_db:match(bar).
而使用db.erl的话只能写出这样的代码:
Db = db:new(),
NewDb = db:write(foo, bar, Db),
db:read(foo, NewDb),
db:match(bar, NewDb).
如果熟悉C语言和C++语言,那么很容易得出,第一种写法类似类,里面封装了个Db的成员变量,而后者更像是C语言写的。
现在来分析下my_db.erl的实现。my_db首先启动了个进程,该进程做的就是在本小节开头提到的Db操作。然后,my_db使用函数封装了进程间消息发送与接收的处理(这些函数称为Client Functions)。从这个角度来看,my\_db和类的原理是一致的,向外提供操作接口,内部又具有成员变量(这里指的是操作的Db是全部函数共享的)。
my_db.erl的代码如下:
-module(my_db).
-export([start/0,stop/0,write/2,delete/1,read/1,match/1]).
-export([init/0]).
start() ->
register(my_db, spawn(?MODULE, init, [])),ok.
stop() -> call({stop}).
write(Key, Element) -> call({write, {Key, Element}}).
delete(Key) -> call({delete, {Key}}).
read(Key) -> call({read, {Key}}).
match(Element) -> call({match, {Element}}).
call(Msg) ->
my_db ! {request, self(), Msg},
receive {reply, Reply} -> Reply end.
reply(From, Msg) ->
From ! {reply, Msg}.
init() ->
loop(db:new()).
loop(Db) ->
receive
{request, From, {stop}} ->
db:destroy(Db),
reply(From, ok);
{request, From, {write, {Key, Element}}} ->
NewDb = db:write(Key, Element, Db),
reply(From, ok),
loop(NewDb);
{request, From, {delete, {Key}}} ->
NewDb = db:delete(Key, Db),
reply(From, ok),
loop(NewDb);
{request, From, {read, {Key}}} ->
Result = db:read(Key, Db),
reply(From, Result),
loop(Db);
{request, From, {match, {Element}}} ->
Result = db:match(Element, Db),
reply(From, Result),
loop(Db)
end.
db.erl的代码如下:
-module(db).
-export([new/0, destroy/1, write/3, delete/2, read/2, match/2]).
new() ->
[].
destroy(_) ->
[].
write(Key, Element, Db) ->
[{Key, Element}|Db].
delete(Key, [{Key, _}|T]) ->
delete(Key, T);
delete(Key, [H|T]) ->
[H|delete(Key, T)];
delete(_, []) ->
[].
read(Key, [{Key, Element}|_]) ->
{ok, Element};
read(Key, [_|T]) ->
read(Key, T);
read(_, []) ->
{error, instance}.
match(Element, [{Key, Element}|T]) ->
[Key|match(Element, T)];
match(Element, [_|T]) ->
match(Element, T);
match(_, []) ->
[].
这里值得注意的一点是,db.erl是我在读Programming Erlang第四章的时候编写的,而my_db.erl是在读第六章的时候编写的。而my_db.erl用到db.erl,在其的编写过程中我并未改动db.erl。db.erl规定了进程所做的事情,而my_db.erl规定了客户端使用方法,并封装了个Db,Db其实就是指数据源了。
阅读(670) | 评论(0) | 转发(0) |