【问题标题】:Update progress bar - MVP pattern更新进度条 - MVP 模式
【发布时间】:2020-01-25 10:29:54
【问题描述】:

我正在研究 MVP 模式,但很难遵循这些原则来实时更新进度条。据我了解,Presenter 检查模型中是否有任何更新,然后输出结果,因此模型中没有 Presenter 的实例化,只有 Presenter 应该实例化模型和视图。

我的问题是:我应该如何按照 MVP 原则更新进度条? 我当然可以从 Model 中调用 presenter.update_progress_bar(i, total),但是这样就违反了 MVP 原则。

这是一个最小的工作示例:

PS:目前,我正在使用 CLI。

/main.py

import modules

def main():
    modules.View(modules.Presenter).run()

if __name__ == "__main__":
    main()

/modules/__init__.py

from modules.Model.Model import Model
from modules.Model.progressbar import ProgressBar
from modules.View.View import View
from modules.Presenter.Presenter import Presenter

/modules/Model/Model.py

class Model:
def __init__(self):
    pass

def long_process(self):
    import time
    for i in range(10):
        time.sleep(0.1)
        print("Update the progress bar.")
    return True

/modules/Model/progressbar.py

# MIT license: https://gist.github.com/vladignatyev/06860ec2040cb497f0f3
import sys
class ProgressBar:
def progress(count, total, status=''):
    bar_len = 60
    filled_len = int(round(bar_len * count / float(total)))

    percents = round(100.0 * count / float(total), 1)
    bar = '=' * filled_len + '-' * (bar_len - filled_len)

    sys.stdout.write('[%s] %s%s ...%s\r' % (bar, percents, '%', status))
    sys.stdout.flush()

/modules/View/View.py

import sys
class View:
def __init__(self, presenter):
    self.presenter = presenter(self)

def run(self):
    self.presenter.long_process()

def update_progress_bar(self, msg):
    sys.stdout.write(msg)

def hide_progress_bar(self, msg):
    sys.stdout.write(msg)

def update_status(self, msg):
    print(msg)

/modules/Presenter/Presenter.py

class Presenter:
def __init__(self, view):
    import modules
    self.model = modules.Model()
    self.view = view

def long_process(self):
    if self.model.long_process():
        self.view.update_status('Long process finished correctly')
    else:
        self.view.update_status('error')

def update_progress_bar(self, i, total):
    from modules import ProgressBar
    ProgressBar.progress(i, total)
    self.view.update_progress_bar(ProgressBar.progress(i, total))

def end_progress_bar(self):
    self.view.end_progress_bar('\n')

我能做到:

class Model:
def __init__(self, presenter):
    self.presenter = presenter  # Violation of MVP

def long_process(self):
    import time
    for i in range(10):
        time.sleep(0.1)
        self.presenter.update_progress_bar(i, 10)  # Violation of MVP
        print("Update the progress bar.")
    return True

但这是错误的,因为模型现在实例化了 Presenter。有什么建议吗?

【问题讨论】:

    标签: python python-3.x oop progress-bar mvp


    【解决方案1】:

    使用回调:

    import time
    
    class Model:
        def long_process(self, notify=lambda current, total: None):
            for i in range(10):
                time.sleep(0.1)
                notify(i, 10)  
            return True
    
    
    
    class Presenter:
        def long_process(self):
            result = self.model.long_process(lambda c, t: self.update_progress_bar(c, t)):
            if result:
                self.view.update_status('Long process finished correctly')
            else:
                self.view.update_status('error')
    

    这使您的模型独立于客户端代码,同时仍然允许它(我的意思是模型)通知它的调用者。

    顺便说一句,你的代码中有很多东西是完全不符合 Python 的:

    1/ 您不必将每个类放在不同的模块中(它实际上在 Python 中被视为反模式),在嵌套子模块中甚至更少(Python Zen:“扁平比嵌套更好”)。

    2/ 当普通函数足够时,您不必使用类(提示:Python 函数是对象...实际上,Python 中的所有内容都是对象)-您的 ProgressBar 类没有状态,只有一个方法,所以它可能只是一个普通的函数(Python Zen:“简单胜于复杂”)。

    3/ imports should be at the top of the module,不在函数中(如果您必须将它们放在函数中以解决循环依赖问题,那么正确的解决方案是重新考虑您的设计以避免循环依赖)。

    4/module names should be all_lower

    【讨论】:

    • 感谢 Bruno 的建议,它按预期工作。搜索“回调函数 python”我可以找到我需要的东西。必须使用正确的术语进行搜索。关于你对pythonic代码的建议:(1)申请几千行的代码?并且(3)在做应该以串行方式完成的事情时很难避免。无论如何,我会记住这一点,谢谢您指出。
    • 1/ 适用于您编写的任何代码 - 但这并不意味着您也应该将所有代码放在一个文件中,只是您应该首先努力逻辑地组织它 i>(高内聚,低耦合)首先,并且只有当它实际上变得太大而无法满足您的口味时才将模块“拆分”为子模块。另请注意,“千行”课程是您的班级责任过多的肯定症状。请记住,普通函数是可以的,因此您可以在实用程序函数中分解出低级、与状态无关的代码,而只在您的类中保留高级和与状态相关的代码。
    • 恐怕我在这里帮不上什么忙... wrt/模块化,基本规则是high cohesion and low coupling,但这里没有灵丹妙药,主要是使用您自己的判断,并且,嗯,体验一下。但也不要想太多 - 尽量让事情尽可能简单,从简单的模块开始,以后您总是可以在以后不破坏客户端代码的情况下将它们重构到子包中。只需在会话结束时花点时间重新阅读您所做的工作,并检查是否需要进行一些重构。
    • (提示:您可以通过在子包的__init__.py 中导入公共名称来将模块转换为子包而不会破坏客户端代码)
    • wrt/ 词汇,好吧,我想阅读很多(手册、教程、维基百科文章、论坛、问题和答案等)是关键。至少我是这样学习的,而且我仍然在学习 ;-)
    猜你喜欢
    • 2012-09-27
    • 1970-01-01
    • 2013-03-08
    • 2020-11-05
    • 2014-04-09
    • 2017-03-26
    • 1970-01-01
    • 1970-01-01
    • 2013-06-10
    相关资源
    最近更新 更多