Top-Down Design: Hierarchy and Composition

General discussion for topics related to the FreeBASIC project or its community.
anonymous1337
Posts: 5494
Joined: Sep 12, 2005 20:06
Location: California

Top-Down Design: Hierarchy and Composition

Post by anonymous1337 »

I've spent years of programming as a hobbyist building games from the bottom up, with the highest level of code generally being a spaghetti prototype, or a piece of art made in MS-Paint. Usually, when I finally make it to the higher level of the code, I'm completely sick of the project and have no idea what to do next - It's the strangest thing in the world having all the classes you need, but none of them feeling right to be used to compose an engine from them. Building an engine around lower level interfaces is terrible, no matter how essential they may have seemed when the project began.

Bad programming practice lacks structure - Although 'programming things because you feel like' works great for small prototypes, you quickly end up with cluttered code which is hard even for you, the author, to improve upon. However, the 'feel' is that higher level concept. It's the top-down thinking that's more natural. Bottom up might be the real starting point for when the code begins practical, but a bottom-up approach means coding classes you think you'll need, but might not.

This, in my opinion, is due not only to lacking structure, but the utter confusion between the areas of design relating to Composition and Hierarchy.

When it comes to Hierarchy, I think it's a question of functionality in any level of the hierarchy. At the very top you have your complete engine code, which is so likely to change that you really can't help but code anything but a prototype at this level - But you can begin to think, "What is this composed of?". The first response is going to be a programmer's response - Low Level Objects, of course! Why didn't I start with those in the first place? >_<;;

If you were to ask any non-programmer this question though, you'd hear very high level responses. Usually ones never seen in actual code, but are often present in editors for maps and otherwise - The artistic side of your design, which we all only wish we could implement with the wave of a magic wand (or typing finger). While these objects may be impossible, they feed you that information which you don't get with most low level objects - The direct descendants of your full engine code. Right now, you've established with a given amount of certainty within moments what takes a bottom-up design months to decide on, if the design ends up even working at all.

Class dependency is an issue with top-down design though - And this is where we begin separating things by Composition more. While you may be able to code a mock up of how a full engine would work starting from the top, the problem of which classes to begin coding first, which are composed of other classes and which are true hierarchies, is a major problem often not visible until classes actually begin being coded. A generic engine won't do anything, but I think composition (and thus, immediate separation from objects on the same 'level') is important - In fact, the idea of wanting your classes to depend on each other so heavily seems somewhat ridiculous.

With Package-by-Package dependency for the Engine shown, but also keeping in mind that none of these modules are direct descendants (or super classes in actual code design) of the Engine, we can work on one package's classes without worrying about the full engine API as much, or even other objects related to this module. These are objects which are eventually going to be composed together, but can each be a project in their own right.

The idea of writing 'tests firsts' goes beyond simply making sure things work, but also provides a fully working prototype in Top-Down design. Having chosen our package, and having chosen the top-most class, we can prototype (our potential 'test') our class before it's even coded, creating new branches and walking down the tree of classes until we hit our lowest level which is capable of being coded without any lower level classes than the current one.


The Top-Down process seems larger in big projects, but I've found it to be so remarkably efficient in not only prototyping, but building prototypes which can be easily transformed into the final code, that I'm willing to risk a little extra initial thinking to make this work. I found it to simplify things in the end, but sometimes it means hacking away at lower-level classes I've already made in the past. Sometimes it's one class to code for a package and that's really it - Just split things up into smaller classes in the end for flexibility.

We all work lower level first when it comes to outlining our concepts - Technology Assessment, but do we not all dream a game in its complete form? I hope you have some thoughts on this. It's great to learn. I was reading up on design for a while, but most material's never written all too well, or is geared towards people with teams in the tens of programmers. I'm thinking of writing an article on this once I'm done with my current project, further analyzing and guiding the reader through the process.


Got any good references on hand? Any thoughts? :D

I think that in general, Top-Down Design is the only way to truly keep your concept in tact when you move to the lower levels, and it also can give a full explanation (once you get to the more practical, job-specific classes) of what this class and its API will do without you having to have to look at lower-level code.
jevans4949
Posts: 1186
Joined: May 08, 2006 21:58
Location: Crewe, England

Post by jevans4949 »

[2cents]

True programmers are generally more fascinated by the low-level stuff: "What is the most efficient way to sort, generate random numbers ...". Once these problems are solved, practical application becomes boring.

Top-down design is usually a business analyst type of thing; big objects like "accounting system", which they then break down into smaller objects, and fail to spot all the connections between the lower-level objects.

(In the area of business computing) users know their current processes, but not how computers could help, or what parts of the process are difficult.

I believe the key is to get programmers talking to users as soon as possible, and give them the authority to change the top-down model where it is demonstrably "wrong" for the way the organisation works.

Most private projects that make it to (near) completion do so because the programmer has a practical use for it. Note the number of utilities around that have functionality limited by what the author needed at the time. Personally I play a relatively small number of games, and have never felt motivated to write a serious one.

[/2cents]
vdecampo
Posts: 2992
Joined: Aug 07, 2007 23:20
Location: Maryland, USA
Contact:

Post by vdecampo »

After 23 years of programming, I find that my current game coding style is much better than in previous years. So much so that I have been able to bring my projects to completion, which is saying alot. :-)

The format I have adopted is to use Finite State Machines. This is a form of Top-Down design but with the added structure of FSM. Each state of the game gets its own loop which is basically a tree built off the main FSM.

Pseudo code follows...

Code: Select all

Select Case Game.State

     Case TitleScreen
         'Do that title screen stuff
         'Check for state change to GameIntro

     Case GameIntro
         'Init all game vars
         'Do intro then change state to GamePlay

     Case etc, etc, etc....

End Select
This style has its drawbacks in that, if you do no properly plan out each machine state, the FSM tree can get pretty tangled. It takes practice by starting with small projects and learning to scale them up without becoming a bear.

Also, try to stay clear of Global vars. The only global vars I use are generally the game sprites. I create an array for all game sprites and use an ENUM to describe each index with an informative name.

Cheers!
-Vince
anonymous1337
Posts: 5494
Joined: Sep 12, 2005 20:06
Location: California

Post by anonymous1337 »

@jevans:

Thanks. Well you know, this game's only taken me about a week so far and it didn't get that good of a start, and I've been working on it less lately - But it's still been developed faster than any other game I've made by:

- Only coding what was needed, keeping the API small until I have a use for functions.
-- Reiterating through objects that are important or whose API is limited by this 'needed' factor and cleaning them up before moving on to the next object

- Writing a test of how I'll use an object and what's to be expected of it before actually coding it, which means always starting at a higher level than what I'll actually be coding.


@vince:

Yeah, I have yet to play around with FSMs, sadly. They don't seem hard, but they seem designed to help with these kinds of processes (although I'm not sure a game engine would be an FSM... but if it works).

I don't use Global Variables at all, thank God. Not sure if I'm up for using enums to describe each index like that - I think I'd rather have a project-portable system such as naming your own sprites and describing them and their importance in another manner.
vdecampo
Posts: 2992
Joined: Aug 07, 2007 23:20
Location: Maryland, USA
Contact:

Post by vdecampo »

@anonymous1337

Take a look at the source for my competition entry Vandris Escape. It implements both the FSM and Sprite ENUMs. While not my best code since I only had 2 weeks to write it, it does show you the basic FSM structure.

Cheers!
-Vince
maddogg6
Posts: 824
Joined: Dec 07, 2005 22:58
Contact:

Post by maddogg6 »

I believe, like any thing designed, will either take *a lot* of trial and error, which can easily lead to boredom - or - *a lot* of planning - which can also lead to boredom as well. The simpler things have all probably been done/over-done by now - conceptually speaking.

My point? - For a game thats too complex for 1 person to handle alone - will likely lead to their boredom. Thus, why most things of any level of complexity is the result of teamwork, way more often than that of an individual.

Sure, some earlier games were written by 1 person - but those were relatively low in complexity compared to whats expected from a game nowadays - from a user *and* the programmer. I mean, how many single individuals made a 'Space Invaders' clone - compared to how many individuals made a complete game in 3D (a doom clone for instance). The artwork alone is a huge stumbling block for many programmers. I imagine, inspiration requires higher complexity to keep a programmers interest more now than when space invaders was conceived.

So, I think team efforts is probably going to be more and more significant - not just in programming, but in many other design/engineering projects in general.
notthecheatr
Posts: 1759
Joined: May 23, 2007 21:52
Location: Cut Bank, MT
Contact:

Post by notthecheatr »

I've long done things bottom-up, but considering I've never actually completed anything (though I'm always working on something) there may be something to it. I think top-down has its place, the main difficulty I have is design, as like you've said it's hard to make a design when you don't know what the classes look like to begin with. What you said about only creating things when you need them sounds to me like a good idea, I waste so much time trying to "make my classes more flexible" when really there are features I've added which I'll never need or use anyways. The idea of only adding things you really need seems like a sound principle, now if I can just figure out how to apply it to my already begun projects and ideas.
anonymous1337
Posts: 5494
Joined: Sep 12, 2005 20:06
Location: California

Post by anonymous1337 »

will either take *a lot* of trial and error, which can easily lead to boredom
It's pretty easy to choose the initial API and reiterate through things over time :P Fun, too, in my opinion, because there's no question of "what do I want to do?" - You know what you want, because the higher level API is how you will work with things.

This goes for planning a project and even a single class. It's just writing the interface first.
or - *a lot* of planning - which can also lead to boredom as well.
That does in fact lead to boredom when you go through your entire design and realize things aren't going to work out in the end :-(, or you have this idea, which you made into a bunch of low level classes, but no example of how it's going to be used.

This often requires a rewrite, where you have to pretend your libraries don't exist and write the code how you would imagine it to run if you did things differently.

As for team effort, I agree, but it's darned hard to find people who are willing to contribute here. I think it's best to make something for yourself, which you find interesting, and just hope to God that someone else might want to contribute as well :D
The idea of only adding things you really need seems like a sound principle, now if I can just figure out how to apply it to my already begun projects and ideas.
Take your highest level objects, and write an example using them as if their API didn't exist, then re-evaluate their classes and rewrite their APIs.
notthecheatr
Posts: 1759
Joined: May 23, 2007 21:52
Location: Cut Bank, MT
Contact:

Post by notthecheatr »

Thanks, that's perfect!
Lachie Dazdarian
Posts: 2338
Joined: May 31, 2005 9:59
Location: Croatia
Contact:

Post by Lachie Dazdarian »

No global variables?!? Shees…I'll never get these programing geek taboos, like no GOTOs.
vdecampo
Posts: 2992
Joined: Aug 07, 2007 23:20
Location: Maryland, USA
Contact:

Post by vdecampo »

@Lachie

They're not just toboo, or bad practice to have globals. It actually makes debugging an application easier because you KNOW that if there is an error in a sub/function, that the bug is either in the actual sub/function or the values passed to it. With a global var, you have to look everywhere that var could possibly be modified to a value that would cause the sub/function to fail.

Also, it forces you to write code that is more portable and reusable.

IMO

Cheers!
-Vince
Lachie Dazdarian
Posts: 2338
Joined: May 31, 2005 9:59
Location: Croatia
Contact:

Post by Lachie Dazdarian »

Well, perhaps someone could write a tutorial for us programming dummies "How not to use global variables - Examples in application".

Thank you.

;)
anonymous1337
Posts: 5494
Joined: Sep 12, 2005 20:06
Location: California

Post by anonymous1337 »

Lachie, where are you ever tempted to use Global Variables? I could show you alternatives to whatever it is you're doing...
maddogg6
Posts: 824
Joined: Dec 07, 2005 22:58
Contact:

Post by maddogg6 »

Lachie Dazdarian wrote:Well, perhaps someone could write a tutorial for us programming dummies "How not to use global variables - Examples in application".

Thank you.

;)
I dont beleive that is practical - it usually/mostly affects larger projects - the things harder to debug.

But perhaps pointing out source code for projects that use and do not use global vars and GOTO's - then intentionally introduce a bug - and have the 'student' try to find it and correct it. To see the contrast between the opposing concepts, would be more effective.

Otherwise, there is plenty of existing writing on these concepts in general.
http://computer-programming-tutorials.s ... rogramming
I thought did an OK job explaining some stuff. As a for instance.
(edited becuase I didnt think before I typed)
This explains scope pretty well too
http://www.freebasic.net/wiki/wikka.php ... iableScope

So, understanding how the compiler converts HL into machine code (and how a CPU works_, would help better understand the 'why it is evil'. But becuase BASIC starts with 'Beginner' - its more likely 'its evil' will be more effective in promoting good habits than attempting to expalin all the mechanics behind the 'why'.
Last edited by maddogg6 on Dec 28, 2007 19:42, edited 1 time in total.
notthecheatr
Posts: 1759
Joined: May 23, 2007 21:52
Location: Cut Bank, MT
Contact:

Post by notthecheatr »

There are times it is proper to use global variables, particularly in a library. However, they generally aren't recommended.

For example, if I have a global variable someVar and 20 different subs in my program access someVar, then when I find an error in someVar I have to find which of those subs it is that makes someVar the wrong value. Whereas if we just have single procedures modifying their local variables you only have to debug the single procedure.

Like anonymous1337 said, if you give an example we can show alternatives. There are indeed times when you would use global variables, but those times are somewhat rare. There are almost always better/safer alternatives.

I really should write a tutorial on scope though, I'm glad you mentioned it.
Post Reply