I want to explain OOP by diving straight into writing code and let the explanation come fluently. Let us begin by creating a new project within our solution.
Step 0 - Right click your solution -> Add -> New Project...
Step 1 - Select Installed -> Visual C# -> Class Library
Step 2 - Name your project CardGameLibrary and click OK
Now we have two projects in our solution. We could have all classes within the same project and even in the same file if we really wanted to, but there are several very good reasons for the different layers of abstraction. Separation of Concern (SoC) is one of them. Which simply put is: different kind of code should be divided into their own kind of solution, project, namespace, class, method etc. But this does not mean you should create a new project all the time. You will learn when to do what with experience. It is all about being readable and maintainable. Moving on...
In the new CardGameLibrary project you should see a autogenerated class called Class1 (.cs is just the C# file extension). Rule #0, only one class per file. Period. Delete this file (select and hit delete on your keyboard). Add a new folder to the project called 'Model' (Right click project -> Add -> New folder). Add a new class called Card to that folder (Right click folder -> Add -> Class...).
You should see something like:
So, now lets start talking about Object Oriented Programming (OOP). There are many things to this, but as mentioned in my previous post:
"To explain the class declaration... In the real world, you'll often find many individual objects all of the same kind. A bicycle company creates thousands of bikes each identical to each other, but still unique. Each bicycle was built from the same blueprint and therefore contains the same components. In object-oriented terms, we say that a bicycle is an instance object (or instance for short) of the class Bicycle. A class is the blueprint from which individual objects are created."
While the blueprint of each bicycle might be the same, that does not mean that the data of each object stays the same. You could for instance create two instances of the Bicycle class, one blue and one yellow, but they fundamentally work the same way.
You can model a program you can do it in an infinite number of ways, but we want something that is easy to understand and when creating a program that is going to simulate the real world, like a card game, why not start with a class called Card? Each and every class should have a single responsibility, it might be difficult to define 'single', but that's something you will become better at with experience. But "handle everything" is not a good single responsibility. So the purpose of this class is simply to represent a card used in a deck of card. A class can have different properties and methods which either take actions on them self or other objects. Lets just dive right into how you would approach this in C# .NET.
Lets start with two properties, color of the card and number.
At the beginning of each property you see "public", which is the visibility level, meaning it can be seen from outside the class. Another class can read and write (get and set) to that property. However the setter (set;) is private which means you can only read this property from the outside, but within the class you can set the property. Which brings us to the constructor, which is a method that is used to create an instance of a class. You as a developer has to make sure that the constructor creates an object that makes sense, it has to be an object that it has the required input arguments (input variables). Lets start with an example that is very basic and improve upon it.
In this first constructor we have two input arguments 'string color' and 'int number', which we assign to their respective property. While this works if the constructor is used in the right way, it also opens up the possibility for errors. But it is bad practice to leave anything for chance. When a program is small and you are the only one working on a project you will probably be able handle it, but with just a few more lines of code you will ensure that a class always works as intended. When somebody else consume your class or reads your code it should always be clear what you were intending to accomplish. The first problem with this code is the input arguments, they are not sufficiently well defined. They can be set of values far greater than those who actually make sense. In a regular card game there are only four colors - 'hearts', 'spades', 'diamonds' and 'clubs', but the argument 'color' is of type string which can be just about any kind of text. So if somebody uses the constructor in the wrong way, like using strings that does not make any sense in a game of cards; you could end up with weird bugs where players are dealt cards of the color 'orange' or the "color" 'fried chicken', since any string (text) is possible. You can even use a special value called null, which means nothing. It is a special object without any content and without methods. You should try and avoid using it, because it causes problems. If you create an object without instantiating it, then it has the value null. If you try to call a method on a null object you get something called a 'NullPointerException', which is a special Exception class that can cause your program to stop working (unless handled; more about that in another). The other argument 'number' is of type int, which is a primitive datatype that cannot be null and is an 32-bit integer within the range -2,147,483,648 to 2,147,483,647, but the only integers that really makes any sense is 1 to 14. Not only can an int be way to many wrong integers, but it does not really make sense to use integers for the number. Especially not for aces that can both mean 1 and 14.
So how do we solve this? There are many ways, but I am going to solve this using something called 'enum'. I cannot really explain it much better than Microsoft, so here is a link where you can read about it. It is common practise to put enums within the same class or at least in the same file, but we will instead put each enum in a separate file, because of separation of concern.
Lets start by creating a enum for the colors called "CardColorEnum"
Step 0 - Right click the Model folder -> Add -> New Item...
Step 1 - Visual C# Items -> Code -> Code File -> Name it CardColor.cs -> Click Add
Step 2 - Write the following code in that file
Now we have the enum we need with all the colors of a regular card game. Lets use it as an input parameter for our card class.
Simply change the color argument in the card constructor from string to CardColor and the same for the Color property.
We are about to do the same with number. But first, you will learn that while coding you will do mistakes and things that does not make sense and one of the most important things when writing code is that it is easy to read and understand. Because usually you will be sitting reading code, either your own or others. If I am repeating this a lot it is because it is really that important. Naming something correctly tells you or the next one reading it really what its purpose is. Therefore we will change the name from number to value, because Knight, Queen, King and Ace are not really numbers, they are just representations of some kind of value, which differ in different games (well same for 2, 3... 10 really).
So here is our new CardValue enum.
Lets use our new enum and change our Card class.
We have now learned how to create a simple basic class that makes sense and is difficult to misuse.
What's next?
In the next post I will be continue where I left off and start building the foundation for a card game.
/*
Live and let live.
*/