最近买了一本《精通Python设计模式》(作者:Sakis Kasampails),其中有一章是关于MVC的。MVC作为一种非常经典而且基础的架构模式,非常值得学习。
最近,随着深度学习的研究,我整个技术开发使用的语言基本上转向Python了,所以最近看了《Introduction to Programming Using Python》,其中有一章是GUI编程,使用的Tkinter,虽然其介绍较为基础,并不是完整的API介绍,但大部分常用的功能均有涉及。
然而,在使用Tkinter编写GUI程序,并且功能比较多的时候,我们的代码往往显得杂乱无章。因此,需要一个良好的设计模式来进行代码的管理,所以我选择了MVC模式来进行尝试。
本文的结构如下:大致分为三个部分。第一部分讲解MVC模式的含义。第二部分讲解了《精通Python设计模式》一书中关于MVC的示例,该示例清晰地展示了MVC架构模式的精髓,从头构建了一个MVC模式的控制台程序。第三部分我将介绍该示例的GUI版本,使用Tkinter工具以及MVC模式进行实现。
第一部分:什么是MVC架构模式?
模型-视图-控制器(Model-View-Controller,MVC)模式,其名称来自用来切分软件应用的三个主要部分:模型、视图、控制器。
模型是核心的部分,代表这应用的信息本源,包含和管理(业务)逻辑、数据、状态以及应用的规则。视图是模型的可视化表现(比如:计算机图形用户界面、控制台界面、智能手机的应用图形界面、PDF文档、饼图和柱状图等。视图只是展示数据,并不处理数据。控制器是模型与视图之间的胶水(glue)。模型与视图之间的所有通信都通过控制器进行。
我觉得链接http://www.itpub.net/thread-1869988-1-1.html的MVC示意图对帮助理解MVC有一定的帮助,如下图所示:
图中,用户向控制器提出一些操作请求,然后控制器将这些请求传到模型层进行处理,模型层处理完毕之后会将结果返回给控制器,控制器将结果更新到视图上给用户一个视觉上的反馈。
看了上述的解释以及示意图,想必你还是不能完全理解MVC模式,那下面进入第二部分,通过介绍一个示例来进一步感受MVC的设计思想。
第二部分:MVC控制台示例讲解
'''
这是一个简单的示例,用来展示如何从头实现MVC。
示例名称:名人名言打印机
想法:用户输入一个数字,然后就能看到与这个数字相关的名人名言。
名人名言存储在一个quotes元组中。
这种数据通常存放在数据库、文件或其他地方,只有模型能够直接访问它。
'''
quotes = ('A man is not complete until he is married.Then he is finished.',
'As I said before,I never repeat myself.',
'Behind a successful man is an exhausted woman.',
'Black holes really suck...', 'Facts are stubborn things.')
# 模型极为简约,只有一个get_quote()方法,基于索引n从quotes元组中返回对于的名人名言。
class QuoteModel:
def get_quote(self, n):
try:
value = quotes[n]
except IndexError as err:
value = 'Not found!'
return value
# 视图有3个方法,分别是show(),error(),select_quote()
# show()用于在屏幕上输出一句名人名言(或者提示Not found)
# error()用于在屏幕上输出一条错误信息
# select_quote()用于读取用户的选择
class QuoteTerminalView:
def show(self, quote):
print('And the quote is:"{}"'.format(quote))
def error(self, msg):
print('Error:{}'.format(msg))
def select_quote(self):
return input('Which quote number would you like to see? ')
#控制器负责协调。
#__init__()方法初始化模型和视图
#run()方法校验用户提供的名言索引,然后从模型中获取名言,并返回给视图
class QuoteTerminalController:
def __init__(self):
self.model=QuoteModel()
self.view=QuoteTerminalView()
def run(self):
valid_input=False
while not valid_input:
n=self.view.select_quote()
try:
n=int(n)
except ValueError as err:
self.view.error("Incorrect index '{}'".format(n))
else:
valid_input=True
quote=self.model.get_quote(n)
self.view.show(quote)
def main():
controller=QuoteTerminalController()
while True:
controller.run()
main()
第三部分:Tkinter MVC实践
import tkinter as tk
quotes = ('A man is not complete until he is married.Then he is finished.',
'As I said before,I never repeat myself.',
'Behind a successful man is an exhausted woman.',
'Black holes really suck...', 'Facts are stubborn things.')
class QuoteModel:
def get_quote(self, n):
try:
value = quotes[n]
except IndexError as err:
value = 'Not found!'
return value
class QuoteGUIView:
def __init__(self):
self.window = tk.Tk()
self.window.title("名人名言选择器")
frame1 = tk.Frame(self.window)
frame1.pack()
tk.Label(frame1, text="请输入需要查找的名言的索引? ").pack(side="left")
self.quoteNum = tk.StringVar() # 名言索引
tk.Entry(frame1, textvariable=self.quoteNum, justify="right").pack(side="left")
self.btn = tk.Button(frame1, text="查询")
self.btn.pack(side="left")
frame2 = tk.Frame(self.window)
frame2.pack()
self.quoteText = tk.StringVar() # 名言文本
tk.Label(frame2, textvariable=self.quoteText).pack()
def error(self,msg):
self.quoteText.set('Error:{}'.format(msg))
def show(self, quote):
self.quoteText.set('And the quote is:"{}"'.format(quote))
class QuoteGUIController:
def __init__(self):
self.view = QuoteGUIView()
self.model = QuoteModel()
def run(self):
self.view.btn.bind("<Button-1>", self.btnClicked)
self.view.window.mainloop()
def btnClicked(self,event):
n=self.view.quoteNum.get()
try:
n=int(n)
except ValueError as err:
self.view.error("Incorrect index:"+n)
else:
quote = self.model.get_quote(n)
self.view.show(quote)
if __name__ == '__main__':
controller=QuoteGUIController()
controller.run()