Python 设计模式——MVC模式

模型 - 视图 - 控制器模式

MVC 不仅仅是一种实现用户界面的软件模式,同时也是一种易于修改和维护的架构。通常 MVC 模式将应用程序分为 3 个基本部分:模型(Model)、视图(View)和控制器(Controller)。这 3 个部分相互关联,有助于将信息的处理与信息的呈现分开。

MVC 模式的工作机制为:模型提供数据和业务逻辑(如何存储和查询信息),视图负责数据的展示(如何呈现),而控制器则是两者之间的粘合剂,根据用户要求的呈现方式协调模型和视图。视图和控制器依赖于模型,但模型是可以独立工作的。

UML

  • 模型:定义针对数据的所有操作(如创建、修改和删除等),并提供与数据使用有关的方法
  • 视图:提供相应的方法,帮助根据上下文和应用程序的需要构建 Web 或 GUI 界面
  • 控制器:从请求接收数据,并将其发送到系统的其他部分。需要提供用于路由请求的方法

MVC 模式的主要意图:

  • 将数据和数据的展示隔离开
  • 使类的维护和实现更加简单
  • 灵活地改变数据的存储和显示方式,两者相互独立

模型是应用程序的基石,提供客户端请求的数据,必须在多个操作中保持一致。
视图用来将数据展示在接口上供用户查看。可以独立开发,但不应包含复杂的逻辑;需要足够灵活,适应多种平台;应避免与数据库直接交互。
控制器应该作为模型和视图之间的粘合剂,要尽可能薄;不应该进行数据库调用或参与数据的展示。

示例代码

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
class Model:
services = {
'email': {'number': 1000, 'price': 2},
'sms': {'number': 1000, 'price': 10},
'voice': {'number': 1000, 'price': 15}
}


class View:
def list_services(self, services):
for svc in services:
print(svc, ' ')

def list_pricing(self, services):
for svc in services:
print("For", Model.services[svc]['number'],
svc, 'message you pay $',
Model.services[svc]['price'])


class Controller:
def __init__(self):
self.model = Model()
self.view = View()

def get_services(self):
services = self.model.services.keys()
return (self.view.list_services(services))

def get_pricing(self):
services = self.model.services.keys()
return (self.view.list_pricing(services))


if __name__ == '__main__':
controller = Controller()
print("Services Provided:")
controller.get_services()
print("Pricing for Services:")
controller.get_pricing()

# => Services Provided:
# => email
# => sms
# => voice
# => Pricing for Services:
# => For 1000 email message you pay $ 2
# => For 1000 sms message you pay $ 10
# => For 1000 voice message you pay $ 15

现实世界中的 MVC 模式

目录结构:

1
2
3
4
5
6
mvc
├── server.py
└── templates
├── base.html
├── index.html
└── new.html

server.py 源代码:

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
import tornado
import tornado.web
import tornado.ioloop
import tornado.httpserver
import sqlite3

def _execute(sql):
db = sqlite3.connect('sqlite3.db')
cursor = db.cursor()
cursor.execute(sql)
res = cursor.fetchall()
db.commit()
db.close()
return res


class IndexHandler(tornado.web.RequestHandler):
def get(self):
query = "select * from task;"
todos = _execute(query)
print(todos)
self.render('index.html', todos=todos)


class NewHandler(tornado.web.RequestHandler):
def post(self):
name = self.get_argument('name', None)
query = "create table if not exists task (id INTEGER \
PRIMARY KEY, name TEXT, status NUMERIC);"
_execute(query)
query = "insert into task (name, status) values ('%s', %d);" % (name, 1)
_execute(query)
self.redirect('/')

def get(self):
self.render('new.html')


class UpdateHandler(tornado.web.RequestHandler):
def get(self, id, status):
query = "update task set status=%d where \
id=%s;" % (int(status), id)
_execute(query)
print(query)
self.redirect('/')


class DeleteHandler(tornado.web.RequestHandler):
def get(self, id):
query = "delete from task where id=%s;" % id
_execute(query)
self.redirect('/')


class RunApp(tornado.web.Application):
def __init__(self):
Handlers = [
(r'/', IndexHandler),
(r'/todo/new', NewHandler),
(r'/todo/update/(\d+)/(\d+)', UpdateHandler),
(r'/todo/delete/(\d+)', DeleteHandler),
]
settings = dict(
debug=True,
template_path='templates',
static_path='static',
)
tornado.web.Application.__init__(self, Handlers, **settings)


if __name__ == '__main__':
http_server = tornado.httpserver.HTTPServer(RunApp())
http_server.listen(5000)
tornado.ioloop.IOLoop.instance().start()

templates/base.html 源代码:

1
2
3
4
5
6
7
8
9
<!DOCTYPE>
<html>
<head>
{% block header %}{% end%}
</head>
<body>
{% block body %}{% end %}
</body>
</html>

templates/index.html 源代码:

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
{% extends 'base.html' %}
<title>ToDo</title>
{% block body %}
<h3>Your Tasks</h3>
<table border="1">
<tr align="center">
<td>Id</td>
<td>Name</td>
<td>Status</td>
<td>Update</td>
<td>Delete</td>
</tr>
{% for todo in todos %}
<tr align="center">
<td>{{ todo[0] }}</td>
<td>{{ todo[1] }}</td>
{% if todo[2] %}
<td>Open</td>
{% else %}
<td>Closed</td>
{% end %}
{% if todo[2] %}
<td><a href="/todo/update/{{todo[0]}}/0">Close Task</a></td>
{% else %}
<td><a href="/todo/update/{{todo[0]}}/1">Open Task</a></td>
{% end %}
<td><a href="/todo/delete/{{todo[0]}}">X</a></td>
</tr>
{% end %}
</table>

<div>
<h3><a href="/todo/new">Add Task</a></h3>
</div>
{% end %}

templates/new.html 源代码:

1
2
3
4
5
6
7
8
9
10
11
{% extends 'base.html' %}
<title>ToDo</title>
{% block body %}
<div>
<h3>Add Task to your List</h3>
<form action="/todo/new" method="post" id="new">
<p><input type="text" name="name" placeholder="Enter task" />
<input type="submit" class="submit" value="add" /></p>
</form>
</div>
{% end %}

运行 python server.py 命令,浏览 http://localhost:5000/ ,效果如下:
tasks

add task

PS:需安装 tornado 模块(pip install tornado