Yep, I started over again. Almost one month ago, I wrote that I had re-built how everything worked because it was becoming very repetitive.
I deleted nearly 95% of the item generation code and replaced it.
That re-write meant that I combined all of my item generators… at that time it was weapon, and armor only but I was working on the shield code… into a single item-creation routine that could handle all the edge cases of the different items in one “elegant” solution. I put that in quotes because in my mind, it really was elegant. It gracefully handled almost anything you could throw at it and would try it’s darnedest to return an item as close as possible to the one you requested.
I also made the argument for why I was using associative arrays.
What I’ve been building with my item code is an associative array of values that represent that item in game turns… I’m building an Object without using Object Oriented code. This isn’t the point in the article where I confess my sins and tell you that I have converted my code to OOP. What I have done, however, is use OOP principals to model my code.
Well, this time, I have deleted 100% of the original item generation code and replaced all of it… with objects.
I Finally Gave Up on Arrays
I’ve shared a whole lot over the years about why I don’t program using Object Oriented Programming (OOP) principals. Namely, I have a hard time thinking of operations that way. My introduction to programming was BASIC and Assembly and working through a program from initialization to completion has always been how I think about software. This is called a procedural approach, and ALL software is procedural in the end. You can write software in lots of different paradigms, but your compiler turns that code into a procedure that the chipset can understand. So, abstracting out how all that works has never made sense to me.
Until… well.. this past week.
I’ve been working through a book titled Clean Code: A Handbook of Agile Software Craftsmanship recommended to me by a co-worker. Through that book and the encouragement of literally everyone who has ever looked at my code, I finally saw the use-case for objects in this project.
getDescription()
I’ve made lots of arguments as to why my arrays were better. They were less overhead to implement. They were easier to store and retrieve between GET/POST requests. They don’t require “magic” to work. This ended up being all lies, and the thing that finally made me realize this was when I wanted to simply print the description of something to the browser.
Everything in this game… items, rooms, monsters, chests… will be represented on the screen by a sentence that describes that thing. It’s made up of all the words and phrases that my generator code picks to describe that thing… and they are all different.
The description for an item consists of: Element, Prefix, Type, Adjective, Abstract, Bonus.
The description for a room consists of: Action, Move, Direction, Enter, Adjective, Place, Description.
In order to be able to properly display the sentences that make up these two things, I wrote different functions for each itemGetDescription()
and roomGeDescription()
. It works. It works good enough for the simple pages that are currently a part of noroguesallowed. The thing is, it becomes complex once I have a bunch of these things… I first need to know what array I’m dealing with so I can call the correct function itemGetDescription($weapon)
not roomGetDescription($weapon)
.
And this is where the power of OOP comes in. If each Class in my code implements a getDescription()
method, I don’t have to worry about what I’m dealing with and which version of the description routine to call. I can just loop through all the Objects in the current game state and call the getDescription()
method on them. Magic.
Serialize and Unserialize
One of the biggest reasons I was sticking to arrays was the ease of being able to store them with serialize()
and unserialize()
. These two built-in functions would allow me to put all my game state into session or a database to store it between requests, and quickly rebuild it. I wasn’t sure how I would handle this with Classes, and rather than research, I stuck to my beliefs that arrays were superior because of this.
About 30 seconds on google taught me that serialize()
and unseralize()
work exactly the same with Objects. Assuming the class is defined wen unseralize()
is called, the object gets rebuilt as an instance. Magic.
Magic?
I always thought Classes abstracted a bit too much. Things became too hidden for me to understand how they work. I spent way too much time tracking a request through a bunch of linked functions to try and figure out what was going on. That’s because I’ve only really worked with classes in the scope of a framework… and frameworks explicitly hide a whole bunch of stuff because that’s the whole point. They make things easier for the programmer by abstracting out all the inane and repetitive things the coder would have to normally write to get a class up and running.
Well, this is a learning project, and I’m not using a framework. My goal is to keep this as close to vanilla PHP as I possibly can. I might lean on jQuery a tiny bit for Bootsrap CSS, but I don’t want to have a list of package dependencies to lean on… and this has been my favorite part of the code conversion I’ve gone through over the past week.
Converting this code to class-based OOP has taught me more about programming in a week than I’ve learned in years of doing my job. It’s been exceedingly fun. I now have a pretty firm grasp on the basics of PHP objects, and I’ve begun to leverage the power of using things like abstract classes and polymorphism.
I discovered that I could use an absract class calld Holdable on weapons and shields that helps me determine if something can be “held” by the player. It also implements a function that can determine how many hands that object takes up. Without classes, I would have had to write that into the item code somewhere and possibly duplicated it to make sure it worked in both cases.
The four different types of items (weapons, armor, shields, trinkets) all have various different attributes that make them up and because of this, I am able to implement the common code in the parent Item constructor while having a class-specific constructor to handle the individuality.
If you are reading this and laughing because I’m stating the obvious, this is not lost on me. These are the key basics that are taught to every programmer today. I knew these things before choosing to NOT use classes, and I willingly ignored the wisdom of others.Why? Because that’s how I learn. I must confess, these might be the first fully-functioning classes I’ve ever written in PHP.
The vast majority of the work I do on a regular basis is retrieve some data with an SQL statement and display it in a table… classes aren’t really a needed thing there and when they are PDO connections, or libraries like Carbon, I’m just leaning on someone else’s code. Trying to do things “my way” first and seeing the flaws, allowed me to fully appreciate the work it took to convert this thing to a class-based system.
The entire app isn’t classes by the way. I still have quite a bit of procedural code in there for things like handling inputs or running little helper functions. I see absolutely no reason to contrive that code with objects… yet. 🙂