How Snake can introduce OOP: a complex concept in simple words
Hello! You are welcomed by the editors of the site GeekBrains.ru , an online learning service for programming. We decided to start a blog on Habré! We are sure that we will still have time to tell and discuss many interesting things from the world of programming, IT and online education. But let's start very simply, without any special preludes, with a review of the free course on the basics of C # and OOP from one of our students. The slogan of the course is “Complicated concept in simple words.” Let's see how this is true.
A few words about the listener: IT project manager, familiar with procedural programming, web development, SQL. A closer acquaintance with the PLO was needed for in-depth implementation in business processes. So, the word to our graduate.
“There is such a programmer joke: a programming language is easy to learn - unlike foreign languages, there are very few words in it. Indeed, learning the names of commands, functions, libraries, data types, and even syntax is not so difficult, especially since many of them are similar in different languages. However, it is not for nothing that the statement is called a joke - in order to work with a specific programming language, you need to know the principles, the fundamentals of the paradigm, the standards of the language. Object-oriented programming, like any programming paradigm, has a set of principles and rules that are valid for all languages and will be useful in any case. ')
For myself, I chose an orientation course for mastering the principles of object-oriented programming, which is built on the creation of a working draft in C #, a console game called Snake. This is the same snake, for which several generations have been killing time at lectures, playing Tetris and black and white Nokia phones. But I must say that it is much more pleasant to write your toy, and, more importantly, more useful. During the creation of the game, the teacher reveals all the principles of the PLO, and so that each principle is perceived not as an imposed boring theory, but as a solution to the question already ripe in the head: "How to simplify the code and make it more readable?"
At the beginning of the program, I plunged right into two modern tools that developers use.
Visual Studio is an integrated development environment for a number of programming languages. It is in Visual Studio that you can get acquainted with the source code editor, class designer, debugger, and console.
GitHub is a web service for hosting IT projects and their joint development, based on the Git version control system. Acquaintance with it helps to understand how the project works, to refer to the open code, to copy it, if necessary, to view previous versions of the code. For communication between the development environment and the code repository, the Smartgit application is used.
The chosen language is C #, but, as I understand from my practice, the principles of OOP are the same and can be easily applied when learning another language.
Initially, the teacher emphasizes that the product will be developed. The choice of the snake was not accidental - everyone knows the logic of the game, its features and requirements. And in development it is important to have a holistic view of the future project at an early stage. Such a vision helps break it down into significant steps and avoid many omissions.
The first two lessons are simple and understandable to anyone, even completely unfamiliar with programming. Traditionally, work begins with a happy 'Hello, world!'
I once again repeated for myself what a function is, how it works, how variables are created. For writing code, a procedural approach is used - the functions are sequentially applied, taking the specified parameters as input. Two shortcomings in the creation of all the code inside the main main function immediately become apparent: the spread of the code and the declaration of variables right inside this function.
namespaceSnake { classProgram { staticvoidMain(string[] args ) { int x1 = 1; int y1 = 3; char sym1 = '*'; Draw( x1, y1, sym1 ); int x2 = 4; int y2 = 5; char sym2 = '#'; Draw( x2, y2, sym2 ); Console.ReadLine(); } staticvoidDraw(int x, int y, char sym) { Console.SetCursorPosition( x, y ); Console.Write( sym ); } } }
Indeed, after the first two sessions, there is a feeling of delight in understanding rather complex things. I checked on myself, so I subscribe to the comments of the listeners.
In the third lecture, I was introduced to the concept of class, data type. The class is one of the main concepts of the PLO, so close attention is paid to its study. Variables begin to be created as class instances, that is, objects (hence the name of the OOP).
If the listener is a beginner, then he learns to understand the language of the code and the expression Point p1 = new Point (); begins to be perceived as “an object is created, the point p1 as an instance of the class Point, which accepts coordinates as input”.
At the same lesson, the listener learns to think like a computer. This happens by using a breakpoint and traversing the code through the debugger: step by step, you can see the creation of class objects, the initialization of variables, the function (call the Draw method).
In the fourth lesson, the constructor of the class Point is created - an explicitly written constructor with a special syntax that returns nothing.
publicPoint(int _x, int _y, char _sym) { x = _x; y = _y; sym = _sym; }
I noticed how the amount of code of the main program is reduced after creating the constructor. The constructor takes the coordinates of the point and the symbol of its designation as input, but the user doesn’t see the implementation details - they are hidden inside the constructor. So I ran into the first of the three principles of OOP - encapsulation. Encapsulation is a property of the system that allows you to combine data and methods that work with them in a class and hide all implementation details from the user.
The fifth lecture immerses in the organization of memory, the work of the program with the stack and the heap. Explanations are supplemented by visual diagrams. After that, work begins with the new class of the standard C # List library (list), in which the functions of adding and deleting an element are created, and a foreach loop arises.
List<int> numList = new List<int>(); numList.Add( 0 ); numList.Add( 1 ); numList.Add( 2 ); int x = numList[ 0 ]; int y = numList[ 1 ]; int z = numList[ 2 ]; foreach(int i in numList) { Console.WriteLine( i ); } numList.RemoveAt( 0 ); List<Point> pList = new List<Point>(); pList.Add( p1 ); pList.Add( p2 ); Console.ReadLine(); } }
Working with the loop in the debugger, the listener even more clearly understands the structure and sequence of the program. For the purpose of implementing the game, we created horizontal and vertical lines of obstacles for the snake, which are nothing more than a list of points. I tried to keep up with following the teacher, sorting out his code, creating my own and training already in my program.
namespaceSnake { classHorizontalLine { List<Point> pList; publicHorizontalLine(int xLeft, int xRight, int y, char sym) { pList = new List<Point>(); for(int x = xLeft; x <= xRight; x++) { Point p = new Point( x, y, sym ); pList.Add( p ); } } publicvoidDrow() { foreach(Point p in pList) { p.Draw(); } } } }
The teacher notes that both the point and the lines, and later the moving snake itself are in fact figures, therefore there must be some solution for optimizing the code, which will allow not to copy the code, but to reuse it. So I met the second principle of OOP - inheritance. Inheritance is a property of the system that allows you to describe a new class based on an existing one with partially or completely replaced functionality. Thus, each line, snake and point become a special case (inherited) from the class Figure: class HorizontalLine: Figure.
namespaceSnake { classFigure { protected List<Point> pList; publicvoidDrow() { foreach ( Point p in pList ) { p.Draw(); } } } }
The hereditary class necessarily contains attributes of the parent class, but it can also have its own. The example of inheritance is additionally understood on a textbook and clear example of the class Worker, inherited from the class Man, having height and age from the parent class and its own characteristic - salary. By the way, for the purposes of self-training understanding of inheritance in the PLO, it is best to work with the design of a student or employee card — I understood this immediately, first by self-consolidating my knowledge, and then working with the project.
And the snake must learn to move in the field and be controlled by arrows from the keyboard. The task seems difficult, but I remembered that it was still a console, and therefore the implementation of snake movement should be as simple as possible. I already knew that the snake should move in four directions, something is, grow or shrink. And here comes the time of abstraction - a situation in which the code is written on the basis of the selected significant characteristics of the object, and minor - are excluded. We choose significant signs: a snake is a figure from points on the map, it has a starting position, coordinates, and it moves in one of four directions. The Snake class is seriously changing and growing.
{ classSnake : Figure { publicSnake( Point tail, int length, Direction direction ) Direction direction; publicSnake( Point tail, int length, Direction _direction ) { direction = _direction; pList = new List<Point>(); for(int i = 0; i < length; i++) for ( int i = 0; i < length; i++ ) { Point p = new Point( tail ); p.Move( i, direction ); pList.Add( p ); } } internalvoidMove() { Point tail = pList.First(); pList.Remove( tail ); Point head = GetNextPoint(); pList.Add( head ); tail.Clear(); head.Draw(); } public Point GetNextPoint() { Point head = pList.Last(); Point nextPoint = new Point( head ); nextPoint.Move( 1, direction ); return nextPoint; } } }
In general, if we continue to talk about abstraction, the concept of an abstract class is widely used in OOP. A template class is created that implements only the functionality known and needed by the developer at the moment. Classes derived from the abstract, all the functionality will be able to further complement. But back to the project. The Direction class appears, in which another enum data type is used — an enumeration consisting of a set of named constants. In our case, these are constant directions: right, left, up, down. The Point class has a Move method.
publicvoidMove(int offset, Direction direction) { if(direction == Direction.RIGHT) { x = x + offset; } elseif(direction == Direction.LEFT) { x = x - offset; } elseif(direction == Direction.UP) { y = y + offset; } elseif(direction == Direction.DOWN) { y = y - offset; } }
Thus, the movement of a snake is implemented as a position shift with the tail wiping with a space. The snake is controlled by keys and the control is implemented as follows.
publicvoidHandleKey(ConsoleKey key) { if ( key == ConsoleKey.LeftArrow ) direction = Direction.LEFT; elseif ( key == ConsoleKey.RightArrow ) direction = Direction.RIGHT; elseif ( key == ConsoleKey.DownArrow ) direction = Direction.DOWN; elseif ( key == ConsoleKey.UpArrow ) direction = Direction.UP; }
Again, I ran into encapsulation - the snake control goes to the Snake class. At the next stage, the snake begins to eat and the prey is created in an infinite loop using the FoodCreator function, the coincidence of the coordinates of the snake head and the point representing the food are checked.
Creating obstacles for a snake eating in an endless cycle and working on the Wall class, I learned about the third paradigm of OOP, polymorphism, the ability of a function to process data of different types. In OOP polymorphism is that the object uses the methods of the derived class, which is not at the time of the creation of the base. At runtime, derived class objects can be treated as base class objects in places such as method parameters, collections, or arrays. When this happens, the declared type ceases to match the type itself at run time. Immediately make a reservation that polymorphism is not immediately understood, I needed to listen to the lecture again and turn to the wonderful Schildt textbook, which had long been at hand and was waiting in the wings.
At the last lesson, the snake became completely independent, and I learned to handle collisions with obstacles and my own snake tail. The code in the lecture is no longer being created, but taken from the repository and disassembled. I did not go on about the temptation to copy someone else's code, and some time after listening to the course I created my own, referring to the lectures over and over again. I advise you to do the same - because knowledge and knowledge are necessary for work and understanding. I hope I gave enough teasers to make you want to go to GitHub and understand the implementation of a simple game, the main code of which is only 52 lines, which means that all the principles of OOP have been successfully applied.
Summing up, the teacher once again returns to the main paradigms of the PLO and draws attention to the public and private access modifiers and talks about the virtual keyword, thanks to which the method can be redefined in the inherited class. Private is the private data and code inside the object, public is open. Private data and code are available only from another part of the same object, that is, it cannot be accessed from outside. Open data and code are accessible from any part of the program and often serve as an interface to the closed parts of an object. If we talk about the course as a whole, it helped me - the quality of my work and the level of communication with the developers changed. I advise you to try anyone who is even slightly interested in programming, at a minimum, it develops the brain and teaches you to think systemically. I will definitely come back to listen to other courses and chat with professionals. Well, I wish good luck to the brave newcomers! ”
Have you noticed how popular the video format has become in content, advertising, management? It is well known that the video immediately involves both sight and hearing, which means it is perceived better. In addition, the video course can be stopped, rewound, listen a few more times, ask questions in the comments. Plus, GeekBrains teaches practitioners for whom programming is a daily job and therefore they are always up to date with the latest trends in their industry. Of course, viewing the course with a glass of tea in front of the monitor will bring little benefit, so in conclusion we want to give some advice to the audience.
Listen to the course with a pencil or pen - write down points that you should listen to or additionally watch on the Internet or a book.
Do not miss the incomprehensible moments, try to understand, refer to additional sources.
Do not copy the code, create your own - only this will allow you to truly learn how to work with the code.
Work with the debugger - knowing how the program works step by step helps to create slim and understandable functions.
Go back to the theory and course even when you were able to create your application - it helps to streamline knowledge and find a new point for development.
We are sure that the time for self-education is never wasted. It will definitely pay off.