标签归档:数据源架构模式

数据源架构模式之活动记录

数据源架构模式之活动记录

【活动记录的意图】
一个对象,它包装数据表或视图中某一行,封装数据库访问,并在这些数据上增加了领域逻辑。

【活动记录的适用场景】
适用于不太复杂的领域逻辑,如CRUD操作等。

【活动记录的运行机制】
对象既有数据又有行为。其使用最直接的方法,将数据访问逻辑置于领域对象中。
活动记录的本质是一个领域模型,这个领域模型中的类和基数据库中的记录结构应该完全匹配,类的每个域对应表的每一列。
一般来说,活动记录包括如下一些方法:
1、由数据行构造一个活动记录实例;
2、为将来对表的插入构造一个新的实例;
3、用静态查找方法来包装常用的SQL查询和返回活动记录;
4、更新数据库并将活动记录中的数据插入数据库;
5、获取或设置域;
6、实现部分业务逻辑。

【活动记录的优点和缺点】
优点:
1、简单,容易创建并且容易理解。
2、在使用事务脚本时,减少代码复制。
3、可以在改变数据库结构时不改变领域逻辑。
4、基于单个活动记录的派生和测试验证会很有效。

缺点:
1、没有隐藏关系数据库的存在。
2、仅当活动记录对象和数据库中表直接对应时,活动记录才会有效。
3、要求对象的设计和数据库的设计紧耦合,这使得项目中的进一步重构很困难

【活动记录与其它模式】
数据源架构模式之行数据入口:活动记录与行数据入口十分类似。二者的主要差别是行数据入口 仅有数据库访问而活动记录既有数据源逻辑又有领域逻辑。

【活动记录的PHP示例】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
<?php
 
/**
 * 企业应用架构 数据源架构模式之活动记录 2010-10-17 sz
 * @author phppan.p#gmail.com  http://www.phppan.com
 * 哥学社成员(http://www.blog-brother.com/)
 * @package architecture
 */
 
/**
 * 定单类
 */
class Order {
 
    /**
     *  定单ID
     * @var <type>
     */
    private $_order_id;
 
    /**
     * 客户ID
     * @var <type>
     */
    private $_customer_id;
 
    /**
     * 定单金额
     * @var <type>
     */
    private $_amount;
 
    public function __construct($order_id, $customer_id, $amount) {
        $this->_order_id = $order_id;
        $this->_customer_id = $customer_id;
        $this->_amount = $amount;
    }
 
    /**
     * 实例的删除操作
     */
    public function delete() {
        $sql = "DELETE FROM Order SET WHERE order_id = " . $this->_order_id . " AND customer_id = "  . $this->_customer_id;
        return DB::query($sql);
    }
 
    /**
     * 实例的更新操作
     */
    public function update() {
    }
 
    /**
     * 插入操作
     */
    public function insert() {
    }
 
    public static function load($rs) {
        return new Order($rs['order_id'] ? $rs['order_id'] : NULL, $rs['customer_id'], $rs['amount'] ? $rs['amount'] : 0);
    }
 
}
 
class Customer {
 
    private $_name;
    private $_customer_id;
 
    public function __construct($customer_id, $name) {
        $this->_customer_id = $customer_id;
        $this->_name = $name;
    }
 
    /**
     * 用户删除定单操作 此实例方法包含了业务逻辑
     * 通过调用定单实例实现
     * 假设此处是对应的删除操作(实际中可能是一种以某字段来标记的假删除操作)
     */
    public function deleteOrder($order_id) {
        $order = Order::load(array('order_id' => $order_id, 'customer_id' => $this->_customer_id));
        return $order->delete();
    }
 
    /**
     * 实例的更新操作
     */
    public function update() {
    }
 
    /**
     * 入口类自身拥有插入操作
     */
    public function insert() {
    }
 
    public static function load($rs) {
        /* 此处可加上缓存 */
        return new Customer($rs['customer_id'] ? $rs['customer_id'] : NULL, $rs['name']);
    }
 
    /**
     * 根据客户ID 查找
     * @param integer $id   客户ID
     * @return  Customer 客户对象
     */
    public static function find($id) {
        return CustomerFinder::find($id);
    }
 
}
 
/**
 * 人员查找类
 */
class CustomerFinder {
 
    public static function find($id) {
        $sql = "SELECT * FROM person WHERE customer_id = " . $id;
        $rs = DB::query($sql);
 
        return Customer::load($rs);
    }
}
 
class DB {
 
    /**
     * 这只是一个执行SQL的演示方法
     * @param string $sql   需要执行的SQL
     */
    public static function query($sql) {
        echo "执行SQL: ", $sql, " <br />";
 
         if (strpos($sql, 'SELECT') !== FALSE) { //  示例,对于select查询返回查询结果
            return array('customer_id' => 1, 'name' => 'Martin');
        }
    }
 
}
 
/**
 * 客户端调用
 */
class Client {
 
    /**
     * Main program.
     */
    public static function main() {
 
 
        header("Content-type:text/html; charset=utf-8");
 
        /* 加载客户ID为1的客户信息 */
        $customer = Customer::find(1);
 
        /* 假设用户拥有的定单id为 9527*/
        $customer->deleteOrder(9527);
    }
 
}
 
Client::main();
?>

同前面的文章一样,这仅仅是一个活动记录的示例,关于活动记录模式的应用,可以查看Yii框架中的DB类,在其源码中有一个CActiveRecord抽象类,从这里可以看到活动记录模式的应用

另外,如果从事务脚本中创建活动记录,一般是首先将表包装为入口,接着开始行为迁移,使表深化成为活动记录。
对于活动记录中的域的访问和设置可以如yii框架一样,使用魔术方法__set方法和__get方法。

数据源架构模式之行数据入口

数据源架构模式之行数据入口

【行数据入口的意图】
充当数据源中单条记录入口的对象。每行一个实例
行数据入口提供了看起来像记录结构中记录的对象,但可以用编程语言的常规机制访问它。所有对数据源的访问细节都隐藏在这个接口之后。

【行数据入口的适用场景】
1、适用于事务脚本处理,能够很好的分离数据库访问代码,并且也很容易被不同的事务脚本重用。

【行数据入口的运行机制】
行数据入口是和单条记录极其相似的对象。行数据入口一般能实现从数据源类型到内存中类型的任意转换。

【行数据入口的优点和缺点】
优点:
1、能很好的分享数据库访问代码
2、很容易被不同的事务脚本重用。
3、使用行数据入口,可以在改变数据库结构时不改变领域逻辑。

缺点:
程序写起来比较冗长,代码量相对来说会大一些。

【行数据入口与其它模式】
数据映射器:行数据入口可以和数据映射器一起使用。
数据源架构模式之活动记录:行数据入口可以在移动领域逻辑后演变成活动记录。

【行数据入口的PHP示例】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
<?php
 
/**
 * 企业应用架构 数据源架构模式之行数据入口 2010-09-27 sz
 * @author phppan.p#gmail.com  http://www.phppan.com
 * 哥学社成员(http://www.blog-brother.com/)
 * @package architecture
 */
class PersonGateway {
 
    private $_name;
    private $_id;
    private $_birthday;
 
    public function __construct($id, $name, $birthday) {
        $this->setId($id);
        $this->setName($name);
        $this->setBirthday($birthday);
    }
 
    public function getName() {
        return $this->_name;
    }
 
    public function setName($name) {
        $this->_name = $name;
    }
 
    public function getId() {
        return $this->_id;
    }
 
    public function setId($id) {
        $this->_id = $id;
    }
 
    public function getBirthday() {
        return $this->_birthday;
    }
 
    public function setBirthday($birthday) {
        $this->_birthday = $birthday;
    }
 
    /**
     * 入口类自身拥有更新操作
     */
    public function update() {
        $data = array('id' => $this->_id, 'name' => $this->_name, 'birthday' => $this->_birthday);
 
        $sql = "UPDATE person SET ";
        foreach ($data as $field => $value) {
            $sql .= "`" . $field . "` = '" . $value . "',";
        }
        $sql = substr($sql, 0, -1);
 
        $sql .= " WHERE id = " . $this->_id;
 
        return DB::query($sql);
    }
 
    /**
     * 入口类自身拥有插入操作
     */
    public function insert() {
        $data = array('name' => $this->_name, 'birthday' => $this->_birthday);
 
        $sql = "INSERT INTO person ";
        $sql .= "(`" . implode("`,`", array_keys($data)) . "`)";
        $sql .= " VALUES('" . implode("','", array_values($data)) . "')";
 
        return DB::query($sql);
    }
 
    public static function load($rs) {
        /* 此处可加上缓存 */
        return new PersonGateway($rs['id'] ? $rs['id'] : NULL, $rs['name'], $rs['birthday']);
    }
 
}
 
/**
 * 人员查找类
 */
class PersonFinder {
 
    public function find($id) {
        $sql = "SELECT * FROM person WHERE id = " . $id;
        $rs = DB::query($sql);
 
        return PersonGateway::load($rs);
    }
 
    public function findAll() {
        $sql = "SELECT * FROM person";
        $rs = DB::query($sql);
 
        $result = array();
        if (is_array($rs)) {
            foreach ($rs as $row) {
                $result[] = PersonGateway::load($row);
            }
        }
 
        return $result;
    }
 
}
 
class DB {
 
    /**
     * 这只是一个执行SQL的演示方法
     * @param string $sql   需要执行的SQL
     */
    public static function query($sql) {
        echo "执行SQL: ", $sql, " <br />";
 
        if (strpos($sql, 'SELECT') !== FALSE) { //  示例,对于select查询返回查询结果
            return array('id' => 1, 'name' => 'Martin', 'birthday' => '2010-09-15');
        }
    }
 
}
 
/**
 * 客户端调用
 */
class Client {
 
    /**
     * Main program.
     */
    public static function main() {
 
 
        header("Content-type:text/html; charset=utf-8");
 
        /* 写入示例 */
        $data = array('name' => 'Martin', 'birthday' => '2010-09-15');
        $person = PersonGateway::load($data);
        $person->insert();
 
        /* 更新示例 */
        $data = array('id' => 1, 'name' => 'Martin', 'birthday' => '2010-09-15');
        $person = PersonGateway::load($data);
        $person->setName('Phppan');
        $person->update();
 
        /* 查询示例 */
        $finder = new PersonFinder();
        $person = $finder->find(1);
        echo $person->getName();
 
    }
 
}
 
Client::main();
?>

行数据入口本身有更新和插入语句,对于查询的操作放在外部的查询类中,不过也会调用行数据类本身返回数据。
如果有看过Yii框架的源代码,对于以上的示例程序应该有一种很熟悉的感觉!

数据源架构模式之表数据入口

数据源架构模式之表数据入口

【表数据入口的意图】
充当到数据库表的入口的对象。一个实例处理表中的所有行。
表数据入口包含了用于访问单个表或视图的所有SQL,如CRUD等。其它的代码调用它的方法来实现所有与数据库的交互。

【表数据入口的适用场景】
1、表数据入口可以同表模块一起使用。
2、适用于事务脚本处理

【表数据入口的运行机制】
表数据入口的实现非常简单,一般会包括几个从数据库中获取数据的查找方法以及更新,插入和删除方法。
每个方法都将输入参数映射为一个SQL调用并在数据库连接上执行该语句。

【表数据入口的优点和缺点】
1、简单。表数据入口可能是使用起来最简单的数据库接口模式。
2、为数据源的精确访问逻辑提供了一种自然的方法。
3、相同的接口即可以用于SQL操作操作数据库,又可以用于存储过程。

【表数据入口的PHP示例】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<?php
/**
 * 企业应用架构 数据源架构模式之表数据入口 2010-09-18 sz
 * @author phppan.p#gmail.com  http://www.phppan.com
 * 哥学社成员(http://www.blog-brother.com/)
 * @package architecture
 */
 
class PersonGateway {
 
    /**
     * 查询所有人员数据
     * @return <type>
     */
    public function findAll() {
        $sql = "SELECT * FROM person";
        return DB::query($sql);
    }
 
    /**
     * 根据名字查找人员数据
     * @param <type> $name
     * @return <type>
     */
    public function findByName($name) {
        $sql = "SELECT * FROM person WHERE `name` = '" . $name . "'";
        return DB::query($sql);
    }
 
    /**
     * 更新人员数据
     * @param <type> $key   关键字
     * @param <type> $data  需要更新的数据, 例如:$data = array('id' => 1, 'name' => 'Martin', 'birthday' => '');
     */
    public function update($id, $data) {
        if (empty($id) || !is_array($data)) {
            return FALSE;
        }
 
        $sql = "UPDATE person SET ";
        foreach ($data as $field => $value) {
            $sql .= "`" . $field . "` = '" . $value . "',";
        }
        $sql = substr($sql, 0, -1);
 
        $sql .= " WHERE id = " . $id;
 
        return DB::query($sql);
    }
 
    /**
     * 插入人员的数据
     * @param <type> $data 需要写入的数据, 例如:$data = array('name' => 'Martin', 'birthday' => '');
     */
    public function insert($data) {
        if (!is_array($data)) {
            return FALSE;
        }
 
        $sql = "INSERT INTO person ";
        $sql .= "(`" . implode("`,`", array_keys($data)) . "`)";
        $sql .= " VALUES('" . implode("','", array_values($data)) . "')";
 
        return DB::query($sql);
    }
 
}
 
class DB {
 
    /**
     * 这只是一个执行SQL的演示方法
     * @param string $sql   需要执行的SQL
     */
    public static function query($sql) {
        echo "执行SQL: ", $sql, " <br />";
    }
}
 
/**
 * 客户端调用
 */
class Client {
 
     /**
     * Main program.
     */
    public static function main() {
        $person = new PersonGateway();
 
        header("Content-type:text/html; charset=utf-8");
        $data = array('name' => 'Martin', 'birthday' => '2010-09-15');
        $person->insert($data);
 
        $id = 1;
        $data['id'] = $id;
        $person->update($id, $data);
 
        $person->findAll();
 
        $person->findByName('Martin');
    }
}
 
Client::main();
 
?>

如上所示代码仅为一个演示版本
然而这个演示版本有一些可以优化的地方,如:将更新和插入的SQL拼装过程可以提取出来等。

数据表接口是一个很简单的数据源模式,这在我们平常的工作中也有用到,有可能看起来与上面的例子相差较大,但是可能只是将某些方法进行了优化而已,究其本质是一样的。