Как насчет:
public override int GetHashCode()
{
return string.Format("{0}_{1}_{2}", prop1, prop2, prop3).GetHashCode();
}
Предполагая, что производительность не является проблемой:)
blockquote>
Это не 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
...
к:
Foo.DoSomething();
void OnFooWidthChanged() { UpdateDisplay2(); }
void OnFooPaletteChanged() { UpdateDisplay1(); }
Взгляды о том, какие изменения в данных Вы отображаете, должны вызвать то, что перекрашивание может разъяснять, и от клиента сторона UI и от сервера сторона Foo.
, Если Вы находите, что, 100 штук UI, которые, возможно, должны быть перекрашены, когда изменения состояния Foo, все они должны быть перерисованы, когда палитра изменяется, но только 10, когда ширина изменяется, она могла бы предложить что-то о том, о чем должны сигнализировать события/изменения состояния Foo. Если Вы находите, что у Вас есть большой обработчик событий OnFooStateChanged (), который посылает багажом свойства многого Foo для наблюдения то, что изменилось в попытке минимизировать обновления UI, он предлагает что-то о гранулярности модели событий Foo. Если Вы находите, что хотите записать немного автономного виджета UI, который можно использовать в нескольких местах в UI, но что он должен знать, когда Foo изменяется, и Вы не хотите включать весь код, который реализация Foo приносит с ним, он предлагает что-то об организации Вас данные относительно Вашего UI, где Вы используете классы по сравнению с интерфейсами, и т.д. Существенно, он заставляет Вас думать более серьезно о том, что Ваш уровень представления, более серьезно, чем "весь код в моих классах формы".
- ПК
Я в настоящее время работаю с (собственной) платформой, которая предоставляет себя хорошо UI-as-state-machine парадигме, и она может определенно уменьшить (но не устранить), проблемы со сложными и непредвиденными взаимодействиями между элементами UI.
основное преимущество - то, что это позволяет Вам думать в более высоком уровне абстракции при более высокой гранулярности. Вместо того, чтобы думать, "Если кнопка A нажимается тогда, заблокировано поле комбинированного списка B, текстовое поле C очищено и и Кнопка D разблокирована", Вы думаете, что "Нажимающая кнопка A помещает приложение в ПРОВЕРЕННОЕ состояние" - и ввод того состояния означает, что определенные вещи происходят.
я не думаю, что это полезно (или даже возможно) смоделировать весь UI как единственный конечный автомат, все же. Вместо этого обычно существует много конечных автоматов меньшего размера, что каждый обрабатывает одну часть UI (состоящий из нескольких средств управления, которые взаимодействуют и принадлежат вместе концептуально), и один (возможно, больше чем один) "глобальный" конечный автомат, который обрабатывает более основные проблемы.
Эй Morgan, мы создаем пользовательскую платформу в AS3 здесь в Radical и используем парадигму конечного автомата для включения любого фронтэнда действие UI.
у Нас есть установка конечного автомата для всех событий кнопки, всех событий дисплея и т.д.
AS3, будучи управляемым событиями языком, делает это очень привлекательной опцией.
, Когда определенные события пойманы, состояния кнопок / экранные объекты автоматически изменяются.
Наличие обобщенного набора состояний могло определенно помочь de-spaghttify Ваш код!
Каждый интерфейсный объект это представлено пользователю, может перейти к другому состоянию от текущего. В основном необходимо создать карту того, что кнопка может привести к какой другое состояние.
Это отображение позволит Вам видеть неиспользованные состояния или, куда несколько кнопок или путей могут привести к тому же состоянию и никаким другим (которые могут быть объединены).
Конечный автомат - что-то, что позволяет коду работать с другими конечными автоматами. Конечный автомат является просто логикой, которая имеет память прошедших событий.
Поэтому люди являются конечными автоматами, и часто они ожидают, что их программное обеспечение будет помнить то, что они сделали в прошлом так, чтобы они могли продолжиться.
, Например, можно поместить весь обзор на одной странице, но люди более довольны несколькими меньшими страницами вопросов. То же с регистрациями пользователя.
Так конечный автомат имеют партия из применимости для пользовательских интерфейсов.
Они должны быть поняты прежде чем быть развернутым, тем не менее, и весь дизайн должен быть завершен, прежде чем код будет написан - конечный автомат может, быть, и будет неправильно использован, и если у Вас нет очень четкого представления о том, почему Вы используете один, и какова цель, можно закончить оказывающиеся в худшем положении, чем другие методы.
-Adam
Конечные автоматы являются слишком обычно низким уровнем, чтобы помочь Вам думать о пользовательском интерфейсе. Они делают хороший выбор реализации для инструментария UI, но существует только слишком много состояний и переходов для описания в нормальном приложении для Вас для описания их вручную.
мне нравится думать о UIs с продолжениями. (Google это - термин является достаточно конкретным, что Вы получите много высококачественных хитов.)
Вместо моих приложений, находящихся в различных состояниях, представленных флагами состояния и режимами, я использую продолжения для управления тем, что приложение делает затем. Является самым легким объяснить с примером. Скажите, что Вы хотите открыть диалоговое окно подтверждения прежде, чем послать электронное письмо. Шаг 1 создает электронное письмо. Шаг 2 получает подтверждение. Шаг 3 посылает электронное письмо. Большинство инструментариев UI требует, чтобы Вы пасовали назад управление к циклу событий после каждого шага, который делает это действительно ужасным, при попытке представить его с конечным автоматом. С продолжениями Вы не думаете с точки зрения шагов силы инструментария на Вас - это - весь один процесс создания и отправки электронного письма. Однако, когда для процесса нужно подтверждение, Вы получаете состояние своего приложения в продолжении и вручаете то продолжение кнопке OK на диалоговом окне подтверждения. Когда хорошо нажимается, Ваше приложение продолжается от того, где это было.
Продолжения относительно редки на языках программирования, но к счастью можно получить вид версии бедного человека с помощью закрытий. Возвращение к электронной почте, отправляющей пример, в точке, необходимо получить подтверждение, которое Вы пишете остальной части процесса как закрытие и затем вручаете то закрытие кнопке OK. Закрытия являются видом подобных анонимных вложенных подпрограмм, которые помнят значения всех Ваших локальных переменных в следующий раз, когда их называют.
, Надо надеяться, это дает Вам некоторые новые направления для размышления о. Я попытаюсь возвратиться позже с реальным кодом, чтобы показать Вам, как он работает.
Обновление: вот полный пример с QT в Ruby. Интересные части находятся в 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()
Не действительно проблема UI, чтобы быть честным.
я сделал бы следующее:
На эту тему есть книга. К сожалению, он распродан, а доступные редко используемые б / у стоят очень дорого.
Constructing the User Interface with Statecharts
by Ian Horrocks, Addison-Wesley, 1998
Я получил презентацию о шаблоне, который я назвал "State First".
Это комбинация MPV / IoC / FSM, и я успешно использовал ее в .Net / WinForms, .Net / Silverlight и Flex (на данный момент).
Вы начинаете с написания кода вашего FSM:
class FSM
IViewFactory ViewFactory;
IModelFactory ModelFactory;
Container Container; // e.g. a StackPanel in SL
ctor((viewFactory,modelFactory,container) {
...assignments...
start();
}
start() {
var view = ViewFactory.Start();
var model = ModelFactory.Start();
view.Context = model;
view.Login += (s,e) => {
var loginResult = model.TryLogin(); // vm contains username/password now
if(loginResult.Error) {
// show error?
} else {
loggedIn(loginResult.UserModel); // jump to loggedIn-state
}
};
show(view);
}
loggedIn(UserModel model) {
var view = ViewFactory.LoggedIn();
view.Context = model;
view.Logout += (s,e) => {
start(); // jump to start
};
show(view);
}
Затем вы создаете свои IViewFactory и IModelFactory (ваш FSM позволяет легко увидеть, что вам нужно)
public interface IViewFactory {
IStartView Start();
ILoggedInView LoggedIn();
}
public interface IModelFactory {
IStartModel Start();
}
Теперь все, что вам нужно сделать, это реализовать IViewFactory
, IModelFactory
, IStartView
, ILoggedInView
и модели. Преимущество здесь в том, что вы можете видеть все переходы в FSM, вы получаете сверхнизкую связь между представлениями / моделями, высокую тестируемость и (если ваш язык позволяет) большое количество безопасных типов.
Один важный момент в использовании FSM - это то, что вам нельзя • просто переходите между состояниями - вы также должны переносить все данные о состоянии при переходе (в качестве аргументов см. loggedIn
выше). Это поможет вам избежать глобальных состояний, которые обычно засоряют графический код.
Вы можете посмотреть презентацию по адресу http://prezi.com/bqcr5nhcdhqu/ , но на данный момент она не содержит примеров кода.
1127160]