3

我在 Python 中制作“比简单脚本和东西更大”的应用程序时遇到了一些问题。我有一个名为 WinMain 的类,一个名为 Engine 的类,一个名为 State,另一个名为 Element。它是这样布置的:

WinMain 是程序的主类,它有一个 mainLoop 函数和其他各种东西。

引擎是一个包含图像和渲染代码等数据的对象。

State 是 Engine 持有的一个对象,每次调用 Engine 的 step 函数时都会调用它的 update 方法。

元素是 State 持有的对象,例如要渲染的 gui 按钮和图片。

由于我的渲染功能在引擎中,我将如何让我的元素(由状态持有)来渲染东西?我想我可以给每个元素引擎的实例,但这似乎有点 hacky,因为我必须做这样的事情:( picture = Element(engine, (0, 0), 'gui_image')不好)

我宁愿做picture = Element((0, 0), 'gui_image')一些如何让 Element 使用 Engine 渲染事物的方法。

这似乎是我开始的大多数项目的主要结构问题,我似乎找不到解决方法(除了通过类和函数的参数传递大量变量)。我该怎么做?

4

3 回答 3

4

引擎是一个包含图像和渲染代码等数据的对象

这听起来像一个上帝类:它们在 GUI 代码中很常见,而您遇到的问题是它的常见效果。

Engine中不同东西之间的概念关系是什么?它是否需要紧密耦合,或者引擎是否可以在状态、渲染器和其他一些东西之间进行协调?

然而,这一点会发生故障,通过状态机传递引擎(或渲染器或其他)是避免当前架构出现全局(或单例)的唯一方法。


打破这种依赖关系的常用方法是使用类似 MVC(模型/视图/控制器)模式的东西。具体来说,听起来 Engine 已经大致是一个 Controller,State 和 Elements 已经覆盖了 Model 部分。然而,引擎、状态和元素也在处理渲染/呈现部分:这引入了耦合。

如果您能找到一种方法将状态更改发布给某个观察者(故意保持模糊,因为我们不希望状态逻辑过多依赖于观察者的细节),您可以让渲染器监听状态(模型)更新并绘制它们。现在,观察者可以是任何你想要的东西,所以它很容易模拟,并且它也与引擎解耦。

于 2012-10-11T22:38:39.360 回答
1

显而易见的事情是将您的“引擎”作为每个“元素”对象中的属性(或在其中调用渲染方法时作为值传递) - 为此,您的“状态”对象必须知道Engine 实例,并在实例化时将其传递给每个 Element,或者在每个渲染方法调用时传递它——这似乎让您感到困扰。

同样,“状态”要知道“引擎”,它必须知道它 - 无论是在创建时,还是在调用将触发渲染(“状态”)的操作时。

Python 的动态特性确实有办法让你不必每次都显式地编写所有这些 - 是的 - 但你必须小心不要把它埋得太多,这样你的代码就无法维护了。让我们看看我是否可以在这里归结为一些例子:

class WinMain(object):
    def __init__(self):
        self.engine = Engine()

    def loop_once(self):
        self.engine.step()

class Engine(object):
    def __init__(self):
        self.state = State()

    def step(self):
        self.state.update(self)

    def render(self, obj):
        print (obj)

class State(object):
    def __init__(self):
        self.element = Element()

    def update(self, engine):
        self.element.render(engine)

class Element(object):
    def render(self, engine):
        engine.render(self)


if __name__ == "__main__":
    win = WinMain()
    win.loop_once()

此代码仅在更新时传递 Engine 对象——这比将引擎固定为状态属性或元素属性更有趣,因为它允许 Element 实例完全属于不同的引擎。它还将“引擎”参数与元素创建时间分离 - 您抱怨您根本不想使用引擎的部分 - 所以它可以不那么混乱。

无论哪种方式,您都必须让“元素”以某种方式了解它们的“对象”——如果您不明确传递参数,则必须隐式地进行(即让“引擎”实例在“状态”中已知)和“元素”以某种方式)。一种方法是编写基类或元类以在实例化时执行帧自省,并将调用上下文中的“self”变量绑定到新对象中的属性。这种隐含的做法闻起来很糟糕。:-) 另一种方法是将对象的关系保存在单独的数据结构中,并使用您的方法可以导航的 DOM 接口。但是,无论如何,您都必须进行额外的调用来注释关系(也许额外的调用可以进行自省的基类或元类,如上所示)。

再想一想——Python 应用服务器“zope”使用“acquisition”解决了这个问题:zope 应用程序中的所有对象都位于树中——当人们试图从没有它的对象中检索属性时(例如,在尝试调用元素的“render”方法时),获取机制在树上“向上”搜索该属性,直到找到支持该方法的父级。然后调用它,并获取原始对象作为参数。这对您来说是一个可能的解决方案,但不仅需要实现获取机制的基类,

这里有一个简单的“收购”实现来简化你的案例:

class WinMain(object):
    def __init__(self):
        self.struct = []
        self.engine = Engine()

    def loop_once(self):
        self.engine.step()


class TraverseError(AttributeError):
    pass

class AquisitionBase(object):
    tree = {}      

    def __getattr__(self, attr):
        # __getattr__ is only invoked when the original object does not have
        # attr by itself
        try:
            return getattr(self.__class__.tree[id(self)], attr)
        except (IndexError, AttributeError), error:
            raise TraverseError(error)

    def __setattr__(self, attr, obj):
        cls = self.__class__
        # Anotate parent:
        cls.tree[id(obj)] = self
        return super(AquisitionBase,self).__setattr__(attr, obj)


class Engine(AquisitionBase):
    def __init__(self):
        self.state = State()

    def step(self):
        self.state.update()

    def render(self, obj):
        print (obj)

class State(AquisitionBase):
    def __init__(self):
        self.element = Element()

    def update(self):
        self.element.render(self.element)

class Element(AquisitionBase):
    pass


if __name__ == "__main__":
    win = WinMain()
    win.loop_once()

请注意,如果您有包含“元素”的列表或字典,则可以通过创建知道此方案的列表和字典的子类来克服有关容器的问题,并基本上实现一个__setitem__注释父级的方法对象也被归于那里。

本质上——不管怎样,考虑一下,传递“引擎”参数听起来更好——即使 Python 可以满足你的要求。

于 2012-10-11T22:39:34.347 回答
0

我不知道 Python 如果(何时)我的语法错误,请原谅,但一般的 OO 答案是将engine对象传递给执行渲染的元素的函数。

所以在Engine's step 函数中你会有类似的东西:

state.Update(this)      - so State gets a reference to the engine.

如果您愿意,还可以State参考Engine它的创建时间。

然后在state.Update()你会做的方法中:

element.Render(engine)

这可能在一个循环中,您可以在其中迭代state对象持有的元素集合。

于 2012-10-11T21:32:31.603 回答