How Snake Can Introduce OOP: A Complex Concept in Simple Words

    Hello! You are welcomed by the editors of the GeekBrains.ru website , an online programming training service. We decided to start a blog on Habré! We are sure that we will still have time to tell and discuss a lot of interesting things from the world of programming, IT and online education. But let's start very simply, without much prelude, with a review of a free course on the basics of C # and OOP from one of our students. The slogan of the course reads "A complex 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 OOP was needed for deep implementation in business processes. So, a word to our graduate.





    “There is such a programming joke: a programming language is easy to learn - unlike foreign languages, it has very few words. 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 without reason 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, and language standards. Object-oriented programming, like any programming paradigm, has a set of principles and rules that are valid for all languages ​​and are useful in any case.

    For myself, I chose an orientation courseto learn the principles of object-oriented programming, which is built on creating a working draft in C # - the console game "Snake". This is the same snake, after which several generations killed time at lectures, playing on tetris and on black and white Nokia phones. But I must say that writing your toy is much more pleasant, and, most importantly, more useful. During the creation of the game, the teacher reveals all the principles of OOP, and in such a way that each principle is perceived not as an imposed boring theory, but as a solution to a question that has already arisen in the head: “How to simplify the code and make it more readable?” But first things first.

    At the beginning of the program, I immediately plunged 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 is arranged, turn to open source, copy it, if necessary, and view previous versions of the code. The Smartgit application is used to communicate the development environment and the code repository.

    The selected language is C #, but, as I already understood from my practice, the principles of OOP are the same and can be easily applied when learning another language.

    Initially, the teacher focuses on the fact that the product will be developed. The choice fell on the snake is no coincidence - 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 already at an early stage. Such a vision helps break it down into meaningful stages and avoid many omissions.

    The first two lessons are simple and understandable to anyone who is even completely unfamiliar with programming. Traditionally, work begins with the happy 'Hello, world!'

    namespace Snake
    {
    	class Program
    	{
    		static void Main( string[] args )
    		{
    			Console.WriteLine("Hello world");
    			Console.ReadLine();
    		}
    	}
    }
    



    Once again I repeated for myself what a function is, how it works, how variables are created. To write code, a procedural approach is used - the functions are sequentially applied, taking the specified parameters at the input. Two drawbacks of creating all the code inside the main function of main become immediately apparent: the growth of the code and the declaration of variables right inside this function.

    namespace Snake
    {
    	class Program
    	{
    		static void Main( 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();
    		}
    		static void Draw(int x, int y, char sym)
    		{
    			Console.SetCursorPosition( x, y );
    			Console.Write( sym );
    		}
    	}
    }
    

    Indeed, after the first two classes there is a feeling of delight from understanding rather complicated things. I checked it myself, so I subscribe to the comments of the audience.



    In the third lecture, I got acquainted with the concept of a class, data type. A class is one of the basic concepts of OOP, so close attention is paid to its study. Variables begin to be created as instances of the class, that is, objects (hence the name of OOP).

    If the listener is a beginner, then he learns to understand the code language and the expression Point p1 = new Point (); it begins to be perceived as "the object p1 is created as an instance of the Point class, which takes coordinates at the input."

    namespace Snake
    {
    	class Point
    	{
    		public int x;
    		public int y;
    		public char sym;
    		public void Draw()
    		{
    			Console.SetCursorPosition( x, y );
    			Console.Write( sym );			
    		}
    	}
    }
    

    In the same lesson, the student learns to think like a computer. This happens by using a breakpoint and passing through the code through the debugger: step by step you can see the creation of class objects, initialization of variables, the function's operation (calling Draw method).



    In the fourth lesson, the constructor of the Point class is created - an explicitly written constructor with special syntax that returns nothing.

    public Point(int _x, int _y, char _sym)
    		{
    			x = _x;
    			y = _y;
    			sym = _sym;
    		}
    

    I noticed how the amount of code in the main program is reduced after creating the constructor. The constructor takes the coordinates of the point and the symbol of its designation as an input, but the user does not see the implementation details - they are hidden inside the constructor. So I came across the first of the three OOP principles - 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 plunges into the issue of memory organization, how the program works with the stack and heap. Explanations are supplemented by visual diagrams. After that, work begins with a new class of the standard C # List library (list), in which functions for adding and removing an element are created, and a foreach loop is created.

    List numList = new List();
    			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 pList = new List();
    			pList.Add( p1 );
    			pList.Add( p2 );
     			Console.ReadLine();
     		}
     	}
    

    Working with the loop in the debugger, the listener more clearly understands the structure and sequence of the program.
    For the purpose of implementing the game, we created horizontal and vertical obstacle lines for the snake, which are nothing more than a list of points. I tried to keep up with the teacher, analyzing his code, creating my own and training in my program.

    namespace Snake
    {
    	class HorizontalLine
    	{
    		List pList;
    		public HorizontalLine(int xLeft, int xRight, int y, char sym)
    		{
    			pList = new List();
    			for(int x = xLeft; x <= xRight; x++)
    			{
    				Point p = new Point( x, y, sym );
    				pList.Add( p );
    			}
    		}
    		public void Drow()
    		{
    			foreach(Point p in pList)
    			{
    				p.Draw();
    			}
    		}
    	}
    }
    

    The teacher notes that both the point and the lines, and later the moving snake itself, are essentially figures, so there must be some solution for optimizing the code, which will allow you not to copy the code, but to reuse it. So I got acquainted with the second principle of OOP - inheritance. Inheritance is a property of the system that allows you to describe a new class on the basis of an existing one with partially or completely replaced functionality. Thus, each line, snake and point becomes a special case (inherited) from the class Figure: class HorizontalLine: Figure.

    namespace Snake
    {
    	class Figure
    	{
    		protected List pList;
    		public void Drow()
    		{
    			foreach ( Point p in pList )
    			{
    				p.Draw();
    			}
    		}
    	}
    }
    

    An inherited class necessarily contains the characteristics of the parent class, but may have its own. The example of inheritance is further analyzed by the textbook and comprehensible example of the class Worker inherited from the class A person who has growth and age from the parent class and his own characteristic - salary. By the way, for the purposes of self-training of understanding of inheritance in OOP, it is best to work with designing a student or employee card - I understood this immediately, first fixing my knowledge on my own, and then working with the project.

    And then the snake must learn to move in the field and be controlled by arrows from the keyboard. The task seems to be difficult, but I remembered that we are still talking about the console and therefore the implementation of the movement of the snake should be as simple as possible. I already knew that the snake should move in four directions, that is, to grow or decrease. And here comes the time of abstraction - a situation in which the code is written based on the selected significant characteristics of the object, and insignificant ones are excluded. We select 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.

    {
     	class Snake : Figure
     	{
    		public Snake( Point tail, int length, Direction direction )
    		Direction direction;
    		public Snake( Point tail, int length, Direction _direction )
     		{
    			direction = _direction;
     			pList = new List();
    			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 );
     			}
     		}
    		internal void Move()
    		{
    			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 that is known and needed by the developer at the moment. Classes derived from the abstract will be able to supplement all functionality in the future.
    But back to the project. The Direction class appears, which uses another enum data type - an enumeration consisting of a set of named constants. In our case, these are direction constants: right, left, up, down. The Point class has a Move method.

    public void Move(int offset, Direction direction)
     		{
     			if(direction == Direction.RIGHT)
     			{
     				x = x + offset;
     			}
     			else if(direction == Direction.LEFT)
     			{
     				x = x - offset;
     			}
     			else if(direction == Direction.UP)
     			{
     				y = y + offset;
     			}
     			else if(direction == Direction.DOWN)
     			{
     				y = y - offset;
     			}
     		}
    

    Thus, the movement of the snake is implemented as a shift in position with grinding the tail with a space. The snake is controlled by keys and the control is implemented as follows.

    public void HandleKey(ConsoleKey key)
    		{
    			if ( key == ConsoleKey.LeftArrow )
    				direction = Direction.LEFT;
    			else if ( key == ConsoleKey.RightArrow )
    				direction = Direction.RIGHT;
    			else if ( key == ConsoleKey.DownArrow )
    				direction = Direction.DOWN;
    			else if ( key == ConsoleKey.UpArrow )
    				direction = Direction.UP;
    		}
    

    Once again, I came across encapsulation - snake management goes into the Snake class.
    At the next stage, the snake begins to eat and prey is created in an endless cycle using the FoodCreator function, the coincidence of the coordinates of the snake's head and the point representing the food is checked.

    while (true)
    			{
    				if(snake.Eat( food ) )
    				{
    					food = foodCreator.CreateFood();
    					food.Draw();
    				}
    				else
    				{
    					snake.Move();
    				}					
    				Thread.Sleep( 100 );
    				if (Console.KeyAvailable)
    				{
    					ConsoleKeyInfo key = Console.ReadKey();
    					snake.HandleKey( key.Key );
    				}
    			}
    

    Creating obstacles for the snake eating in an infinite loop and working on the Wall class, I learned about the third OOP paradigm - polymorphism, the ability of a function to process data of different types. In OOP, polymorphism is that an object uses methods of a derived class that were not there at the time the base class was created. At run time, objects of a derived class can be considered as objects of a base class in places such as method parameters, collections, or arrays. When this happens, the declared type ceases to match the type itself at runtime. I must make a reservation right away that polymorphism is not immediately understood; I needed to listen to the lecture again and turn to Schildt’s wonderful textbook, which had long been at hand and was waiting in the wings.

    In 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 generated, but taken from the repository and disassembled. I did not follow the temptation to copy someone else's code, and for a while after listening to the course I created my own, turning to lectures over and over again. I advise you to do the same - because knowledge and knowledge are needed for work and understanding. I hope I have given enough teasers to make you want to go to GitHub and figure out how to implement 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 OOP paradigms 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 private data and code inside an object, public is open. Private data and code are available only from another part of the same object, that is, they 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 the object.
    If we talk about the course as a whole, then it helped me - the quality of my work and the level of communication with the developers have changed. I advise everyone who is at least a little interested in programming to try it, at least it develops the brain and teaches you to think systemically. I will definitely return 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 video immediately engages both vision and hearing, which means it is perceived better. In addition, the video course can be stopped, rewound, listened several more times, ask questions in the comments. Plus, at GeekBrainsThey teach 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 moments that are worth listening to or additionally watch on the Internet or a book.

    • Do not miss incomprehensible moments, try to figure it out, 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 a debugger - knowing how the program works step by step helps to create harmonious and understandable functions.

    • Return to theory and course even when you have already been able to create your application - this helps to organize knowledge and find a new point for development.

    We are sure that time for self-education is never wasted. It will definitely pay off.

    Also popular now: