什麼是模型-視圖-控制(MVC)模型
在介紹模型-視圖-控制(Model-View-Controller,MVC)模型之前,我們先介紹一個概念:關注點分離原則。
關注點分離原則(Separation of Concerns, SoC) 是軟件工程相關的設計原則之一。 SoC原則背後的思想是將一個應用切分成不同的部分,每個部分只負責解決自身問題。分層設計中的層(數據訪問層、業務邏輯層和表示層等)即是關注點。
MVC模式: 又叫做 模型–視圖–控制,是 SoC原則 在面向對象中的實現。MVC 也被廣泛稱之爲架構模式;MVC 意爲把組織分爲三個部分:模型部分、視圖部分 和 控制器:
- 模型 :是組織的核心部分,負責訪問數據,管理應用狀態;
- 視圖 :是 模型 的外在表現,提供展示和交互功能;不涉及到對數據的處理,例如用戶的操作界面;
- 控制器 :是 模型 與 視圖 的連接器,模型 與 視圖 通過 控制器 進行通信;每個視圖應該有一個與之匹配的控制器;控制器 使得 模型與 視圖 可以解耦;
三者之間的關係如下圖所示:
MVC 模型的應用場景
在實際的使用過程中,Web 框架 Web2py 就是一個支持 MVC 模式的輕量級 Python 框架。Django 也是一個 MVC 框架。不過稍有不同,控制器 被稱爲 視圖,視圖 被稱爲 模板。 Django使用名稱 模型—模板—視圖( Model-Template-View, MTV)來替代MVC。MVC 模型的使用適合以下場景:
- 視圖 與 模型 的分離允許美工一心搞 UI8部分,程序員一心搞開發,不會相互干擾。
- 由於 視圖 與 模型 之間的松耦合,每個部分可以單獨修改/擴展,不會相互影響。例如,添加一個新視圖的成本很小,只要爲其實現一個 控制器 就可以了。
MVC 模型的優點
- MVC 模型最大的優點就是 松耦合:視圖 和 模型 是 松耦合,每一個部分可以單獨的修改和擴展,不會相互影響。
- 無需修改模型,就可以實現多個視圖 連接同一個 模型,因爲每一個 視圖 都有一個與之相應的 連接器,連接器 通常被設計成規模瘦小。如下圖所示:
使用 MVC 模型的注意事項
在從頭開始實現 MVC 時,請確保創建的 模型很智能,視圖很傻瓜,控制器很瘦;
- 模型很智能 的原則爲:
- 包含所有業務規則和邏輯;
- 處理應用的狀態;
- 訪問應用數據(包括數據庫、雲);
- 不依賴 UI;
- 視圖很傻瓜 的原則爲:
- 展示數據;
- 運行用戶與其交互;
- 僅做最小的數據處理;
- 不存儲任何數據;
- 不能直接訪問數據;
- 不包含業務規則和邏輯;
- 控制器很瘦 的原則爲:
- 在運行過程中,可以更新視圖和模型;
- 如果需要,在數據傳遞給模型和視圖之前進行處理;
- 不展示數據;
- 不直接訪問應用數據;
- 不包含業務規則和邏輯;
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元組中返回對應的名人名言(字符串); - 視圖有三個方法,分別是 `select_quote()、show()、 error()`。 - `select_quote()` 用於讀取用戶的選擇; - `show()` 用於在屏幕上輸出一句名人名言(或者輸出提示信息 `Not found`!); - `error()` 用於在屏幕上輸出一條錯誤消息; - 控制器負責協調。 - ` __init__()` 方法初始化模型和視圖。 - `run()` 方法校驗用戶提供的名言索引,然後從模型中獲取名言,並返回給視圖展示;
具體代碼如下所示:
# 模型
class QuoteModel:
# 從數據庫中讀取名人名言
def get_quote(self, n):
try:
value = quotes[n]
except IndexError as err:
value = 'Not found!'
return value
# 視圖
class QuoteTerminalView:
# 選擇名人名言
def select_quote(self):
return input('Which quote number would you like to see?')
# 顯示名人名言
def show(self, quote):
print('And the quote is: "{}"'.format(quote))
# 顯示錯誤
def error(self, msg):
print('Error: {}'.format(msg))
# 控制器
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()
if __name__ == '__main__':
main()
源碼在這裏
參考資料
- 《精通Python設計模式》