mor*_*des 30 user-interface finite-automata
我正在寻找解决我的前端小部件代码的方法.有人建议使用有限状态机来思考我正在做的事情.我知道一个状态机模式可以适用于几乎任何问题.我想知道是否有一些经验丰富的UI程序员实际上养成了这个习惯.
所以,问题是 - 你们中的任何一个UI程序员都会在你的工作中考虑状态机吗?如果是这样,怎么样?
谢谢,-Morgan
Mic*_*rdt 30
我目前正在使用(专有)框架,它非常适合UI作为状态机的范例,它肯定可以减少(但不能消除)UI元素之间复杂和无法预见的交互问题.
主要的好处是它允许您以更高的粒度在更高的抽象级别进行思考.而不是想"如果按下按钮A然后组合框B被锁定,文本字段C被清除并且按钮D被解锁",您认为"按下按钮A将应用程序置于CHECKED状态" - 并且进入该状态意味着某些事情发生.
但是,我不认为将整个UI建模为单个状态机是有用的(甚至是可能的).相反,通常有许多较小的状态机,每个状态机处理UI的一部分(由几个在概念上相互作用并在一起的控件组成),以及一个(可能不止一个)处理更多基本问题的"全局"状态机.
Ken*_*Fox 10
状态机通常太低级别,无法帮助您考虑用户界面.它们为UI工具包提供了一个很好的实现选择,但是在正常的应用程序中有太多的状态和转换需要您手动描述它们.
我喜欢考虑具有延续的UI.(谷歌它 - 这个术语足够具体,你可以得到很多高质量的点击.)
而不是我的应用程序处于由状态标志和模式表示的各种状态,我使用continuation来控制应用程序下一步做什么.用一个例子来解释是最容易的.假设您要在发送电子邮件之前弹出确认对话框.第1步构建一封电子邮件.第2步得到确认.第3步发送电子邮件.大多数UI工具包都要求您在每个步骤之后将控制权传递回事件循环,如果您尝试使用状态机来表示它,则会使其变得非常难看.通过延续,您不会考虑工具包强加给您的步骤 - 这是构建和发送电子邮件的所有过程.但是,当进程需要确认时,您将继续捕获应用程序的状态,并将该继续执行到确认对话框上的"确定"按钮.按下确定后,
在编程语言中,Continuations相对较少,但幸运的是,你可以使用闭包获得一个穷人的版本.回到电子邮件发送示例,在您需要获得确认时,您将剩余的进程写为闭包,然后将该闭包交给OK按钮.闭包类似于匿名嵌套子例程,它们在下次调用时记住所有局部变量的值.
希望这能给你一些新的思考方向.我将尝试稍后使用真实代码回来向您展示它是如何工作的.
更新:以下是Ruby中Qt的完整示例.有趣的部分在ConfirmationButton和MailButton中.我不是Qt或Ruby专家,所以我很欣赏你们所能提供的任何改进.
require 'Qt4'
class ConfirmationWindow < Qt::Widget
def initialize(question, to_do_next)
super()
label = Qt::Label.new(question)
ok = ConfirmationButton.new("OK")
ok.to_do_next = to_do_next
cancel = Qt::PushButton.new("Cancel")
Qt::Object::connect(ok, SIGNAL('clicked()'), ok, SLOT('confirmAction()'))
Qt::Object::connect(ok, SIGNAL('clicked()'), self, SLOT('close()'))
Qt::Object::connect(cancel, SIGNAL('clicked()'), self, SLOT('close()'))
box = Qt::HBoxLayout.new()
box.addWidget(label)
box.addWidget(ok)
box.addWidget(cancel)
setLayout(box)
end
end
class ConfirmationButton < Qt::PushButton
slots 'confirmAction()'
attr_accessor :to_do_next
def confirmAction()
@to_do_next.call()
end
end
class MailButton < Qt::PushButton
slots 'sendMail()'
def sendMail()
lucky = rand().to_s()
message = "hello world. here's your lucky number: " + lucky
do_next = lambda {
# Everything in this block will be delayed until the
# the confirmation button is clicked. All the local
# variables calculated earlier in this method will retain
# their values.
print "sending mail: " + message + "\n"
}
popup = ConfirmationWindow.new("Really send " + lucky + "?", do_next)
popup.show()
end
end
app = Qt::Application.new(ARGV)
window = Qt::Widget.new()
send_mail = MailButton.new("Send Mail")
quit = Qt::PushButton.new("Quit")
Qt::Object::connect(send_mail, SIGNAL('clicked()'), send_mail, SLOT('sendMail()'))
Qt::Object::connect(quit, SIGNAL('clicked()'), app, SLOT('quit()'))
box = Qt::VBoxLayout.new(window)
box.addWidget(send_mail)
box.addWidget(quit)
window.setLayout(box)
window.show()
app.exec()
Run Code Online (Sandbox Code Playgroud)
小智 9
不需要将UI建模为状态机; 它是显示的对象,它可以有助于建模为状态机.然后,您的UI变为(过度简化)一组事件处理程序,用于各种对象中的状态更改.
这是一个变化:
DoSomethingToTheFooObject();
UpdateDisplay1(); // which is the main display for the Foo object
UpdateDisplay2(); // which has a label showing the Foo's width,
// which may have changed
...
Run Code Online (Sandbox Code Playgroud)
至:
Foo.DoSomething();
void OnFooWidthChanged() { UpdateDisplay2(); }
void OnFooPaletteChanged() { UpdateDisplay1(); }
Run Code Online (Sandbox Code Playgroud)
考虑到您正在显示的数据中的哪些更改应该导致重新绘制可以澄清,无论是从客户端UI端还是服务器Foo端.
如果你发现,当Foo的状态发生变化时,可能需要重新绘制100个UI内容,当调色板发生变化时,所有这些都需要重新绘制,但是当宽度发生变化时,只需要重新绘制10个,它可能会暗示某些事件/状态改变Foo应该发出信号.如果您发现有一个大型事件处理程序OnFooStateChanged()通过检查许多Foo的属性来查看已更改的内容,为了最大限度地减少UI更新,它会提示有关Foo事件模型的粒度的信息.如果您发现想要编写一个小的独立UI小部件,您可以在UI中的多个位置使用它,但它需要知道Foo何时更改并且您不希望包含Foo实现带来的所有代码,它建议一些与您的UI相关的数据组织,是你的表示层,比"我的表单类中的所有代码"更严肃.
-PC
有一本关于这个主题的书.可悲的是,它的绝版和罕见的二手产品非常昂贵.
Constructing the User Interface with Statecharts
by Ian Horrocks, Addison-Wesley, 1998
Run Code Online (Sandbox Code Playgroud)
我们只是在谈论 Horrocks用 Statecharts 构建用户界面,二手价格从 250 美元到近 700 美元不等。我们的软件开发经理将其评为他拥有的最重要的书籍之一(遗憾的是,他住在世界的另一端)。
Samek 关于状态图的书籍大量借鉴了这项工作,尽管在一个略有不同的领域,据报道并不那么清晰。“嵌入式系统的 C/C++ 事件驱动编程中的实用 UML 状态图”也可在Safari 上找到。
Horrocks 被大量引用 - ACM 门户上有20 篇论文,因此如果您可以访问那里,您可能会发现一些有用的内容。
有一本书和软件FlashMX for Interactive Simulation。他们有一个关于状态图的 PDF示例章节。
使用 UML 的对象、组件和框架:催化(SM)方法有一章关于行为模型,其中包括大约十页使用状态图的有用示例(我注意到它可以非常便宜地二手获得)。它相当正式和沉重,但该部分很容易阅读。
呈现给用户的每个界面项都可以从当前状态转到另一种状态。您基本上需要创建一个映射,说明哪些按钮可以导致哪些其他状态。
此映射将允许您查看未使用的状态或多个按钮或路径可以导致相同状态而没有其他状态的状态(可以组合的状态)。