Beh01der / EasyFlow

EasyFlow - Simple and lightweight Finite State Machine for Java
http://datasymphony.com.au/open-source/easyflow
Apache License 2.0
445 stars 86 forks source link

Current pattern/library status #23

Open konmik opened 9 years ago

konmik commented 9 years ago

Hi, Andrey.

The library looks promising to me, but I am a little bit confused that it was not updated for years. Did you switch off Android development, or decided that the pattern does not work well / there are better ways to structure the screen logic? Or you're just using it as a proven tool and do not have a need to make it better?

Can you please elaborate on the effectiveness of the pattern. Do you use it in every app/on every screen, or it fits only some cases?

Beh01der commented 9 years ago

Hi Konstantin,

I am still using it in my Android apps plus I recently created a similar library for Node.js (as I do Web dev as well). I personally, like the pattern for it's code clarity. It's very easy to come back to your code after long break and update the logic if required.

I use it for: handling UI events in some cases, handling application start up logic (it's quite complex in my case, has many async calls) and handling sync logic (multiple http calls, re-login when required, etc). I never used it for high-performance apps though. I don't actively develop it as it's got all what I need.

For example, here's my start up logic. I have no idea how I would handle it w/o EasyFlow.

One advice - don't use maven version. It's well outdated. Build it from the sources.

        flow = from(CHECK_SIGNED_UP).transit(
                true,
                on(no).to(SIGNING_UP).transit(
                        on(ok).to(VALIDATING_LOCAL_DB),
                        on(fail).to(SIGNING_UP),
                        on(cancel).finish(EXIT)
                ),
                on(yes).to(VALIDATING_LOCAL_DB).transit(
                        on(fail).to(SEARCHING_OLD_DB).transit(
                                on(yes).to(CHECK_LEGACY_SIGNED_UP).transit(
                                        on(yes).to(CLEANING_UP_OLD_DB),
                                        on(no).to(UPLOADING_OLD_DB).transit(
                                                on(fail).finish(EXIT),
                                                on(retry).to(UPLOADING_OLD_DB),
                                                on(ok).to(CLEANING_UP_OLD_DB).transit(
                                                        on(done).to(RESTORING_DB_FROM_CLOUD)
                                                )
                                        )
                                ),
                                on(no).to(RESTORING_DB_FROM_CLOUD).transit(
                                        on(fail).finish(EXIT),
                                        on(retry).to(RESTORING_DB_FROM_CLOUD),
                                        on(ok).to(VALIDATING_LOCAL_DB)

                                )
                        ),
                        on(ok).to(INITIALIZING_POLICY).transit(
                                on(done).to(CHECKING_AUTO_UNLOCK).transit(
                                        on(yes).to(VALIDATING_PASSWORD).transit(
                                                on(yes).to(INITIALIZING_UI),
                                                on(yes_skip_init).to(CHECKING_DAILY_TASK),
                                                on(no).to(CHECKING_AUTO_UNLOCK),
                                                on(cancel).finish(EXIT)
                                        ),
                                        on(no_skip_init).to(CHECKING_DAILY_TASK),
                                        on(no).to(INITIALIZING_UI).transit(
                                                on(done).to(CHECKING_IMPORT_START).transit(
                                                        on(yes).to(IMPORTING).transit(
                                                                on(done).finish(COMPLETED)
                                                        ),
                                                        on(no).to(CHECKING_JUST_UPDATED).transit(
                                                                on(yes).to(SHOWING_INFO).transit(
                                                                        on(done).finish(COMPLETED)
                                                                ),
                                                                on(no).to(CHECKING_DAILY_TASK).transit(
                                                                        on(yes).to(RUNNING_DAILY_TASK).transit(
                                                                                on(remind_premium).to(REMINDING_PREMIUM).transit(
                                                                                        on(done).finish(COMPLETED)
                                                                                ),
                                                                                on(remind_review).to(REMINDING_REVIEW).transit(
                                                                                        on(done).finish(COMPLETED)
                                                                                ),
                                                                                on(no).finish(COMPLETED)
                                                                        ),
                                                                        on(no).finish(COMPLETED)
                                                                )
                                                        )
                                                )
                                        )
                                )
                        )
                )
        )
konmik commented 9 years ago

This looks pretty clean. Like a higher-level language without all of the implemenation details.

Thaks for the answer, I will think how I could use your library! :)

konmik commented 9 years ago

Yet another question.

How do you deal with configuration changes?

In example, a user presses a button and switches the state to "showing item selection fragment". Then, a config change occurs, the flow will be reset but the fragment will still be shown.

In your example you're just starting the new flow from the beginning every time the user flips the screen.

Beh01der commented 9 years ago

Konstantin, I am not sure that I understand correctly what you mean by "config change occurs". If you mean that your app will restart and, thus, reset status of your FSM, there are at least 2 ways to avoid it.

First, you can make activity not restart on config change (I believe, it's controlled by some flag in Android manifest). Second, you can put your FSM + context (which holds FSM state) into a singleton, so it will survive app config change.

konmik commented 9 years ago

There is no way you can prevent Android from destroying your activity, the only reliable way you can use is to save/restore all states that activities have.

Beh01der commented 9 years ago

That's right. However, it doesn't meant that all app's memory will be released. Even with activity "destroyed" application normally stays in "cached" state with all singletons alive and happy (for as long as there's enough RAM).

Anyways, if I was to design a new application with EasyFlow, I would keep all application's status in StatefulContext, then I would serialise / deserialise it as required. From the StatefulContext object I would be able to restore status of the entire application.

konmik commented 9 years ago

There is no way you can prevent Android from clearing singletons as well.

OK, StatefulContext looks good. Thanks! :)