game programming 04 - style & design principles
TRANSCRIPT
Game ProgrammingStyle & Design Principles
Nick Prühs
Objectives
• To get an idea of good code style and structure in general
• To understand the importance of consistent naming and code
conventions
• To learn how to property design types and members
2 / 61
What is “good code”?
• Straight-forward and obvious
You’ll spend far more time reading code than writing.
You’ll spend much time reading code you didn’t write.
• Loose coupling
• Well tested
• Reused code
• Efficient (?)
3 / 61
How to achieve “good code”?
Source: http://xkcd.com/844/ 4 / 61
How to achieve “good code”?
• Pair programming
• Code Reviews
• Client-first programming, Test-driven development
• Refactoring
• Unit Testing
• Great tools
• Static code analysis
5 / 61
Naming Conventions
• Greatly increase readability and usability
• Differ from language to language
We’ll take a look at C#, but many guidelines apply to other
languages as well.
6 / 61
C# Capitalization
• PascalCasing
Namespaces
Types
Member Names
• camelCasing
Method Parameter Names
7 / 61
Capitalization of Acronyms
• Both characters of two-character acronyms
Example: IO
• Only first character of three-character acronyms
Example: Xml, Html
• Never for camelCased identifiers (such as parameters)
8 / 61
Word Choice
• Easily readable:
HorizontalAlignment instead of AlignmentHorizontal
• Favor readability over brevity:
CanScrollHorizontally instead of ScrollableX
• No underscores
• No Hungarian notation
• No abbreviations
Abbreviations don’t work well with IDEs.
• Semantic names
GetLength instead of GetInt
9 / 61
Namespace Names
<Company>.<Product>.<Feature>.<SubNamespace>
• Plural names where appropriate
Example: System.Collections
• Don’t use the same name for namespace and type
• Don’t use common names such as Element, Component
10 / 61
Type Names
• Nouns for classes and structs
Example: List, Vector
• Derived classes can end with name of base class
Example: ArgumentException
• Adjectives for interfaces
• Prefix interface names with ‘I’
Example: IComparable
• Use descriptive names for type-parameters
Dictionary<TKey, TValue> instead of Dictionary<K, V>
• Singular names for non-flags enums
Color instead of Colors
11 / 61
Member Names
• Verbs for methods and events
Example: AddComponent, ComponentAdded
• Nouns or adjectives for fields
Example: Duration, Invulnerable
• Nouns for properties
• Plurals for collections
Items instead of ItemList
12 / 61
Boolean Trap #1
• Be positive!
Enabled = true instead of Disabled = false
CaseSensitive = true instead of CaseInsensitive = false
13 / 61
Tabs vs. Spaces
• Holy war between two fractions of programmers
• Some are for tabs…
Seperation of visual representation from data
Customizable
Faster
Specifically meant for indentation
• … while others are for spaces.
Always one column
Supported by IDEs anyway
14 / 61
Tabs vs. Spaces
Most important rule:
Stay consistent.
… or it might even blow up your version control.
15 / 61
Tabs vs. Spaces
„That said, only a moron would use tabs to format their code.”
- Jeff Atwood
16 / 61
Type Design – Class vs. Struct
Make a type a struct instead of a class if it…
• Logically represents a single value,
• Has a valid state if all data is set to zero,
• Has an instance size < 16 bytes,
• Is Immutable, and
• Won’t have to be boxed frequently
17 / 61
Type Design – Class vs. Struct
Make a type a struct instead of a class if it…
• Logically represents a single value,
• Has a valid state if all data is set to zero,
This is the case for array initialization, for example.
• Has an instance size < 16 bytes,
• Is Immutable, and
• Won’t have to be boxed frequently
18 / 61
Type Design – Class vs. Struct
Make a type a struct instead of a class if it…
• Logically represents a single value,
• Has a valid state if all data is set to zero,
• Has an instance size < 16 bytes,
Value type assignments copy all values
• Is Immutable, and
• Won’t have to be boxed frequently
19 / 61
Type Design – Class vs. Struct
Make a type a struct instead of a class if it…
• Logically represents a single value,
• Has a valid state if all data is set to zero,
• Has an instance size < 16 bytes,
• Is Immutable, and
Passing (and returning) by value implicitly creates a copy
Value types that can be changed will be confusing!
• Won’t have to be boxed frequently
20 / 61
Type Design – Class vs. Struct
Make a type a struct instead of a class if it…
• Logically represents a single value,
• Has a valid state if all data is set to zero,
• Has an instance size < 16 bytes,
• Is Immutable, and
• Won’t have to be boxed frequently
Happens when they’re cast to a interface
Allocated on heap and garbage collected, then!
21 / 61
Type Design – Class vs. Interface
• Use interfaces for polymorphic value type hierarchies
Example: IComparable, IConvertible
• If an interface does more than exactly one thing, that’s a warning
sign.
22 / 61
Type Design – Class vs. Interface
23 / 61
Type Design – Class vs. Interface
• Favor (abstract) classes over interfaces
Adding members to an interface is a breaking change!
Seperating of contract from implementation with an interface is a
myth…
… whereas doing so with an abstract class is not.
24 / 61
Type Design – Enums
• Favor enums over static constants
• Provide a zero value for simple enums (e.g. None)
• Don’t use enums for open sets
• Don’t use flag enums if some combinations can be invalid
25 / 61
Member Design – Property vs. Method
Make a member a method instead of a property if it…
• Is significantly slower than a field access would be,
• Is a conversion operation (such as ToString),
• Returns a different result each time it is called,
• Has an observable side effect,
• Returns a copy of an internal state, or
• Returns an array
26 / 61
Member Design – Properties
• Preserve previous value if a property setter throws an exception
• Don’t throw exceptions from property getters.
Use a method in that case.
27 / 61
Gotcha!
Don‘t throw exceptions from
static constructors!
28 / 61
Member Design – Methods
• Shorter overloads should simply call longer ones
public int IndexOf(string s)
public int IndexOf(string s, int startIndex)
public int IndexOf(string s, int startIndex, int count)
• Use descriptive parameter names
• Avoid varying names or inconsistent ordering
• Make only the longest overload virtual
• Allow null to be passed for optional parameters
Default arguments are not CLS-compliant!
29 / 61
Member Design – Extensions
Use an extension method, if ...
• It adds functionality relevant to every implementation of an interface,
or
• An instance method would introduce unwanted dependencies
30 / 61
Member Design – Operator Overloads
• Make sense for types that feel like primitive types
• Should be defined symmetrically
• Provide methods with friendly names, as well
Some languages don‘t support operator overloads.
31 / 61
Member Design – Parameters
• Use the least derived required paramter type
• Validate all arguments
• params keyword is useful for arbitrary (but small) numbers of
parameters
Pay attention on parameter order
Be aware that null is a valid params array argument
32 / 61
Boolean Trap #2
Guess what this code means?
widget.repaint(false);
33 / 61
Boolean Trap #2
Guess what this code means?
widget.repaint(false);
• Function argument is called immediate
true means immediate painting
false means deferred painting
• Better use method overloads or enums!
34 / 61
Boolean Trap #3
Guess what this code means?
var opacitySlider = new Slider(true);
35 / 61
Boolean Trap #3
Guess what this code means?
var opacitySlider = new Slider(true);
• Function argument is called horizontal
true means horizontal slider
false means vertical slider
• Better use subclasses!
„Heck, who knows someday you’ll need a diagonal slider!”
36 / 61
Boolean Trap #4
Guess what this code means?
stackView.updateHeight(false);
37 / 61
Boolean Trap #4
Guess what this code means?
stackView.updateHeight(false);
• Immediate or not?
• Don‘t update height?
• Update width???
38 / 61
Boolean Trap #5
The more, the merrier!
event.initKeyEvent("keypress", true, true,
null, null, false, false, false, false, 9,
0);
39 / 61
Exceptions
• Help you deal with any unexpected or exceptional situations that
occur when a program is running
• Exceptions are types that all ultimately derive from
System.Exception.
• Generated by the common language runtime (CLR), by the .NET
Framework or any third-party libraries, or by application code.
40 / 61
Benefits of Exceptions
• Integrate well with object-oriented languages
Does not require changing the method signature
• Can’t be ignored, whereas error codes can
• Exception objects contain detailed information about the error, such
as the state of the call stack and a text description of the error.
• Possibility of defining an Unhandled Exception Filter (UEF)
• Supported by debuggers
41 / 61
Gotcha!
Don’t return error codes!
42 / 61
Exception Design
• Exceptions provide consistent error reporting
No bool return value required
No HRESULT
No GetLastError
• Don’t return error codes.
If your error code is for the developer, add additional information
to the exception message instead.
If your error code is for the exception handler, add a new
exception type instead.
43 / 61
Throwing Exceptions
• Throw the most specific exception that makes sense.
ArgumentException
ArgumentNullException
• Provide rich and meaningful error messages.
• Introduce a new exception type only if it should be handled
differently than existing exception types.
44 / 61
Handling Exceptions
• Use a try block around the statements that might throw exceptions.
• Once an exception occurs in the try block, the flow of control jumps
to the first associated exception handler that is present anywhere in
the call stack.
• If no exception handler for a given exception is present, the program
stops executing with an error message.
45 / 61
Handling Exceptions
Unhandled exception reported by Microsoft Visual Studio 2012
46 / 61
Exception Best Practice
47 / 61
Exception Best Practice
• Do not catch an exception unless you can handle it and leave the
application in a known state.
• Do not catch non-specific exceptions, such as System.Exception.
• Use a finally block to release resources, for example to close any
streams or files that were opened in the try block.
48 / 61
Gotcha!
Never use empty catch blocks!
49 / 61
Common Exceptions
• Thrown by your application or the framework
InvalidOperationException
ArgumentException
ArgumentNullException
ArgumentOutOfRangeException
• Thrown by the CLR
NullReferenceException
IndexOfOutRangeException
StackOverflowException
OutOfMemoryException
50 / 61
Collection Design
• Inherit from Collection<T>, ReadOnlyCollection<T> or KeyedCollection<TKey, TItem>
• Implement IEnumerable<T> Allows foreach iteration of your collection.
• Implement ICollection<T> or IList<T> where it makes sense
• Implement an indexer where it makes sense
51 / 61
Gotcha!
Don’t implement ICloneable!
52 / 61
The interface ICloneable
• Contract doesn’t define whether cloning an object returns a deep or
shallow copy
• Define your own Clone method if required, without implementing the
interface
53 / 61
Hint
Implement IEquatable<T>
on value types!
54 / 61
The interface IEquatable
• Defines a method for determining equality of object instances
• Implement on value types to prevent boxing
• Override Object.Equals, == and != operators as well
55 / 61
Hint
Implement IComparable<T>
where it makes sense!
56 / 61
The interface IComparable
• Defines a type-specific comparison method for ordering or sorting
type instances.
• Override <, >, <= and >= operators as well
57 / 61
Equals, GetHashCode & ToString
• If Object.Equals returns true for any two objects, GetHashCode must
return the same value for these objects.
Dictionaries, which are implemented as Hashtables in .NET, will
place two equal objects in different buckets otherwise, and
lookup might fail in that case.
• Override ToString wherever it makes sense.
Provides more useful debug output.
58 / 61
Gotcha!
GetHashCode must return the exactly same value across the
lifetime of an object!
59 / 61
References
• Stack Overflow. What does a good programmer's code look like? http://stackoverflow.com/questions/366588/what-does-a-good-programmers-code-look-like, April 2017.
• Cwalina, Abrams. Framework Design Guidelines. 2nd Edition. Addison-Wesley, 2011.
• Hidayat. hall of api shame: boolean trap. http://ariya.ofilabs.com/2011/08/hall-of-api-shame-boolean-trap.html,August 24, 2011.
• Atwood. Death to the Space Infidels! http://www.codinghorror.com/blog/2009/04/death-to-the-space-infidels.html, April 13, 2009.
• MSDN. System Namespace. https://msdn.microsoft.com/en-us/library/System(v=vs.110).aspx, April 2017.
60 / 61
5 Minute Review Session
• Name a few characteristics of good code!
• How can you achieve good code?
• Tabs or spaces?
• When should you use a struct instead of a class?
• When should you use a method instead of a property?
• Name the three common interfaces and base classes that can be used for collections in .NET!
• What is the main purpose of the interface IEquatable?
• What is the main purpose of the interface IComparable?
• How are Equals and GetHashCode related to each other?