-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Context#executeCommand #64
base: master
Are you sure you want to change the base?
Conversation
Would In the View wiring code:
And in the command wiring code (should have a similar pre-0.7.0 check):
I'd like to get rid of both of these actually, and perhaps bump to a 1.0 release candidate since this is a breaking change (but paving the way for where we want to go with a pure-DI solution, and opposed to the context-as-a-junk-drawer metaphor) Anyway, tangent... but some food for thought. :) |
Ummmm? I use context in my commands all the time to (un)wire things. |
Let's think about another way to do this. Perhaps injecting an On Friday, September 5, 2014, creynders [email protected] wrote:
|
I (un)wire all kinds of stuff in my commands, not just commands. How do you do it otherwise? |
I think having the DI is already discouraging enough to not stash all kinds of stuff in your context? I mean there's no good reason to do so if you have DI right? |
Anyway, this shouldn't prevent you from releasing, there's no rush to this whatever the outcome. |
I guess I've never really unwired anything before so maybe I'm missing out Generally everything I wire up will stay wired up until the root view is What's the use case for things outside the context unwiring other things? I would rather the unwiring happen in the context itself somehow. On Friday, September 5, 2014, creynders [email protected] wrote:
|
For instance it's very easy to implement undo/redo functionality with "dynamic" wiring. I create an UndoModel which has a map of events to do_Command/undo_Command pairs. It wires all of those events to the do_Commands and listens for these events itself. If such an event is triggered it wires the relevant undo_Command to a "undomodel:undo:[index]" event. (The Obviously, come to think of it, would the context have an Another use case would be to have commands wired/unwired to a specific event depending on model state for instance. E.g.: you have a debugging settings panel with a "log remotely" checkbox. If it's ticked a command which invokes the LogService is wired to (an) event(s). When it's toggled off the command is unwired again. Another advantage is not at run-time but during development, I group some setting up of wirings into a BootstrapDebuggingCommand for instance. The context wires the BootstrapDebuggingCommand to a "startup" event of some sorts, but only if Also, how do you avoid having monolithic context classes? |
Hmm, I see the use cases but I just wonder if there's a safer way to do this than passing around the whole context object directly. The whole point of the injected With regard to "monolithic" context classes, I split out my command/view/class/value wirings into separate files, and This still could be considered "monolithic" in the sense that the With regard to dynamic wiring (one event could conditionally map to one of many commands) I generally try to avoid this, preferring a single command with a conditional. If the business logic in each conditional block starts to become big, you can extract each block out into its own separate utility file, which can be require'd in by the command, itself. I realize we're probably coming to a difference that's more philosophical (religious?) about how Geppetto apps should be structured. But when it comes to wiring, I tend to favor a structure that's more monolithic (and predictable!), over something that's more dynamic (and hard to follow). |
I'll whole heartedly confess dynamic wiring has bitten me in the ass before. But I think it's a matter of knowing where to use it and when. (As with everything regarding programming 😁) However I find the monolithic wiring exactly the opposite of clear, it confuses the hell out of me. But yeah, this is definitely one of those more ideological discussions. I'm not debating this for debate's sake, but to learn whether you solve this maybe more elegantly. ATM I can't see how a monolithic (even when split up) context could be any more clear than such a flow controller. Anyway, I agree one should try and shield people from themselves, but it shouldn't come at the cost of not facilitating different approaches. As long as we're not giving examples of hanging all kinds of crap on the context IMO we've done our duty. |
@creynders I see your use cases, but I'd still strongly prefer that we inject only the whitelisted methods we want into Geppetto actors. We already have this precedent with the injected I see your point about giving good examples, etc. but at the end of the day, I've still seen developers taking a "quick and dirty" approach of just stuffing some property directly onto the |
Yeah, I understand. I just wonder whether the benefit of the encapsulation outweighs the pollution of the commands. To me So, which methods would be considered for injection? The full resolver API: Seems a bit much to me, yet all of those methods need to be accessible from commands IMO. |
To sum it up, I think the major difference in ideology between us is that to me commands are the glue that make up the application. All the other parts should be reusable and easily portable, however the application needs to be "made" somewhere: the commands. This means they have to be able to do everything what's necessary. The reason why this is not the contexts responsibility is because the contexts role is exactly that: providing a context into which the components are assembled. Two contexts allow two identical components to live side-by-side at the same time and behave differently. And following SRP (obviously this is debatable) I'd say contexts provide context and commands execute, i.e. do stuff. To me it also goes against best practice to have a class which exposes a ton of facilitating methods (i.e. the context) only to have it operate on itself. It gives too much responsibility to the context and muddies intent. It would be like letting a model listen to all system events directly (i.e. having a Then, as a pragmatic programmer I definitely think ease-of-use, i.e. user friendliness trumps strictness every time. Even if hiding the context out of reach of commands would be more correct, the ease-of-use of having it available in them wins out. On a final note: if the context is only available in commands, even if people start hanging all kinds of crap on it, they can't access it from anywhere else so it would be of very little use to them. And last but not least: having the context injected into commands allows people to still use it as you do. But not injecting the context into commands will seriously hinder people following my approach: they can't use shallow commands, have to wire the context manually each time, wire many many commands to receive the context, i.e. they'll have to write a lot more boiler plate. |
And come to think of it there's an alternative option: we could make the context instance non-dynamic, i.e. prohibit to create members that aren't predefined. I'll rather spent my time on coming up with a solution for that, than rewriting Geppetto (which would be necessary) to be able to inject all context and resolver methods into commands. For instance we could use |
This is the last one, promised ;)
I added an
executeCommand
method to the context. It's something I realise I could/would use sometimes. For instance in my current project I have a pretty complex control flow state machiny-thing. Basically it's a controller. The reason why I don't rely on a commands/events mapping is because I find it far more clear to have all of this centralised instead of having to track the flow through a dozen or so commands. Anyway, in my controller there are however sites where I do want to execute a command, since the command centralises some complex update mechanic for instance, so ATM I simply dispatch dedicated events, which are mapped to the relevant commands, but actually would like it more to being able to execute the command directly withexecuteCommand
accepts a second parameter, named "event", which is an object witheventName
andeventData
members. E.g.