Questions about code design
SionI have a few questions concerning how the best code design is achived. First off, the level of abstraction. What level of abstraction should be used? Examples: a) Unit1.CanSeeUnit(Unit2) b) Unit1.CanSeeUnit(Xpos: 100, Ypos: 100, Width: 10, Height: 10) c) The function in answer b) in a module, and then called by the function in a) from a class Second, class or not class. Which functions should be contained within a class, and which should be accessed from a module? Examples: a) Unit1.CanSeeUnit(Unit2) b) CanSeeUnit(Unit1,Unit2) Third, parameters. How should class-parameters be passed? Examples: a) Sprite.ReCalc(State: ATTACK) b) Sprite.State = ATTACK Sprite.ReCalc() Fourth, custom types. Should custom data types be passed to classes? Examples: a) Pos.X = 5 Pos.Y = 10 Unit.Pos = Pos b) Unit.PosX = 5 Unit.PosY = 10 Fifth, level of class hierarchy. When dealing with classes, how much should the classes automate each other? Examples: a) Player.ReCalcArmies b) For Each Army in Player Army.ReCalcUnits Next c) For Each Army in Player For Each Unit in Army Unit.Recalc Next Next I hope some experinced programmers will give me some input. You don't have to know the correct solution to the questions, and there may no be a single correct answer, but a few guidelines to designing code would be nice! Of cause others are also invited to post any questions concerning code design that they may have. Thanks in advance [:)]
sdwDo whatever floats your boat.
Sr. GuapoYeah, just do whatever makes it easier for you to use while still being functional. It isn't a bad idea to pick up some good habits, though, in case you ever work on a team or release code to others. Edit: Woohoo!!! 250...
Eric ColemanThis is a good article to read http://www.geocities.com/tablizer/oopbad.htm There are a lot of links in the article, so to get the full whole idea you need to a lot of reading. In the end, it all depends on your own preferences. Read this, http://www.vb-helper.com/rant_vb_net_not_oo.html , more for its comparisons on design instead of a rant on vb.net.
quote:
Having more objects is not a goal. If creating more objects was a goal, you could do away with constants such as 37 and 12 and make objects instead. You could create an instance of the 12 object and an instance of the 37 object and then use the 12 object's Plus method to add then two together. In some weird sense, that would be more "object-oriented" but not in a particularly useful way.
Lachlan87The main thing is to try to be consistent. If the only way to give your player a laser gun is to write: [code]GiveWeapon(LaserGun, Player1)[/code] And the only way to give the player a machine gun is: [code]Player1.Weapon.Add(MachineGun)[/code] Then your not being consistent, and it will be hard for others (and yourself) to pick up on your coding scheme. One of the things I like about programming things into objects is--and this will probably sound silly to you--because then I can "explore" them with the "intelli-sense" feature of visual studio. The bigger my projects get, the harder it is for me to remember the exact name of all my functions, variables, etc. If I have them all under more general objects, it cuts back on a lot of guess-work for me. But I think if you are familliar with both procedural and OO programming, you will find that you get a kind of a feel for what would be best programmed which way. Of course, there will always be someone who will disagree with you, but if there was a "right" way to do everything, than there would be no art to programming at all.
Eric ColemanJust a programming tip for you, You can use Intellisense for standard modules as well. Just type the name of the module, then a . and then you should see a list of properties, methods, and public variables and functions. Yes, you can use properties in standard modules. For those that are OOP purists, just skip over this message. There is a point when OO design complicates things too much. When I was programming my game Gladiator I looked into using the skeletal animation code from source forge. It is called "Cally3D" or something like that. The demo's look nice, but when I downloaded the code it was extremly complicated. I studied it for a really long time trying to figure out exactly what was going on, but everything was so deeply hidden in many levels of abstraction it became useless to try to modify it to suit my needs. Even though OOP is supposed to encapsulate concepts, it failed miserably in this case. There was so much inheritance and overloading that given a single line of code you had to trace back through a large spider-web hierarchy of dependent classes. I eventually realized that I was getting nowhere with the code, so I wrote my own version in a procedural format. It turned out to be a lot less code and a lot less complicated. Instead of treating scalars, vectors, and matrices as "objects", I treated them as numbers, which they are. Instead of a hundred different functions, I only needed a few. Just so this isn't totally negative, if you don't understand the mathematics behind such stuff and you have no need to modify the code to suit your needs, then OOP is fine, just so long as you don't have to worry about it. Unfortunately, if you're programming a game, then you will have to worry about it. Even though consistancy is important, I do think there is a middle ground for using both procedural and object oriented design in a program. For example, I used classes to wrap up the procedural code that I wrote for my particle system.
Lachlan87Thanks for the tip, I hadn't known that!
cjb0087forced OOP, the reason nobody(sane) likes java
PeterBasically, the point is to make your code as simple and elegant as you can get it. Before going nuts with classes try programming with user defined types and functions in modules, that actually gives you a pretty good idea of how to organize things, after it becomes a lot easier to create and organize classes in a meaningful and useful way. Good luck with coding, its good to see people interested in not just writing code but writing good code =)
SionOkay, thanks to everyone for the input. The main reason I made this thread was because I thought some code design approaches would work better than others. Both in terms of clearity, matainability and efficientcy. I had also presumed that larger blocks of code should be called from modules instead being called from inside classes to avoid storing many instances of the same code block in memory, but you havn't mentioned that, so I guess it dosn't matter. The sum of the answers must be that code design is equally good regardless of which designing approach that has been used. That code should be designed as needed, but the use of classes should be limited to instances where they provide a significiant advantage to avoid cluttering up the code with too many classes. In relation to my design questions in the first post of the thread, I've decided to use the following design in my current game: Abstraction:
quote:
a) Unit1.CanSeeUnit(Unit2) b) Unit1.CanSeeUnit(Xpos: 100, Ypos: 100, Width: 10, Height: 10) c) The function in answer b) in a module, and then called by the function in a) from a class
Chosen: c) Clear and easy-to-use interface in classes while still keeping the larger blocks of code in modules, just in case that it is more memory efficiant. Classes vs. modules:
quote:
a) Unit1.CanSeeUnit(Unit2) b) CanSeeUnit(Unit1,Unit2)
Chosen: a) Well, I already addressed this, but besides providing a clear class interface it's also more logical to use the [UNIT].[FUNCTION] notation. In my mind, anyway. Parameters:
quote:
a) Sprite.ReCalc(State: ATTACK) b) Sprite.State = ATTACK Sprite.ReCalc()
Chosen: b) Setting only one parameter at a time also provides a clearer class interface - both internally and externally. The different methods also seem a lot less complicated when most of the parameters have been set elsewhere. Custom types:
quote:
a) Pos.X = 5 Pos.Y = 10 Unit.Pos = Pos b) Unit.PosX = 5 Unit.PosY = 10
Chosen: b) Using custom data types as parameters and returns from class functions can be very handy in some cases, but I've chosen not to use them. First of all, in many cases using UDTs as parameters just means creating a new variable, jamming all the data in it and then sending it as a parameter, where passing the data would be just as clear. Secondly, I've had some problems passing UDTs to and from classes, and it seems that UDTs would have to be replaced by small classes instead, and this is where things start getting complicated. Thirdly (does that word exist?) many of my parameters and returns are other classes which kind of eliminates this problem. Class hierachy:
quote:
a) Player.ReCalcArmies b) For Each Army in Player Army.ReCalcUnits Next c) For Each Army in Player For Each Unit in Army Unit.Recalc Next Next
Chosen: a) I've chosen to handle as few classes as possible from my main rutine. This will hopefully help keeping the overview of what's going on clear and keeping bugs at a minimum. I'll probably end up with units grouped into a class, and then use those grouped functions such as Enemies.RecalcUnits and Player.RecalcUnits. That's it.
Lachlan87
quote:
Originally posted by Sion
I had also presumed that larger blocks of code should be called from modules instead being called from inside classes to avoid storing many instances of the same code block in memory
If you don't need a new copy of the function or variable for every instance of the object, declare it as Shared (in vb.net, not sure about vb6). That way VB will make all the objects share the function, instead of giving them their own copies. At any rate, that's how I was taught.
SionWhich is the best (clearest and fastest) way to get events from classes in a collection? I'm having a class named cUnits containing a number of cUnit-classes in a collection. I would like each of the cUnit-classes to raise events on special occations, but handling events from arrays or collections of events-raising classes are not very straight-forward in Visual Basic. One approach is to use some Visual Basic modules, found on the Internet, that uses low-level functions to redirect events to allow multiple events to be handled. I am not sure, but I would guess that such techniques are CPU and/or RAM-expensive. And on top of that - it's not very pretty. The approach I'm currently using is calling a public method instead of using events, and using this method as replacement for the regular event. This works fine and has an acceptable level of clearity. But do anyone have other suggestions as to how this go about dealing with this problem?
Eric ColemanThis answer assumes that you're using VB 6. Events in VB6 from activex classes are late bound. You may want to stick with calling public procedures (or friend procedures if you can.) You may want to read this, http://www.elitevb.com/content/01,0038,01/ for more information.
SionYes, I'm using Visual Basic 6.0 [:)] Thanks for the link, Eric, it's very interesting reading. However it's waaay too advanced for me, and it's too much of an overkill in this specific case. I think I'm just going to go with calling public methods, probably creating a public class to recieve the "events" to enable external event-handling. You mention using a friend method to get the raised "events". It's funny, I've never ever used a friend method! I don't know how it has slipped through, but I've never used it, and only seen it used in very very few occations. This even though I've been working with Visual Basic for nearly 6 years now! Well, any blank spot in my Visual Basic knowledge needs to be filled immediately! [;)] I found out - as you may already know - that friend-methods allow the local project to acces the method, but isn't accessable from external callers. This sort of a cross between the private and public method declarations. Something very different from the other method declarations type is - surprisingly enough - speed. (This may not surprise Eric, as he recomended me to use a friend-method, but it surprised ME!). Appearently a friend-method is called ~8 times faster than its public counterparts. Holy moly, that's a lot. I should probably search my current project for methods that can be converted to friend [:p] Description of the speed increase using friend can be found here: [url]http://www.devx.com/vb2themax/Tip/18396[/url]
IodiplinIndeed, good code is essential. I have downloaded so many projects with useful content, and then disregarded them beceause they were so poorly programmed. I myself use only UDTs (I only use classes if I'm using someone elses code [:)]). I can't count how many projects I've started and said "I'm going to be a good OOP programmer and uses classes." I would start with a huge list of classes. After a little while, I'd remove a few, and then some more. IN EVERY SINGLE CASE, I eventually removed ALL classes and went straight back to UDTs. I'm not saying OOP is bad, it's just not how my mind works. At any rate, I have also come to find that commenting is not only a good habbit, it's a necessary practice at all times. I am consistently commenting all my code. There is not a single varaible in my projects that does not have a single quote directly after it (except for counter variables, perhaps [:P]). When I go back to my code after a long break, it is much easier to become reaquainted. I would like to say one final thing: keep your code looking nice. There's nothing like scrolling down a 100 page module and seeing good tabbing, even spacing, LOTS of spacing (to keep code easily legible) and well aligned code. Things like: [code] Dim a As Single ' Angle of rotation Dim i As Long ' Counter Dim SpriteFrame As Long ' Current sprite frame [/code] can simply make you feel more confident when seeing the code. It just makes you feel better. For me that is necessary considering sloppy code makes me feel depressed, seriously. Just my 2c, but in my oppinion, you can ruin good code by writing it sloppily. [;)] P.S. Sorry, I went a little off-topic.
sdw
quote:
You mention using a friend method to get the raised "events". It's funny, I've never ever used a friend method!
Don't feel bad, neither have I! Up until now I have no idea it exist. Great find, and thanks for posting that. I wonder if Almar has any idea about this :)
quote:
At any rate, I have also come to find that commenting is not only a good habbit, it's a necessary practice at all times.
I actually don't comment unless it's for someone else's benefit, or if the comment says nothing more than "Left off here" or "my todo list". I like to use my own system of a Hungarian Notation with descriptive names. So instead of commenting after where the variable is declared, I would do it this way: [code] Dim sAngle as Single Dim i as long Dim lFrame as long [/code] Because to me comments make things look sloppy. Although I absolutly agree that meaningful spacing between lines of code, tabbing, and dividing your code into blocks can help a great deal in reading your own code instead of putting all your code into one chunk.
Eric ColemanThere should be a mix between both methods. It's very helpfull if variable names can be their own comment, i.e. descriptive variable names. However, you should still comment on the overall behavior of functions, subs, etc. I personally like to keep track of what the function does. Some of my functions can get rather complicated. Things like LengthOfVector is a self descriptive function, but something like CalculateQuantVectorField might not make as much sense if you haven't looked at the code in a long time. Another thing I like to do is document what other functions/events/etc call a particular piece of code. This is a tremendous help when you've forgot about the overall flow of a program. If you have Visio or something similar, that can help too, but having the functions document program flow really helps a lot. As an example, look at the one and only submission I made to planet source code.