I'm posting this here because of syntax similarities and my question is about OOP.
I am designing a timeline system in AS3, like GarageBand.
The timeline (TL) has tracks (ObjectTrack) which have items (Item).
Thinking about the OO architecture, I designed the following diagram for adding a track to the timeline:
As you can see the timeline receives the 'addObjectTrack' call, and delegates it to a presenter (TLPresenter). The presenter creates the model and the view for the track. The presenter adds the track model to the timeline model. The Timeline receives the track view from the presenter, adds is to the displaylist and calls update.
My question is: Is this the right way to implement MVP? I decided to use a central Presenter for the timeline, tracks and item, because of the complex UI logic. Is this the right way? What would be your architecture?
I know it takes a bit more time to understand this question, but I really appreciate your opinion. Thanks!
Personally, I wouldn't start with a complicated diagram like this -- I might keep a pattern in mind but I would start with the few core classes (Timeline, Track, and Item) and try to defer any decisions about how to present these or get any user gestures. I'm at No Fluff Just Stuff right now and these are some of the messages from the speakers: Neal Ford talks about deferring decisions to the Last Responsible Moment -- another speaker talked about Complexity and Complication -- Neal Ford talked about abstraction distractions. Matt Stine and Peter Bell asserted that "architect" should be a set of roles and responsibilities, not a title, and that "architecture" is really the sum of all those things that are hard to change. Bob Martin, in a conference I attended two weeks ago, said that "Architecture" should allow a designer to defer as many decisions as long as possible (again, to the Last Responsible Moment). The main theme is: Start Simple - Start with the Core Logic - abstract away certain infrastructure concerns (presentation, database) until you have sufficiently defined AND tested the Core Logic using Test-Driven Development techniques.
Now about "keeping a pattern in mind" -- I would keep it WAY in the back of my mind, almost buried in my subconsciousness. Look for a book called "Refactoring to Patterns" by Joshua Kirievsky - his advice, which I find fit very well with an emergent design, evolutionary architecture approach (search for articles by Neal Ford on this), is to start with a problem, try to implement a solution, and see how much the problem and your solution aligns with a pattern and its problem/solution space. If there's good alignment, see how far your simple solution fits the pattern and slowly refactor your design towards the pattern to close any gaps.
Class Responsibility Collaborator models are about as complicated as I would start with if I were trying to design/architect this problem.
I understand your point, and I think you're right. It's best to first understand the problem and its solution in the most basic form.
Then when the code evolves, patterns can start to fit in.
The thing is that I did start with simple code.
But one of my first observations was that, as item manipulations are constrained by other items (and tracks), there needed to be something like a controller/presenter/manager that keeps track of all these instances.
And as there will be other views using the same data, I started out with a model.
That's how I ended up with this sort of MVP design.
It may indeed be too early for such a complex setup.
But what's your opinion on the 'controller that keeps track of all these instances' approach?
Another thing that Bob Martin said about architecture is this: good architecture should make it easy to see the reason for something to exist. Real-world examples: If I showed you pictures of parking spaces/lots, just the spaces without any context around them, you probably would have a hard time saying what kind of building the parking space/lot is for. However, if I showed you an aerial view of a parking lot near a cross-shaped building, then you might deduce that it's the parking lot of a Catholic church. I bet that even if I used an image editing tool to gray out the church, you would still be able to tell with a certain degree of certainty. If the parking lot was shown close to a fleet of shuttle buses and a huge building that was right beside a runway, you could probably deduce that it was the long-term parking lot at an airport.
Good architecture has a lot to do with providing context and clues about relationships of the parts that make up the whole. Now, let's take another look at your ObjectTrack. First, I would change the name to just Track because in an object-oriented system, the word "Object" in a class name is redundant. So, say I presented a Track class in this context:
Without even knowing anything more than the names of the classes nearby, you can probably figure out what track does in relation to the rest of the objects in this collection, right? And you could possibly deduce why this ecosystem of classes exists, right? If so, then I would say this is the start of a good architecture. I can't honestly say the same for your Timeline and ObjectTrack classes (presumably your core classes), much less with the added noise of TLPresenter, TLModel, and ObjectTrackModel. I could stare at this for ten minutes (and believe me, I'm getting pretty close to that) and still not have a clue about what these things are for, much less how appropriate their relationships to each other are. I can infer some of the obvious relationships but I still don't know what, in general, this whole thing, as a system, is about.
So the roundabout answer to your question is: I honestly don't know what to think about "the controller keeps track of all these instances" because your architecture does not make it obvious to me. I'm not trying to be thick on purpose, I just can't really say for sure. Why do you feel that "there needed to be something like a controller/presenter/manager that keeps track of all these instances"? What purpose does this serve? Again, I don't know.
If I were to add a class called "RaceCoordinator" to my example collection of classes above, would you be able to form some ideas of how it fits in the big picture? See what I mean?
What I would ask about your statement "The thing is that I started with simple code" is this: How did you end up with a complicated design then? Did you do Test-Driven Development? Test-Driven Development, contrary to common first impressions, is actually not so much about testing as it is about designing. The tests are merely used as a way to motivate you to either add to, modify, or restructure your current design so that it keeps slowly evolving as you add more increments of functionality and capability to your system. The end result of this process over many iterations is a system which may be complex but not complicated. There is a big difference.
Joined: Jun 10, 2012
Thanks again Junilu for your extensive response.
Yes, I gave too little context and class names like ObjectTrack certainly don't help..
In short the timeline is a composition. Timeline has tracks, tracks have items. So that's the three core classes.
It looks like GarageBand:
Reading about MVC/MVP etc. I wanted to decouple data and ui from the start. Every track and item on the timeline has it's own model, and responds to its updates.
But then I did end up with one big controller to handle the item manipulation.
For example, dragging an item on a track with multiple items and without overlap needs information about all the items on the track.
Moving an item to another track with items needs information about the other tracks and its items.
To me it seems that there has to be an object that knows all these elements. Right?
Do you normally start out with a tightly coupled prototype (that works) before moving on to a MVC/MVP approach?
I smell a faulty abstraction. Is Timeline really an Object? What about starting with this: Composition and Track. A Composition is made up of one or more Tracks. A TrackLayout represents the visual layout of tracks in relation to the start of the composition and in relation to each other. Tracks have attributes of instruments, measures, etc. A TrackLayout knows where each track will be displayed in relation to a base point (the origin perhaps?). A track can be moved in relation to the origin such that the TrackLayout can evaluate whether the current Track's position follows certain rules, such as Tracks can't overlap other tracks, tracks must start at least at the start of the Composition, etc.
Starting with these few concepts (they are really my "hypotheses" of how the system would work), I would do TDD to see if my hypotheses work. Passing tests are proofs that my hypotheses do not have any perceivable flaws.
Joined: Jun 10, 2012
By using the word 'track', do you mean a colored block on the track or one of the (horizontal) instrument tracks?
The composition is made up of one or more track and is represented by a TrackLayout. So this TrackLayout would be an object that knows about all tracks right?
Yes, it could mean that a Track is rendered by a TrackLayout as a colored block to represent how much of the composition it contributes to. Again, I wouldn't keep trying to extend too far beyond this initial line of thinking without some passing tests and actual production code to tell me that what I have modeled in my head can actually work as running code. The "think it all the way through first" approach is fine for theoretical physics but it's not really worth putting the same kind of effort into designing a software system because you can easily try out any hypothesis by writing tests and code. By hypotheses I mean the statements like "Track and TrackLayout are related in this way".
Just wanted to mention another thing about design: the concept of Separation of Concerns and the Single Responsibility Principle (SRP), the first of the SOLID design principles. The SRP makes you think about separation of concerns. Things like: if I change the way a Track is rendered for the user to see it, does that change its basic relationships and behavior? The answer to this type of question is usually "No" (logic for displaying something is seldom tightly bound to the logic that governs its core behavior). So when you find yourself answering "Well, right now there's no real distinction between business and presentation logic" that's a warning flag because you may be violating SRP and mixing the concerns of presentation behavior with business/domain behavior.
So what are the domain/business behaviors or responsibilities of a Track? You might try something like:
1. Knows how it should be played relative to a baseline (loud, soft, fast, slow)
2. Knows when it starts and ends, rests, resumes relative to a baseline
3. Knows the kind of instrument to be played
4. Knows what notes need to be played
5. Plays the notes according to its properties (the things it knows)
Do any of these have anything to do with rendering a representation of a Track. e.g. a colored block? In a very basic way, yes, because to render properly you need to know when a Track starts, ends, rests, resumes playing and other things like that. But does rendering have anything to do with actually playing the notes? If you decide to change from displaying a colored block to displaying actual notes on a scale, does that change the way the track actually plays the notes? It probably doesn't, right? So there probably needs to be a separation between Track and how it rendered for viewing by the user.
Cue the TrackLayout. This is the guy that might have the responsibilities of rendering representations of Tracks for users to see. I'll leave it here for now so you can mull this over and maybe try to run with it.
Tom Youngman wrote:Do you normally start out with a tightly coupled prototype (that works) before moving on to a MVC/MVP approach?
Tom -- you'll have to bear with the length and verbosity of my replies, I'm just trying to put down my thought process as well. Thanks for your patience and please don't feel like I'm being condescending or anything if it seems like I'm just reiterating something you're already thinking and not answering your questions at all.
To answer this one directly, a tightly coupled prototype that works is as good a place to start as any. From there, I still wouldn't make any intentional moves towards MVC/MVP. I would first go back to design principles like the ones I just long-windedly wrote about above. Patterns themselves are also based on those principles. What I would do when I detect the smell of strong coupling is to ruthlessly refactor so I can make the coupling looser or break it altogether. That activity will result in code that is either aligned with the pattern or not. If the code doesn't align with the pattern but I still like it because the smell is gone, then I'm good. If the code does align with the pattern, then I'm also good because it validates what I just did. So like I said before, I don't really like to make the pattern the direct goal but rather the almost subconscious guide that helps me to successfully refactor towards code that follows good design principles. The fact that it does or doesn't conform to a pattern, to me, is just an internal compass.
I liken the process to making a vase on a potter's wheel. You have a lump of clay that you slowly mold and shape. You also have a template that you can use to check your progress against a desired end result. However, the fun in using the potter's wheel--or so it seems, because I've never actually used one--is the sensation of the clay in your fingers as it spins on the wheel, the way its shape changes in response to your every gesture. The way it morphs from a lump of clay into a thing of ever increasing beauty. Once in a while, you pick up the template and hold it up against your creation. If it's not quite there, you put the template down and massage the clay some more. In the end, the template was really just a guide and a way to know when to stop. It was the molding and morphing that really makes the experience very gratifying. That's the same kind of experience I have with Test-Driven Development, Refactoring, and SOLID design principles and patterns.
That, my friend, is what craftsmanship is about for me. Sorry this got long-winded again. I just get carried away sometimes.
Joined: Jun 10, 2012
I don't mind the length of your replies. At least I found a forum where I don't get replies like 'try googling'.
Anyway, right now there is no distinction between rendering representation and business data.
But I find it difficult to think of a way to design this.
Let's say a composition consists of objects
composition as base
tracks as children of the composition (the horizontal bars to put colored blocks in)
items as children of tracks (the colored blocks, I call them items)
The timeline has one TrackLayout object that is in charge of rendering the tracks and items, as well as manipulating them (with constraints).
How do I make the distinction between rendering representation and business data for an item? Separate it to a 'view object' which has the X and Y values and a 'model object' with the time values?
So TrackLayout manipulates rendering values (X, Y), but what object updates the business model then?
And if TrackLayout renders, I need to make sure it only updates the necessary elements and not the whole timeline. So the TrackLayout needs to know about the hierarchy of the composition?
Should I start with the view objects and layout rendering first, and the coupling with business data later?
I'm still using model-view-terminology because I don't know how to call it now.
Many questions... bit confused...
The thing I'm having trouble buying into are the concepts of "Items as children of Tracks" and "Tracks as children of a Composition". I don't like to make the jump of "having children" right off the bat. This has to do with "prefer composition over inheritance". Here, "composition" with a lowercase means establishing a "has" relationship whereas Composition with an uppercase refers to the concept in your problem domain.
Is it really necessary to think of the colored blocks as children of the Track? Does a Track have multiple colored blocks? What if you think of this as a less tightly bound relationship? Why does a Track need to know about Items at all? Another way to look at this is: Ok, I'm a Track. I know what instrument I play, I have the sheet music (notes, start, rests, end, measure, beat, etc.), now I just play the music. If someone were to take a video of me playing my music, do I care what they're doing? No, right? I don't even care if they were to broadcast this on TV and took shots of me from different angles. I wouldn't really want to worry how they're making me look on TV, I just worry about playing my part of the Composition (uppercase).
I can see how a Composition can be related to multiple Tracks but I don't see it as a parent-child relationship either. Maybe it's just a question of using the proper semantics though. Did you mean "A Composition has one or more Tracks."? Just because a Track can be rendered so that it appears to be "contained in" a Composition doesn't make it necessary to model the objects that way too. When you use composition (lower case), you're really saying something like "Composition knows about, and can make requests on, one or more Tracks." If a Composition did make a request on a Track, what would that request be? "Hey, Track, can you do something for me?" What would "something" be?
The Layout should know about the Tracks. It should also know about "Items". I personally would prefer a name other than Item. I might even question the need to have such a thing as an Item altogether. What if I didn't have "Items". What would the code look like?
(multiple edits for clarity and making sure I was using the right case in certain places)
Maybe a silly question but have you written any code at this point? I would have already written some tests and some code by now. Tests - my hypotheses of how the system should work. Code - provides empirical evidence that my hypotheses are good or not.
Joined: Jun 10, 2012
When I posted my question I had the sequence diagram as working code.
But it felt like a very complex solution to a simple problem.
I now have separated business data and render-related data, with a much easier approach.
Each view instance updates it's model as it is manipulated.
An update method is called when the model changes. When an instance is updated, all children of the instance will update as well.
This way I can refresh only the necessary parts of the Timeline.
However, the manipulation constraints are placed in these view instances as well. Not in a separate object.
For example; The Track object makes sure Items don't overlap.
Is this understandable?
I kind of see where you are right now. I would not make Track responsible for making sure Items don't overlap. I would make a Layout class enforce that rule. Something like:
With this kind of design, you don't even need Items. That need is fulfilled by the logic in the Layout class and the redraw() method. I don't know if this will actually work though but it seems reasonable to expect this kind of structure. And I think it looks simple and straightforward. Tests and real code could still prove that this design has flaws though.
Joined: Jun 10, 2012
I spent some time looking at your input. I accidentally looked over your post on Composition/composition. :S
Perhaps I am writing about parent/child relationships because AS3 works with a display tree (with parents/children).
And I probably have too much visual focus (I see a Track and it has Clips).
I did mean a 'has' relationship between Composition > Tracks > Clips though. (Clip sounds better than Item?)
When it's a well encapsulated composition of display objects, is that a bad thing? I mean, with a function like reordering a track (with all of it's Clips)..
I just can't get my head around thinking in a less rigid way..
So where would this Layout class come in? Is it like a controller class where all these clips send the UI events to?
The addition of tracks, and its rendering, would that be part of the layout too?
Judging from this, it looks like there's a strong influence from the language/framework itself to code towards mixed presentation and model, that is, the presentation is part of the model. If I were to program in this, I would have to make an effort to draw the line between the things that make it easy to conceptualize the problem and the things that make it easy or even possible to use the framework or built-in language constructs. For example, look at lines 9-11, where the addChild method is getting called. This is really a visually-related thing: the Sprite that represents a TrafficLight has, as its children, three other Sprites that represent the different colors a TrafficLight can take on. Conceptually, a TrafficLight's colors would simply be changes in its State (line 17, it's initialized by colorState = 0). That is, a TrafficLight has a State, a State can take on any of the values GREEN, YELLOW, RED. How that is displayed to the user, that would be a separate concern but in the AS3 code, that distinction gets a little blurred and it becomes difficult to maintain the separation. You will have to figure out where to draw that line in your problem domain and learn how to switch from one perspective to the other. I think that will make it easier for you to wrap your head around this problem. That can be a bit of a challenge but I think it will help.
Edit: The more I look at it, the TrafficLights tutorial is actually pretty well written in this respect -- it does a good job at separating the visual concerns from the conceptual logic of Node, Graph, and Edge.
Good luck and thanks for (kind of) turning me on to AS3. It looks cool but then I'll have to really dig into it to know for sure how I feel. One thing I'm not sure of how to do is unit testing in AS3. If it's hard to do, that would be a deal breaker for me.
I'm going to take one more shot at trying to answer your question directly
It looks to me like you are, in fact, going to have to make your TimeLine (or Composition, if you prefer) class be the container/parent for your Tracks, but purely from the visual sense that I wrote about earlier. If you can, try to separate that out conceptually though. Something like "Ok, when I do TimeLine.addChild(track), that's purely visual. Conceptually, I really mean to say that Timeline now knows that it has another Track that it needs to play as part of this piece of music (the Composition)." Then I would have separate logic/functions that take care of the visual concerns and (maybe) separate logic/functions that take care of the conceptual concerns. Does that make sense?
Edit: Looking at the TrafficLightsWeb example some more, it seems to me that's exactly what they're doing. They have Edge.as, Graph.as, Node.as, which all appear to be purely conceptual, separated from the TrafficLight.as which is largely visual mixed in with some conceptual.
Another edit: So yeah, from a visual perspective, your Timeline is probably what I would think of as the Layout class and the Timeline knows about the Tracks that it manages and can ask each Track what part of the display real estate it covers. You can then calculate if another track overlaps that same display real estate. AS3 might even already have that covered if it has something like someSprite.overlaps(anotherSprite).
More edits: Looked back through the thread some more and I think I am able to grok the concept of Track and Item (I agree, Clip is better). A Track can be made up of multiple Clips played one after another. If I had multiple guitars, I'd have multiple Tracks, each playing a guitar but maybe one is a bass guitar, another is the lead guitar, and yet another the rhythm guitar. Within a Track, since it represents only one instrument, you can't really have the Clips overlap -- unless you want to model multiple people playing the same guitar at the same time... WOTE rocks!. Anyway, it makes sense to have a relationship of "Track has Clips" in a conceptual sense and at the same time have one that says "Clips are children of Track" in an AS3 visual sense. Again, you need to keep those two perspectives separate somehow as you code and decide whether any behavior you implement is conceptual or visual in nature.
Joined: Jun 10, 2012
Hehe, now we're really talking the same language ;)
Most simple Flash game code is written as SmartUI; visual and business logic combined.
But for a more complex GUI, an Hierarchical MVC-approach seems a nice way so separate the business logic and representation. You can decide how many MVC triads you want, and thereby choosing the granularity of control (e.g. one for every little control, or only the importent parents)
So, when I think in these terms I am considering the following:
has Track children
Handles layout for the Track children
Handles UI input events and defers it to TimelineController
Updates itself based on a TimelineModel
has references to TrackControllers
updates TimelineModel based on UI input events
has non-visual (and visual?) related data[/list[
TrackView [list]has Clip children
Handles layout for the track
Handles UI input events and defers it to TrackController
Updates itself based on a TimelineModel
has references to ClipControllers
updates TrackModel based on UI input events
if it can't manage the UI event, defer it to the parent controller
has non-visual (and visual?) related data
And so in this line of thinking you could probably imagine the Clip MVC-triad..
With this hierarchy, I am able to control/update only the necessary parts of the timeline. Sometimes just only a Track, sometimes the whole Timeline.
A proper way of separating visual and domain logic?
** However the fact that Views update when a TimelineModel changes implicates that visual data is stored in the model. I know this happens in game code, but is it necessary here?
When the layout is handled in the Views, the visual information does not need to be stored in the model. The model then stores only non-visual domain data that can be used for other views as well.
Tom, this has been a really enjoyable conversation. Thanks for the great questions and your patience in the latency of my grokking your problem. It looks to me like you may have something that would evoke a minimal number of WTFs from a reasonably competent person (which, as Robert C. Martin aka "Uncle Bob" would say, is the only true measure of code quality).
That said, in the end, you will be the true judge of what you can or can't tolerate as far as smells in your code is concerned. Of course, your sensitivity to the welfare of others coming after you will also come into play but I trust from what I have seen that you are a conscientious and empathetic developer and will be able to, in good faith, good conscience and confidence, leave your contact information embedded in comments in your source code for other developers to find later.
In Ranch-speak: Looks to me like yer all geared up so stop lolligaggin' an get those cattle movin', pardner!
Edit: To answer your question (I am such a windbag, aren't I?) of whether it's a proper way of separating visual and domain logic: I think that due to the nature of AS3, there's probably no way to get complete separation. I'd say let your gut and sense of code smell tell you when it's "good enough" and stop there.
Joined: Jun 10, 2012
Junilu, I am sorry for this very late response.
Thank you for your efforts helping me out with my questions.
I must say I had a laugh reading your last post.
As a dutchman I had to read it twice to get the meaning of your rather complex wording
But you are right. And it's a wise lesson (at least for me):
Don't spend too much time thinking of the best or most elegant solution beforehand.
Good enough is ok at first. And never feel unconfident about the solution. That may prevent you from starting at all.
You eventually will get to the better solutions.
And yes, the web is a powerful thing. I'll do my part.