4 ms·
writer of the article here: I simply cannot follow the argument about "added complexity" The use case defines the minimum amount of links you have to make, in
by Retozi 12y ago
writer of the article here:
I simply cannot follow the argument about "added complexity"
The use case defines the minimum amount of links you have to make, in this case 4.
Now you have two options: You just roll with it (we used to do that), or you try to abstract away a few steps (we do this now with the dispatcher).
The dispatcher here is roughly 100 lines of fairly simple, unittestable code, so I really cannot see where the elusive bugs come from. 100 lines that you will easily save if your app is complex enough by the way.
With the dispatcher, you can abstract away a couple of steps consistenly over your app. Now I fully agree that you need a certain threshold of complexity to make it worthwile.
But you cannot avoid the original complexity of your usercase.
There is no alternative to decent abstraction if you reach a certain level of complexity, because it is given externally.
Other concepts like two-way databinding do exactly the same.
- userbinator 12y agoIt seems all you've done is inverted and hidden the sequence dependency in a series of waitFor chains instead of event chains, which in some ways is worse; compare A: update; notify B; B: update; notify C; C: update; notify D; D: update; with D: wait for C; update; C: wait for B; update; B: wait for A; update; A: update; The former is a clear sequential path, the latter builds up and then unwinds in a stack-like fashion. It's like the difference been non-tail and tail-calls. It's only a technical nuisance that one model needs to wait for the other to update. I would certainly not view a sequence dependency that's critical to correct operation as a "technical nuisance" to be hidden away; it's an important fact.
- Retozi 12y agoyep, that's all it does. It just inverted the sequence dependency. You call it "worse". But I argue that it is a much better mental model in the specific use case of updating state given an external disruption from the user. You see, what you call a clear sequential path is what I call evil :). We had tons of those in our app, and it was so hard to keep track of them, and everytime I didn't look at it for a while, I had to invest 10 minutes to track down the event chain. There is one simple reason for this: If you have a sequential path, you loose the context after the first link. A updates because "user-clicked-x", and B updates because A updates. But if you're working on B, you ask yourself why the heck did A update in the first place? You might argue that you are always aware of this, but we found that when our app grew more complex, we actually didn't always know. I'm sure you could find a solution where you keep the sequence and don't lose context, but then you have to state the dependency in the wrong place: In model A you have to say that model B should update. But that's all wrong because A shouldn't care about models that depend on it. The models that depend on A should care! So by inverting the whole sequence dependency, you gain the following: 1. in every model you are aware of the context, i.e. the original event that triggered the state change in the first place 2. you are also aware on what other models this model depends on (it is explicitly stated and not hidden like you said). This means that you can work on model B and extend it without ever looking at other models, while knowing exactly the origin of your state change. In my opinion, this is decoupling at its best. It also helps unittesting greatly. While I agree that inverting the dependency is a drawback, I've gained things that, at least in my opinion, heavily dominate that drawback.
- rtpg 12y ago> A updates because "user-clicked-x", and B updates because A updates. But if you're working on B, you ask yourself why the heck did A update in the first place? Stack traces are a thing. Chrome even has asynchronous stack-traces. A updates because "user-clicked-x", but it might also update because "user-clicked-y". Or because "user-clicked-z". So now I have 3 things to add to A and to B, because B needs to refresh everytime A does. So if you forget about one of these, then suddenly B is out of sync. A "solution" to this problem already exists in terms of event listeners. If B listens to any change on A, then A doesn't have to deal with B, but B updates properly and doesn't have to deal with all event types A has to deal with.
- Retozi 12y agoWell, I remember all these things just too well from our Backbone App (not Backbones fault, but ours!), and I would never go back there again. So now I have 3 things to add to A and to B, because B needs to refresh everytime A does We used to do this. A lot. It was a big mess. Because in reality (for us), it is never that clear cut. You almost never have full dependencies so that thight coupling is the right way to do model it. There are always exceptions that you need to be aware of. We found that decoupling reduces cognitive load greatly, and changing the relationships can be done a magnitude quicker because "listen to except if this and that happens" doesn't scale. There are just too many this and thats if your app is big. I know that the status quo method always looks easier than something new, because its unfamiliar. But restriction of communication flow is, in my opinion, the ONLY way to keep order in a big, complex app. Just consider this: I've tried both ways extensively, and I favor the Flux way. I might be an idiot, but there is a possibility that I'm not. This should encourage people to really try this once to avoid status quo bias.
- coldtea 12y ago>Stack traces are a thing. Chrome even has asynchronous stack-traces. Now this argument line has dissolved into plain sillyness. He showed how he made the congnitive load less and the dependencies more explicit and locally evident IN THE CODE, and you tell him to use "stack traces" for that?
- rtpg 12y agotwo-way databinding does not always do the same thing. Angular's digest cycle is frame-based, where everything is recalculated until you hit a fix-point. This is more analogous to the event dispatcher (A is updated, then on refresh B will) than your dispatcher example.