見出し画像

DP.12:処理担当を3つに分離 - MVCパターン -【Python】

【1】MVCパターン

MVCパターンはプログラムを

Model:アプリで使うデータに取得関連の処理部分
View:Modelに格納された値をコンソールやブラウザ上に表示する処理部分
Controller:ModelとViewの間に入って実行順を制御する部分

の3つに分離してプログラムを作成していく書き方。

MVCはデザインパターンというよりもアーキテクチャパターンだが、MVCをベースに変形を加えたもの(※)がたくさんあり、知っておいて損はないのでMVCの挙動やプログラムの作りをざっとまとめておく。
(※DjangoのMTV:Model-Template-View、iPhoneSDK、 Android、QTでみかけるMVA:Model-View-Adapter、MVP:Model-View-Presenter、等)

【2】MVCの挙動やプログラムの作り

MVCの挙動やプログラムの作りについて、もう少し具体的にざっとまとめておく。

■Modelが行うこと

・DBやクラウドデータなどアプリで使うデータへのアクセス処理をもつ
・様々なデータ構造(変数やクラス)に値をセットしたり、管理したりする

■Viewが行うこと

・出力処理を担当する(Modelが格納した値や取得した値をコンソールやブラウザ上に表示するための処理を担当する)
・画面担当=ユーザが操作する部分を担当する(webアプリならhtml部分など)
・DBやクラウドデータなどのアプリで使うデータには直接アクセスしない

■Controllerが行うこと

・ユーザがViewで行った操作をうけてModel側の処理を動かす
・Model側の内容が変わったら、Viewを更新する処理を動かす
⇒ ようはControllerに渡されたデータを必要に応じて体裁を整えて、ModelやViewの処理に渡す、ということ。
・DBやクラウドデータなどのアプリで使うデータには直接アクセスしない


【3】例:名言・格言(quotes)プリンター

例として「名言・格言(quotes)を出力するプログラム」をMVC風のプログラムで作成してみる。この具体的には

「ターミナル=View」とみなして、まずはユーザが「View(ターミナル)」上に番号を入力する。

「事前に用意しておいた名言・格言データ(quotesオブジェクト)」から対応するデータを取得して「View(ターミナル)」上に出力する

■動作イメージ

画像1

ただし、

View(ターミナル)への表示処理」、「Modelの処理(データの取得)」は分離し、「Controllerの処理」でコールする

という動作にしてMVC風のプログラムにする。

■Controllerを経由したプログラムのイメージ

# Model
class QuoteModel:
  # データ取得
  def get_quote(self,n):
   ・・・(略)・・・


# View
class QuoteTerminalView:

  # View(ターミナル)で入力受付
  def select_number(self):
   ・・・(略)・・・
   
  # Viewに表示
  def show(self, quote):
   ・・・(略)・・・


   
# Controller
class QuoteTerminalController:
   def __init__(self):
       self.model = QuoteModel()
       self.view = QuoteTerminalView()

   def run(self):

       # view側でコンソールの入力を促す
       n = self.view.select_number()


       # Controller ⇒ modelに処理を引き渡す( ⇒ modelの値更新結果をControllerが受け取る)
       quote = self.model.get_quote(n) 

       # Controller(を経由してmodelの値を) ⇒ Viewに引き渡す
       self.view.show(quote) # view(コンソール上に出力)




#### プログラムの実行 ####
controller = QuoteTerminalController()
controller.run()  

▲「Controllerオブジェクト」から「Viewオブジェクト」や「Modelオブジェクト」を呼び出すイメージ。

【4】全体コード

quotes = (
'Life was like a box of chocolates. You never know what you’re gonna get.', 
'Amat victoria curam', 
'After all, tomorrow is another day.', 
'There’s no place like home!',
'Facts are stubborn things.'
)

# Model :Viewにおいて必要な値取得や処理などを行うオブジェクト
# 今回はデータの保持・管理はしていない
class QuoteModel:
   def get_quote(self,n):
       try:
           value = quotes[n]
       except IndexError as err:
           value = 'Not found'
       
       return value

# View コンソールへの出力を行うオブジェクト
class QuoteTerminalView:
   def show(self, quote):
       print(f'And the quote is : {quote}')
   
   def error(self, msg):
       print(f'Error: {msg}')

   def select_number(self):
       return input(f'which quote number would you like to see ? select 0 ~ {len(quotes)-1} : ')


# Controller:MVC系プログラムのエントリポイント相当
class QuoteTerminalController:
   def __init__(self):
       self.model = QuoteModel()
       self.view = QuoteTerminalView()

   def run(self):

       # 前処理部分(View(コンソール上)からの入力待ち相当)
       valid_input = False
       while not valid_input:
           try:
               n = self.view.select_number() # 入力要請などであってもView側でやらせる
               
               # コントローラ内でmodelやviewに引き渡すためのデータ処理を実施
               n = int(n)
               valid_input = True
           except ValueError as err:
               self.view.error(f"Incorrect index '{n}'")


       # Controller ⇒ modelに処理を引き渡す( ⇒ modelの値更新結果をControllerが受け取る)
       quote = self.model.get_quote(n) 

       # Controller(を経由してmodelの値を) ⇒ Viewに引き渡す
       self.view.show(quote) # view(コンソール上に出力)

### 動作確認
def main():
   controller = QuoteTerminalController()
   while True:
       controller.run()
       break


if __name__ == '__main__':
   main()

【実行結果例】

$ python main.py
which quote number would you like to see ? select 0 ~ 4 : 1
And the quote is : Amat victoria curam

▲なお、プログラムを見てわかる通り、MVCという言い回しのため、M⇒V⇒Cの順でプログラムが動くような感じがするがそうではない。
実際にプログラムを追いかけると、

(プログラム起動のトリガー:View部分) 
  ↓
 [ Controller ]
  ↓
(必要があればModel、データアクセスがない等の場合によっては省略)
  ↓
 [ View ]

といった感じで最低限ControllerとViewがあればよく、プログラムの起動トリガーになるViewやControllerから動作を追いかけていく方がソースコードは理解しやすくなるかもしれない。

【補足】:フルスクラッチはしない方がいい
今回は練習としてMVCモデル風のプログラムを作成した。勉強として一からから作ることは構わないがフルスクラッチでMVCモデルを構築して何かしらのアプリをつくることはやめた方がいい。

よほどの専門家や熟練者でない限りは、十分な完成度をもつMVCモデルを構築できる可能性は低い。
できるだけモダンなフレームワークを積極的に活用して効率的な開発をしよう。

【5】おまけ:Soc(Separation of concerns)/関心の分離

ソフトウェア工学の世界では「Soc(Separation of concerns)/関心の分離」という考え方がある。
ざっくりいうと、アプリケーションを「異なるセクション(関心毎)」に分割して、それぞれのセクションで行うべき処理を実装する、というもの。

例えば、

データアクセス層:Model相当
ビジネスロジック層:Controller相当
プレゼンテーション層:View相当

にわける。こうするとアプリケーションの開発と保守がしやすくなる、というもの。


もっと応援したいなと思っていただけた場合、よろしければサポートをおねがいします。いただいたサポートは活動費に使わせていただきます。