Size: 9879
Comment:
|
← Revision 7 as of 2009-12-25 07:08:32 ⇥
Size: 8723
Comment: converted to 1.6 markup
|
Deletions are marked like this. | Additions are marked like this. |
Line 5: | Line 5: |
[[TableOfContents]] | <<TableOfContents>> |
Line 7: | Line 7: |
原文来自 [http://wiki.wxpython.org/index.cgi/ModelViewController wxPython wiki:: ModelViewController ] | 原文 [[http://wiki.wxpython.org/index.cgi/ModelViewController|wxPython wiki :: ModelViewController ]] 翻译:ZhangYunfeng |
Line 10: | Line 12: |
ModelViewController | |
Line 12: | Line 13: |
即将翻译,标记一下先 {{{ ModelViewController UserPreferences wxPython Wiki FrontPage SiteNavigation HelpContents |
ModelViewController 简称为 MVC |
Line 19: | Line 16: |
1 Introduction | = 简介 = |
Line 21: | Line 18: |
This page describes the MVC (ModelViewController) design pattern and shows how to implement it in Python. | 本页介绍 MVC (ModelViewController) 模式并演示其在Python中的实现方法 |
Line 23: | Line 20: |
You can read more about MVC at http://c2.com/cgi/wiki?ModelViewController and you should also check out a variation on this pattern at the ModelViewPresenter page. | 在 http://c2.com/cgi/wiki?ModelViewController 可以了解更多关于MVC的信息, 你也可以察看MVC的一种演化模式 ModelViewPresenter |
Line 25: | Line 22: |
2 Theory (orignally posted at http://mail.python.org/pipermail/python-list/2006-January/319314.html by has) |
= 原理 = (原文出处 http://mail.python.org/pipermail/python-list/2006-January/319314.html by has) |
Line 28: | Line 25: |
MVC is all about separation of concerns. | MVC模式关注于内容的分离。 |
Line 30: | Line 27: |
The Model is responsible for managing the program's data (both private and client data). The View/Controller is responsible for providing the outside world with the means to interact with the program's client data. | Model(模型)负责管理程序中的数据(私有数据和客户数据)。View/Controller(视图/控制器)负责为外界提供与程序中的客户数据进行交互的手段。 |
Line 32: | Line 29: |
The Model provides an internal interface (API) to enable other parts of the program to interact with it. The View/Controller provides an external interface (GUI/CLI/web form/high-level IPC/etc.) to enable everything outwith the program to communicate with it. | 模型提供了内部接口(API),允许程序的其他部分与之交互。视图/控制器提供外部接口(GUI/CLI/web form/high-level IPC/等等),允许程序外部的世界与程序进行交互。 |
Line 34: | Line 31: |
The Model is responsible for maintaining the integrity of the program's data, because if that gets corrupted then it's game over for everyone. The View/Controller is responsible for maintaining the integrity of the UI, making sure all text views are displaying up-to-date values, disabling menu items that don't apply to the current focus, etc. | 模型负责保持数据的完整性,因为数据一旦被破坏,所有的东西就 game over 了。视图/控制器负责保持UI的完整性, 确保所有的文字视图显示的都是最新的数据,使不适用于当前焦点的菜单失效,等等。 |
Line 36: | Line 33: |
The Model contains no View/Controller code; no GUI widget classes, no code for laying out dialog boxes or receiving user input. The View/Controller contains no Model code; no code for validating URLs or performing SQL queries, and no original state either: any data held by widgets is for display purposes only, and merely a reflection of the true data stored in the Model. | 模型不包含视图/控制器代码;没有GUI窗口类,没有对话框布置代码,不接受用户输入。视图/控制器不包含模型代码;没有URLs验证和SQL查询代码,也不包含数据原始状态: 窗口中的任何数据仅仅是为显示目的而存在,而且也仅仅是模型中数据的真实反映。 |
Line 38: | Line 35: |
Now, here's the test of a true MVC design: the program should in essence be fully functional even without a View/Controller attached. OK, the outside world will have trouble interacting with it in that form, but as long as one knows the appropriate Model API incantations, the program will hold and manipulate data as normal. | 如何验证真正的MVC设计呢?如果程序不存在视图/控制器部分,它也应该具有完整的功能。当然,这种情况下外部世界与它进行交互会存在困难,但是 只要知道适当的模型API,程序应该能够正常的保持和操作数据。 |
Line 40: | Line 37: |
Why is this possible? Well, the simple answer is that it's all thanks to the low coupling between the Model and View/Controller layers. However, this isn't the full story. What's key to the whole MVC pattern is the _direction_ in which those connection goes: ALL instructions flow _from_ the View/Controller _to_ the Model. The Model NEVER tells the View/Controller what to do. | 为什么能够达到这种效果?最简单的答案:都是因为模型与视图/控制器层之间的弱耦合关系。但是这并不是它的全部内容。MVC模式的关键是 '''这些连接的走向''':所有的指令都是从视图/控制器指向模型的。模型从不告诉视图/控制器去做什么。 |
Line 42: | Line 39: |
Why? Because in MVC, while the View/Controller is permitted to know a little about the Model (specifically, the Model's API), but the Model is not allowed to know anything whatsoever about the View/Controller. | 为什么呢?因为在MVC中,允许视图/控制器对模型有一些了解(特别是模型的API),但是不允许模型了解视图/控制器的任何情况。 |
Line 44: | Line 41: |
Why? Because MVC is about creating a clear separation of concerns. | 为什么呢?因为MVC就是要建立一个内容上完整分离的结构。 |
Line 46: | Line 43: |
Why? To help prevent program complexity spiralling out of control and burying you, the developer, under it. The bigger the program, the greater the number of components in that program. And the more connections exist between those components, the harder it is for developers to maintain/extend/replace individual components, or even just follow how the whole system works. Ask yourself this: when looking at a diagram of the program's structure, would you rather see a tree or a cat's cradle? The MVC pattern avoids the latter by disallowing circular connections: B can connect to A, but A cannot connect to B. In this case, A is the Model and B is the View/Controller. | 为什么呢?是为了防止无法控制程序的复杂性,导致你这位程序开发者精疲力尽。程序越大,其部件数量越多。这些部件之间的连接越多,开发人员就越难以控制/扩展/替换各个部件,甚至于无法搞清整个系统如何工作。问问你自己:当你看到程序结构图时,你愿意看到一颗树还是猫爪印?MVC模式拒绝回环连接(B可以连接A,但A不能连接B。在这里,A是模型,B是视图/控制器),从而避免了后者的产生。 |
Line 48: | Line 45: |
BTW, if you're sharp, you'll notice a problem with the 'one-way' restriction just described: how can the Model inform the View/Controller of changes in the Model's user data when the Model isn't even allowed to know that the View/Controller, never mind send messages to it? But don't worry: there is a solution to this, and it's rather neat even if it does seem a bit roundabout at first. We'll get back to that in a moment. | 另外,如果你够聪明,你会意识到刚才描述的单向约束中有一个问题:在模型根本不知道视图/控制器的情况下,如何通知视图/控制器其用户数据的变化呢?不要担心,这里有一个解决方案,虽然刚开始看起来有一些绕圈子,但实际上它相当的简洁,我们待会儿会讲到它。 |
Line 50: | Line 47: |
In practical terms, then, a View/Controller object may, via the Model's API, 1. tell the Model to do things (execute commands), and 2. tell the Model to give it things (return data). The View/Controller layer *pushes instructions* to the Model layer and *pulls information* from the Model layer. | 在实际项目中,视图/控制器对象会通过模型的API,1. 让模型做一些事情(执行命令),2. 让模型给它一些东西(返回数据)。视图/控制器层向模型层'''发出指令'''并从模型层中'''获取信息'''。 |
Line 52: | Line 49: |
A MyCoolListControl class for example, should pull the data it needs from the layer below, when it needs it. In the case of a list widget, that generally means asking how many values there are and then asking for each of those items in turn, because that's about the simplest and loosest way to do it and therefore keeps what coupling there is to a minimum. And if the widget wants, say, to present those values to the user in nice alphabetical order then that's its perogative; and its responsibility, of course. | 例如一个 MyCoolListControl 类,应该在需要时能够从下层获取所需的数据。对于list widget的情况,一般意味着询问有多少个值,然后返回每一个值,因为这是完成这种操作最简单最宽松的方式,同时将耦合保持到最低限度。而如果 widget 想向用户展示按照字母排序的结果,那就是它自己的工作,当然要它自己负责完成。 |
Line 54: | Line 51: |
Now, one last conundrum, as I hinted at earlier: how do you keep the UI's display synchronised with the Model's state in an MVC-based system? | 现在,最后一个迷题(也就是我们之前提到的): 在基于MVC的系统中如何保持UI的显示同步于模型的状态呢? |
Line 56: | Line 53: |
Here's the problem: many View objects are stateful, e.g. a checkbox may be ticked or unticked, a text field may contain some editable text. However, MVC dictates that all user data be stored in the Model layer, so any data held by other layers for display purposes (the checkbox's state, the text field's current text) must therefore be a subsidiary copy of that primary Model data. But if the Model's state changes, the View's copy of that state will no longer be accurate and needs to be refreshed. | 这就是问题所在:很多的视图对象都是状态性的,比如 checkbox 可能处于选择或者未选择状态,文字框可能包含一些可以编辑的文字。但是,MVC规定所有的用户数据保存在模型层中,所以其他层中的为了显示目的而存在的任何数据(checkbox的状态,文字框中的当前文字)必须是模型数据的辅助副本。但如果模型状态改变了,视图中的副本就不再准确而应该被刷新。 |
Line 58: | Line 55: |
But how? The MVC pattern prevents the Model pushing a fresh copy of that information into the View layer. Heck, it doesn't even allow the Model to send the View a message to say its state has changed. | 但是如何操作呢?MVC模式禁止模型向视图层发出相关信息的最新副本,甚至不允许模型向视图发出消息,表明其状态的变化。 |
Line 60: | Line 57: |
Well, almost. Okay, the Model layer isn't allowed to talk directly to other layers, since to do so would require it knows something about those layers, and MVC rules prevent that. However, if a tree falls in a forest and nobody's around to hear it, does it make a sound? | 是的,几乎就是这样。Ok,不允许模型层与其他层直接对话,因为这就要求它知道其他层的信息,而且MVC的规定也不允许这样做。但是,如果森林中的一颗树倒下了,而没有人听到,那么它发出声音了吗? |
Line 62: | Line 59: |
The answer, you see, is to set up a notifications system, providing the Model layer with a place it can announce to no-one in particular that it has just done something interesting. Other layers can then post listeners with that notification system to listen for those announcements that they're actually interested in. The Model layer doesn't need to know anything about who's listening (or even if anyone is listening at all!); it just posts an announcement and then forgets about it. And if anyone hears that announcement and feels like doing something afterwards - like asking the Model for some new data so it can update its on-screen display - then great. The Model just lists what notifications it sends as part of its API definition; and what anyone else does with that knowledge is up to them. | 答案就是建立一套通告系统,为模型层提供一个空间,让它可以发出通告,说它已经完成了一些有趣的事情。其他层可以向通告系统发出监听器,监听它们所感兴趣的的通告。模型层根本不需要知道谁在监听(甚至是否有人在监听);它仅仅发出通告,然后就把它抛在了脑后。如果有人听到了这个通告,并且想随之做一些事情 - 比如向模型询问一些新的数据以便更新显示 - 这样就搞定了。模型只是将它所发出的通告列表作为它的API定义的一部分;其他人想拿这个知识做什么事的话,随便。 |
Line 64: | Line 61: |
MVC is preserved, and everyone is happy. Your application framework may well provide a built-in notifications system, or you can write your own if not (see the 'observer pattern'). | MVC is preserved,and everyone is happy. 你的应用程序框架可能已经提供了内置的通告系统,你也可以自己写一个(参考 ObserverPattern)。 |
Line 66: | Line 63: |
3 Code Sample | = 示例代码 = |
Line 70: | Line 67: |
{{{#!python | |
Line 169: | Line 167: |
3.1 Comments FindPage by browsing, title search , text search or an index Or try one of these actions: AttachFile, DeletePage, LikePages, LocalSiteMap, SpellCheck |
|
Line 173: | Line 168: |
= 讨论 = <<Include(/Discuss)>> |
原文 wxPython wiki :: ModelViewController
翻译:ZhangYunfeng
1. 模型-视图-控制器
ModelViewController 简称为 MVC
2. 简介
本页介绍 MVC (ModelViewController) 模式并演示其在Python中的实现方法
在 http://c2.com/cgi/wiki?ModelViewController 可以了解更多关于MVC的信息, 你也可以察看MVC的一种演化模式 ModelViewPresenter
3. 原理
(原文出处 http://mail.python.org/pipermail/python-list/2006-January/319314.html by has)
MVC模式关注于内容的分离。
Model(模型)负责管理程序中的数据(私有数据和客户数据)。View/Controller(视图/控制器)负责为外界提供与程序中的客户数据进行交互的手段。
模型提供了内部接口(API),允许程序的其他部分与之交互。视图/控制器提供外部接口(GUI/CLI/web form/high-level IPC/等等),允许程序外部的世界与程序进行交互。
模型负责保持数据的完整性,因为数据一旦被破坏,所有的东西就 game over 了。视图/控制器负责保持UI的完整性, 确保所有的文字视图显示的都是最新的数据,使不适用于当前焦点的菜单失效,等等。
模型不包含视图/控制器代码;没有GUI窗口类,没有对话框布置代码,不接受用户输入。视图/控制器不包含模型代码;没有URLs验证和SQL查询代码,也不包含数据原始状态: 窗口中的任何数据仅仅是为显示目的而存在,而且也仅仅是模型中数据的真实反映。
如何验证真正的MVC设计呢?如果程序不存在视图/控制器部分,它也应该具有完整的功能。当然,这种情况下外部世界与它进行交互会存在困难,但是 只要知道适当的模型API,程序应该能够正常的保持和操作数据。
为什么能够达到这种效果?最简单的答案:都是因为模型与视图/控制器层之间的弱耦合关系。但是这并不是它的全部内容。MVC模式的关键是 这些连接的走向:所有的指令都是从视图/控制器指向模型的。模型从不告诉视图/控制器去做什么。
为什么呢?因为在MVC中,允许视图/控制器对模型有一些了解(特别是模型的API),但是不允许模型了解视图/控制器的任何情况。
为什么呢?因为MVC就是要建立一个内容上完整分离的结构。
为什么呢?是为了防止无法控制程序的复杂性,导致你这位程序开发者精疲力尽。程序越大,其部件数量越多。这些部件之间的连接越多,开发人员就越难以控制/扩展/替换各个部件,甚至于无法搞清整个系统如何工作。问问你自己:当你看到程序结构图时,你愿意看到一颗树还是猫爪印?MVC模式拒绝回环连接(B可以连接A,但A不能连接B。在这里,A是模型,B是视图/控制器),从而避免了后者的产生。
另外,如果你够聪明,你会意识到刚才描述的单向约束中有一个问题:在模型根本不知道视图/控制器的情况下,如何通知视图/控制器其用户数据的变化呢?不要担心,这里有一个解决方案,虽然刚开始看起来有一些绕圈子,但实际上它相当的简洁,我们待会儿会讲到它。
在实际项目中,视图/控制器对象会通过模型的API,1. 让模型做一些事情(执行命令),2. 让模型给它一些东西(返回数据)。视图/控制器层向模型层发出指令并从模型层中获取信息。
例如一个 MyCoolListControl 类,应该在需要时能够从下层获取所需的数据。对于list widget的情况,一般意味着询问有多少个值,然后返回每一个值,因为这是完成这种操作最简单最宽松的方式,同时将耦合保持到最低限度。而如果 widget 想向用户展示按照字母排序的结果,那就是它自己的工作,当然要它自己负责完成。
现在,最后一个迷题(也就是我们之前提到的): 在基于MVC的系统中如何保持UI的显示同步于模型的状态呢?
这就是问题所在:很多的视图对象都是状态性的,比如 checkbox 可能处于选择或者未选择状态,文字框可能包含一些可以编辑的文字。但是,MVC规定所有的用户数据保存在模型层中,所以其他层中的为了显示目的而存在的任何数据(checkbox的状态,文字框中的当前文字)必须是模型数据的辅助副本。但如果模型状态改变了,视图中的副本就不再准确而应该被刷新。
但是如何操作呢?MVC模式禁止模型向视图层发出相关信息的最新副本,甚至不允许模型向视图发出消息,表明其状态的变化。
是的,几乎就是这样。Ok,不允许模型层与其他层直接对话,因为这就要求它知道其他层的信息,而且MVC的规定也不允许这样做。但是,如果森林中的一颗树倒下了,而没有人听到,那么它发出声音了吗?
答案就是建立一套通告系统,为模型层提供一个空间,让它可以发出通告,说它已经完成了一些有趣的事情。其他层可以向通告系统发出监听器,监听它们所感兴趣的的通告。模型层根本不需要知道谁在监听(甚至是否有人在监听);它仅仅发出通告,然后就把它抛在了脑后。如果有人听到了这个通告,并且想随之做一些事情 - 比如向模型询问一些新的数据以便更新显示 - 这样就搞定了。模型只是将它所发出的通告列表作为它的API定义的一部分;其他人想拿这个知识做什么事的话,随便。
MVC is preserved,and everyone is happy. 你的应用程序框架可能已经提供了内置的通告系统,你也可以自己写一个(参考 ObserverPattern)。
4. 示例代码
(originally posted by Brian Kelly at http://www.techietwo.com/detail-6332577.html)
1 import wx
2
3 # an observable calls callback functions when the data has
4 # changed
5 #o = Observable()
6 #def func(data):
7 # print "hello", data
8 #o.addCallback(func)
9 #o.set(1)
10 # --| "hello", 1
11
12 class Observable:
13 def __init__(self, initialValue=None):
14 self.data = initialValue
15 self.callbacks = {}
16
17 def addCallback(self, func):
18 self.callbacks[func] = 1
19
20 def delCallback(self, func):
21 del self.callback[func]
22
23 def _docallbacks(self):
24 for func in self.callbacks:
25 func(self.data)
26
27 def set(self, data):
28 self.data = data
29 self._docallbacks()
30
31 def get(self):
32 return self.data
33
34 def unset(self):
35 self.data = None
36
37 class Model:
38 def __init__(self):
39 self.myMoney = Observable(0)
40
41 def addMoney(self, value):
42 self.myMoney.set(self.myMoney.get() + value)
43
44 def removeMoney(self, value):
45 self.myMoney.set(self.myMoney.get() - value)
46
47
48 class View(wx.Frame):
49 def __init__(self, parent):
50 wx.Frame.__init__(self, parent, -1, "Main View")
51 sizer = wx.BoxSizer(wx.VERTICAL)
52 text = wx.StaticText(self, -1, "My Money")
53 ctrl = wx.TextCtrl(self, -1, "")
54 sizer.Add(text, 0, wx.EXPAND|wx.ALL)
55 sizer.Add(ctrl, 0, wx.EXPAND|wx.ALL)
56 self.moneyCtrl = ctrl
57 ctrl.SetEditable(False)
58 self.SetSizer(sizer)
59 self.moneyCtrl = ctrl
60
61 def SetMoney(self, money):
62 self.moneyCtrl.SetValue(str(money))
63
64 class ChangerWidget(wx.Frame):
65 def __init__(self, parent):
66 wx.Frame.__init__(self, parent, -1, "Main View")
67 sizer = wx.BoxSizer(wx.VERTICAL)
68 self.add = wx.Button(self, -1, "Add Money")
69 self.remove = wx.Button(self, -1, "Remove Money")
70 sizer.Add(self.add, 0, wx.EXPAND|wx.ALL)
71 sizer.Add(self.remove, 0, wx.EXPAND|wx.ALL)
72 self.SetSizer(sizer)
73
74 class Controller:
75 def __init__(self, app):
76 self.model = Model()
77 self.view1 = View(None)
78 self.view2 = ChangerWidget(self.view1)
79 self.MoneyChanged(self.model.myMoney.get())
80 self.view2.add.Bind(wx.EVT_BUTTON, self.AddMoney)
81 self.view2.remove.Bind(wx.EVT_BUTTON, self.RemoveMoney)
82
83 self.model.myMoney.addCallback(self.MoneyChanged)
84 self.view1.Show()
85 self.view2.Show()
86
87 def AddMoney(self, evt):
88 self.model.addMoney(10)
89
90 def RemoveMoney(self, evt):
91 self.model.removeMoney(10)
92
93 def MoneyChanged(self, money):
94 self.view1.SetMoney(money)
95
96
97 app = wx.PySimpleApp()
98 Controller(app)
99 app.MainLoop()
5. 讨论