- Joined
- Mar 21, 2017
For example, supposeCool, I'll play around with this.
I saw suggestions elsewhere online to use a class to deal with the UI, but every tutorial out there seems to give such unhelpfully trivial examples of classes ("Dog" and "Cat" are members of the class "Animal", etc.) that I couldn't figure out how they'd actually be applied in a real program.
Label
is UI component that simply displays a piece of text, you could make a subclass CalcScreen
that that displays the calculator screen and handles the specific logic of formatting the text. Generally a superclass is a more general type, and a subclass is a more specific type. The advantage of subclassing is that you can reuse the functionality of the superclass between subclasses.
Python:
# the actual project is in C#, so it translates a little awkwardly
class IControl: # represents a control source, like a button press or mouse movement
@abstractmethod
def getSubControlCount(self) -> int: # controls may contain sub-controls
pass
@abstractmethod
def GetSubControl(self, id:int) -> IControl:
pass
class IDigitalControl(IControl): # represents a control with a boolean value
@abstractmethod # such as a button, or joystick direction
def getValue(self) -> bool:
pass
class IKeyboardControl(IControl): # represents a keyboard
class KeyControl(IDigitalControl): # individual keys of the keyboard are sub-controls of the keyboard
def getValue(self) -> bool:
#...
def GetKey(self, key:IKey) -> KeyControl: # get a key matching `key`
#...
def getKeyCount(self) -> int:
#...
def getSubControlCount(self) -> int: # implement the IControl class
return self.getKeyCount()
def GetSubControl(self, id:int) -> IControl:
return self.GetKey(id)
class WindowsKeyboardControl(IKeyboardControl): # A specific kind of keyboard, with a windows layout
#...
IControl
represents a user input source very abstractly, and all it specifies is that a control may contain other controls. IDigitalControl
is a little more concrete, it represents an input like a button that may be on or off. IKeyboardControl
represents a abstract keyboard with no specific layout. WindowsKeyboardControl
represents a keyboard with a windows compatible layout.By making
WindowsKeyboardControl
a subclass of IKeyboardControl
, we can use WindowsKeyboardControl
interchangably as a IKeyboardControl
, the advantage of this is that our code can deal with keyboards without having to depend on the specific layout. Another advantage is that since both xbox facebuttons and keyboard keys are IDigitalControl
we can use them interchangeably in our code;
Python:
class IKey: # a set of singletons describing keyboard keys
class Alpha(IKey):
global a = Alpha('a')
global b = Alpha('b')
#...
class NumPad(IKey):
global num0 = NumPad('0')
global num1 = NumPad('1')
#...
class XBoxController(IControl): # control representing an xbox controller
class FaceButton(IDigitalControl): # control representing an individual facebutton
def getValue(self) -> bool:
#...
xButton = FaceButton(...)
#...
#...
# define a "behavior" that will use our controls
class MovementBehavior: # a "behavior", which can be added to an Entity
def __init__(self, speed:float, forward:IDigitalControl, right:IDigitalControl):
self.speed = speed
self.forward = forward # since forward and right are digital controls, any kind of digital control can be used
self.right = right
def Execute(self, target:Entity): # periodically called by the game loop
forwardAmnt = self.forward.getValue() ? 1 : 0
rightAmnt = self.right.getValue() ? 1 : 0 # poll the controls
moveVector = Vec2(forwardAmnt, rightAmnt) * self.speed
target.Move(moveVector) # move a certain amount based on the controls
#...
# keyboard and controller defined in the start up code
myPlayer = Entity() # create a new entity and add our behavior
myPlayer.AddBehavior(MovementBehavior(0.5,
keyboard.GetKey(IKey.Alpha.w), # move forward with 'w'
controller.xButton)) # move sideways with x on the controller
myPlayer
that moves depending on button presses, but since our buttons are all IDigitalControl
we can use buttons from anything be it a joystick, a controller, a mouse, or a keyboard. Having a superclass creates a symmetry between our types which allows a lot of code reuse.In my opinion a proper example of an unfairly maligned language feature would be goto. It's common beginner advice that you should never use a goto, but that is simply cargo cult foolishness. Gotos are a good backup tool for when prettier control flow fails;
C#:
// A function which attempts to find a node matching a key within a binary tree
// that may be concurrently modified. If a node is removed while our function
// is visiting it then we will need to restart the search.
bool TryFindTreeConcurrent(Node start, Key target, out Node result) {
RETRY_TOP:
var cur = start;
while (CheckValid(cur)) {
if (!TryReadKey(cur, out var curKey) // Try to get the key of the current node
goto RETRY_TOP; // May fail due to datarace
var cmp = Compare(target, curKey);
if (cmp < 0) { // If our target was less than the current node
if (!TryGetLeft(cur, out var left)) // Try to get the left node
goto RETRY_TOP; // May fail due to datarace
cur = left;
} else if (cmp > 0) {
if (!TryGetRight(cur, out var right))
goto RETRY_TOP;
cur = right;
} else { // Found our target
result = cur;
return true;
}
}
result = invalidNode; // Return false if either no node matching target exists, or start no longer exists
return false;
}
//...
// Refactored to remove the goto
bool TryFindTreeConcurrent(Node start, Key target, out Node result) {
while(true) {
var cur = start;
bool retry;
while (retry = CheckValid(cur)) {
if (!TryReadKey(cur, out var curKey)
break;
var cmp = Compare(target, curKey);
if (cmp < 0) {
if (!TryGetLeft(cur, out var left))
break;
cur = left;
} else if (cmp > 0) {
if (!TryGetRight(cur, out var right))
break;
cur = right;
} else {
result = cur;
return true;
}
}
if (!retry) // If the inner loop breaks, retry will be true
break; // If the loop condition fails, retry will be false
}
result = invalidNode;
return false;
}
Stackoverflow geniuses will tell beginners to avoid goto at all costs, and as a result you end up with exceptions as a means of breaking out of multiple loops and other such haram behavior.
Last edited: