OOP Guidelines
BlitzMax Forums/BlitzMax Tutorials/OOP Guidelines
| ||
After seeing several mis-applications of oop, I've decided to write a small tutorial about how to use oop to properly create types. Naming Types For type naming I dont really have a standard. It is most natural to name the type something like "Car" or "House", but the standard used in the standard modules "TBank" or "TList" is very nice since you can have an instance called "Bank" or "List". It's your decision. Elements For elements (fields, globals, constants, functions, methods), just have the name of the element. Nowhere in the element should there be the type name or an abreviation of the type name. Bad: Type TCar Field CarSpeed Field CarWidth Field Method DriveCar() 'BLAH EndMethod Method HonkHorn() 'BLAH EndMethod EndTypeGood: Type TCar Field Speed Field Width Method Drive() 'BLAH EndMethod Method HonkHorn() 'BLAH EndMethod EndTypeThis is often a problem when migrating to an OOP language from a procedural language like all previous versions of blitz. Grouping Globals, Constants, and Fields You should group similar elements of the same element type that are on the same line. For instance: Before: Field X# Field Y# Field Z#After: Field X#,Y#,Z#You can also group groups of similar elements. For instance: Before: Field X#,Y#,Z# Field Speed Field Width#, Height#, Depth#After: Field X#,Y#,Z# Field Width#, Height#, Depth# Field Speed Properties Properties are times when you have some fields that when affected or even the value is retrieved, code is executed, fields change. Although blitzmax doesn't support actual C#/Java properties, they can still be done in a way with two methods. Also, properties are most useful if you make the actual field private so the user cannot edit it. Sadly, BlitzMax does not support this either. MARK SIBLY > ADD PRIVATE ELEMENTS AND OVERLOADS + MAYBE PROPERTIES If not I might add them to my preprocessor if it gets off the ground ;) Okay, so properties using current BMAX stuff: Type Car Field Speed# Field MaxSpeed# Method SetSpeed(Value#) If Value#>MaxSpeed# Then Speed#=MaxSpeed# Elseif Value#<0 Then Speed#=0 Else Speed#=Value# 'Range Check End Method Method GetSpeed#() Return Speed# EndMethod EndTypeSome OOP guidelines for languages without properties suggest making all of your fields private and creating methods like this for all of them (sometimes one set per group). This allows you to later on add code that executes when a field is changed, while maintaining backward compatability. This is what C#/Java properties have solved. You can leave all of your fields as fields, and later on convert them to properties, allowing you to execute code, maintain backwards compatability, have sleek field style access, without bloated classes. Inheritance I'm going to assume you read Mark Sibly's Intro to OO. Inheritance shouldn't be applied to every situation in which you have the same fields / functions / methods / constants / globals / methods. When this statement is true - "<Derived> is a type of <Base>", you should use inheritance. As far as I know BMax doesn't have multiple inheritance. This is no problem because in most cases you only want single inheritance, and multiple inheritance is misuse. There are however some rare situations where a type is a type of two other types (tounge twister here), where those other types are not types of eachother. Anyway, If you have multiple types with similar elements, however they are not types of a type defined by those elements, create your type and include it as a field in all the other types. For instance: Type Vector Field X#,Y# End Type Type SpaceObject Abstract Field Location:Vector 'Strictly not a vector, but hey Field Speed:Vector Method Update() Location.X:+Speed.X Location.Y:+Speed.Y EndMethod End Type Type Ship Extends SpaceObject Abstract 'Ship is a type of space object Field StartHealth#=0, StartArmor#=0 Field Health#, Armor# End Type Type Cruiser Extends Ship 'Cruiser is a type of ship Field StartHealth#=200, StartArmor#=20 End TypeAbstraction Ok, really this should be renamed templation or something, the keyword being "template". However, this is not so, yet. Anyway, abstract should only be used when the inheritance rule of above applies, and also, when you will never actually want to create a base class. At this point you make it abstract. Be sure to make the classes deriving from it final unless you want to have even further inheritance. One application worthy of abstract is creating a module in which one class can do many different things depending on how applied. Basically, a class for handling similar stuff (TStreamFactory), when the actual implementation is totally different (TRamStreamFactory). |
| ||
Type Vector Field X#=67,Y#=54 End Type Type SpaceObject Abstract Field Location:vector= New Vector 'Strictly not a vector, but hey Field Speed:Vector= New Vector Method Update() Location.X:+Speed.X Location.Y:+Speed.Y Print location.x Print location.y EndMethod End Type Type Ship Extends SpaceObject Abstract 'Ship is a type of space object Field StartHealth#=0, StartArmor#=0 Field Health#, Armor# End Type Type Cruiser Extends Ship 'Cruiser is a type of ship Field StartHealth#=200, StartArmor#=20 End Type a:Cruiser=New Cruiser a.Update() just incase anybody problems with the last method you have to add the New ect Command aswell |
| ||
Types and methods are good. Thanks for this! |
| ||
Good stuff, Bot Builder. I agree with you about the importance of privacy; I'm experimenting with starting all of my "private" Field names with an underscore ("public" ones, if any, don't have this). The same goes for any Functions or Methods intended to be private within a Type. Since I think that starting names with underscores is hideously ugly, I'm motivated never to use them outside of Type/End Type blocks and to use Methods to set and retrieve private Field values :). Also, by putting Consts inside of types, enumerated types can be simulated (at least until actually added) and kept separate. So, with: Strict Type TStockMarket Const LOW = 1 Const MEDIUM = 2 Const HIGH = 3 Field _Confidence Method SetConfidence( n ) Assert ( n >= LOW ) And ( n <= HIGH ), "Confidence out of range" _Confidence = n End Method Method GetConfidence() Return _Confidence End Method End Type Type TRadio Const VERYLOW = 1 Const LOW = 2 Const MEDIUM = 3 Const HIGH = 4 Const VERYHIGH = 5 Field _Volume Method SetVolume( n ) Assert ( n >= VERYLOW ) And ( n <= VERYHIGH ), "Volume out of range" _Volume = n End Method Method GetVolume() Return _Volume End Method End Type Local DowJones:TStockMarket = New TStockMarket DowJones.SetConfidence DowJones.HIGH If DowJones.GetConfidence() = DowJones.HIGH Then Print "Buy!" Delay 1000 Local Radio:TRadio = New TRadio Radio.SetVolume Radio.HIGH If Radio.GetVolume() = Radio.HIGH Then Print "That's kind of loud!" Delay 1000 Radio.SetVolume( 0 ) 'out of range errorthere's no confusion between the stock market's HIGH and the radio's HIGH. |