写给普通人的 MCP 入门指南(附案例)

写给普通人的 MCP 入门指南

一套适用于桌面程序、Web 应用、甚至是嵌入式开发的架构思维方式,让你从“会写功能”进阶到“会设计架构”。


第一部分:什么是 MCP?

MCP 是一种常用的软件架构模式,全称是 Model-Controller-Presenter。如果你以前听说过 MVC(Model-View-Controller)或 MVVM(Model-View-ViewModel),那么 MCP 就是它们的“近亲”,也是为了解决相似问题而生的一种结构。

它的核心目标,是让代码的结构更清晰、职责更明确、更容易维护

为什么要用 MCP?

对于初学者或中小项目来说,如果直接在一个 .py 文件或 .js 文件里把 UI 逻辑、数据操作、业务逻辑混在一起,很容易陷入“越写越乱”的局面。

MCP 提供一种“分工合作”的写法,把整个程序拆成三部分:

  • Model:负责数据处理和业务逻辑(比如从数据库读取、写文件、算结果)
  • Controller:控制整体流程,比如用户点了按钮后要执行什么
  • Presenter:负责更新视图,比如把数据展示在界面上,或返回给用户

这样的分工,让你即使三个月后再看代码,也知道谁负责干嘛。


第二部分:MCP 的核心组成

Model(模型层)

这是应用程序的“脑袋”,主要负责:

  • 业务逻辑(计算、判断)
  • 数据存储与读取(数据库、文件、本地缓存等)
  • 对外数据接口

比如,在一个记账应用中,Model 负责“记一笔账”、“删除一笔账”、“计算总金额”。

Controller(控制器)

Controller 是“指挥官”,接收用户的动作并决定:

  • 要调用哪个 Model 方法?
  • 调用完后要不要更新界面?

比如,用户点击了“保存”按钮,Controller 就调用 Model 的 save() 方法,然后通知 Presenter 更新页面。

Presenter(表现层)

Presenter 不处理业务、不管流程,只负责把数据“展示出来”:

  • 更新按钮、表格、图标等界面元素
  • 把 Model 的结果翻译成用户能看懂的信息

如果你用的是 Tkinter、PyQt、HTML+JS,Presenter 就是那些操作“界面控件”的代码。


第三部分:为什么 MCP 更适合普通人上手?

1. 对比 MVC

MVC 中的 View 和 Controller 通常互相交叉,不容易分清界限,新手容易“写着写着全都写进控制器”。

而 MCP 把 View 抽象成 Presenter,让展示逻辑更单纯。

2. 对比 MVVM

MVVM 的数据绑定很强大,但新手往往被“响应式”、“双向绑定”搞晕。

MCP 更加直白——想展示就写展示函数。

3. MCP 强调“责任分离”

不会因为改了界面导致数据逻辑出错。一个新手团队也能各司其职:

  • 一个写 Model
  • 一个写 Presenter
  • 一个管整体流程(Controller)

第四部分:开发步骤和工具推荐

推荐语言与框架:

  • Python + PyQt(桌面)
  • Python + Flask(Web)
  • JavaScript + React(前端)

一个简单的开发流程:

  1. 写好 Model 的接口,比如 save_data()read_data()
  2. 写 Presenter 的渲染逻辑,比如 show_result()update_table()
  3. 最后写 Controller:连接按钮事件、调用 Model 方法,再通知 Presenter 更新

工具推荐:

  • VS Code 或 PyCharm
  • Postman(调接口)
  • Git(版本控制)
  • draw.io(画 MCP 模块图)

第五部分:常见误区与实践建议

❌ 把所有代码都写在 Controller 里

会导致控制器越来越臃肿。建议逻辑交给 Model,展示交给 Presenter。

❌ Model 和 Presenter 相互调用

Model 不能知道 UI 的结构,Presenter 也不该处理数据逻辑。

✅ 写清楚每一层的职责

每一层只做自己该做的事,是 MCP 架构成功的关键。

✅ 为每个模块写注释或文档

普通人看代码更依赖注释,良好的文档可以帮助你自己或他人快速上手。


第六部分:6 个 MCP 案例实战

案例 1:命令行 Todo 应用(Python)

# model.py
class TodoModel:
    def __init__(self): self.todos = []
    def add(self, item): self.todos.append(item)
    def list(self): return self.todos
# presenter.py
class TodoPresenter:
    def show(self, todos):
        print("当前任务:")
        for i, todo in enumerate(todos):
            print(f"{i+1}. {todo}")
# controller.py
from model import TodoModel
from presenter import TodoPresenter

m = TodoModel()
p = TodoPresenter()

while True:
    cmd = input("请输入 add 或 list:")
    if cmd.startswith("add"):
        item = cmd[4:]
        m.add(item)
    elif cmd == "list":
        p.show(m.list())

案例 2:桌面计算器(PyQt5)

结构说明:

  • Model:执行加减乘除计算逻辑
  • Presenter:负责更新界面数字
  • Controller:连接按钮事件与计算函数
# model.py
def calculate(expr: str):
    try:
        return str(eval(expr))
    except:
        return "Error"
# presenter.py
from PyQt5.QtWidgets import QLabel

class Presenter:
    def __init__(self, label: QLabel):
        self.label = label

    def update_display(self, text: str):
        self.label.setText(text)
# controller.py
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QLabel
from model import calculate
from presenter import Presenter

class Calculator(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Calculator")
        self.layout = QVBoxLayout()
        self.display = QLabel("0")
        self.layout.addWidget(self.display)
        self.presenter = Presenter(self.display)

        self.expr = ""
        for btn_text in ["1", "2", "3", "+", "4", "5", "6", "-", "=", "C"]:
            btn = QPushButton(btn_text)
            btn.clicked.connect(lambda _, text=btn_text: self.on_click(text))
            self.layout.addWidget(btn)

        self.setLayout(self.layout)

    def on_click(self, text):
        if text == "=":
            result = calculate(self.expr)
            self.presenter.update_display(result)
            self.expr = ""
        elif text == "C":
            self.expr = ""
            self.presenter.update_display("0")
        else:
            self.expr += text
            self.presenter.update_display(self.expr)

if __name__ == "__main__":
    app = QApplication([])
    window = Calculator()
    window.show()
    app.exec_()

案例 3:天气小程序(Flask)

  • Model:请求天气 API 并解析
  • Controller:处理用户请求(查询城市)
  • Presenter:返回 HTML 页(渲染城市与天气信息)
# model.py
import requests

def get_weather(city):
    url = f"http://wttr.in/{city}?format=3"
    res = requests.get(url)
    return res.text
# presenter.py
from flask import render_template_string

HTML_TEMPLATE = """
城市名:
{% if weather %}

天气:{{ weather }}

{% endif %} """
def show_result(weather=""): return render_template_string(HTML_TEMPLATE, weather=weather)
# controller.py
from flask import Flask, request
from model import get_weather
from presenter import show_result

app = Flask(__name__)

@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        city = request.form.get("city")
        weather = get_weather(city)
        return show_result(weather)
    return show_result()

if __name__ == "__main__":
    app.run(debug=True)

案例 4:成绩管理系统(Tkinter)

  • Model:增删查改成绩信息
  • Controller:连接按钮和业务操作
  • Presenter:用 TreeView 显示学生信息
# model.py
students = []

def add_student(name, score):
    students.append((name, score))

def get_all_students():
    return students
# presenter.py
from tkinter import ttk

class Presenter:
    def __init__(self, tree: ttk.Treeview):
        self.tree = tree

    def update_list(self, data):
        for item in self.tree.get_children():
            self.tree.delete(item)
        for name, score in data:
            self.tree.insert("", "end", values=(name, score))
# controller.py
import tkinter as tk
from tkinter import ttk
from model import add_student, get_all_students
from presenter import Presenter

root = tk.Tk()
root.title("成绩管理")

entry_name = tk.Entry(root)
entry_score = tk.Entry(root)
entry_name.pack()
entry_score.pack()

tree = ttk.Treeview(root, columns=("姓名", "分数"), show="headings")
tree.heading("姓名", text="姓名")
tree.heading("分数", text="分数")
tree.pack()

presenter = Presenter(tree)

def on_add():
    name = entry_name.get()
    score = entry_score.get()
    add_student(name, score)
    presenter.update_list(get_all_students())

btn = tk.Button(root, text="添加", command=on_add)
btn.pack()

root.mainloop()


案例 5:商品浏览收藏(React)

  • Model:模拟商品数据 + 收藏状态
  • Controller:点击收藏逻辑控制
  • Presenter:用组件渲染商品卡片和状态
// App.jsx
import React, { useState } from 'react';

const products = [
  { id: 1, name: "手表" },
  { id: 2, name: "耳机" },
];

function App() {
  const [favorites, setFavorites] = useState([]);

  const toggleFavorite = (id) => {
    setFavorites((prev) =>
      prev.includes(id) ? prev.filter((i) => i !== id) : [...prev, id]
    );
  };

  return (
    <div>
      {products.map((p) => (
        <div key={p.id}>
          <h3>{p.name}</h3>
          <button onClick={() => toggleFavorite(p.id)}>
            {favorites.includes(p.id) ? "已收藏" : "收藏"}
          </button>
        </div>
      ))}
    </div>
  );
}

export default App;


案例 6:个人记账本(Kivy 或桌面)

  • Model:记录收入、支出、总余额
  • Controller:每次添加记录时更新账本
  • Presenter:渲染图表与列表
# model.py
records = []

def add_record(item, amount):
    records.append((item, amount))

def get_total():
    return sum(amount for _, amount in records)

def get_records():
    return records
# main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from model import add_record, get_total, get_records

class Book(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(orientation='vertical', **kwargs)
        self.item_input = TextInput(hint_text="项目")
        self.amount_input = TextInput(hint_text="金额")
        self.output = Label(text="当前余额:0")

        btn = Button(text="添加")
        btn.bind(on_press=self.on_add)

        self.add_widget(self.item_input)
        self.add_widget(self.amount_input)
        self.add_widget(btn)
        self.add_widget(self.output)

    def on_add(self, _):
        add_record(self.item_input.text, float(self.amount_input.text))
        total = get_total()
        self.output.text = f"当前余额:{total}"

class MyApp(App):
    def build(self):
        return Book()

if __name__ == "__main__":
    MyApp().run()


第七部分:总结与进阶

MCP 架构是一种既适合新手、又能支持复杂应用的架构方式。

它的优势在于:

  • 职责清晰,团队协作容易
  • 逻辑清楚,维护简单
  • 适配广泛,从命令行到 Web 都能用

进阶建议:

  • 阅读开源项目中的 MCP 分层方式(如 Flask 项目)
  • 自己用 MCP 写一个完整项目并迭代维护
  • 学会写单元测试覆盖 Model、集成测试 Controller

最后,写好架构的秘诀永远是:“代码是给人读的,顺带让机器运行”。


希望这篇指南能成为你编程路上的结构启蒙!


如果你也喜欢这篇文章:

点个赞 / ⭐ 收藏 / 留个言支持我吧!

你可能感兴趣的:(入门指南,个人开发,python,开发语言)