📜 ⬆️ ⬇️

From the ugly duckling to the swan, or how to fix the curved code

image

What is it about?


Many are starting to write a project to work with one small task, without implying that this story will lead to a multi-user control system, well, let's say, content or save, production. And everything seems to be cool and cool, everything works until you begin to understand that the code that is written consists entirely of crutches and hardcodes. An urgent problem arises: when adding a new feature, you have to mess around with this code for a very long time, remembering “what was written there?” And curse yourself in the past. In particular, this article is intended for novice developers who need refactoring at the first stage is not obvious, but today they are in an unpleasant situation.

For example, at the entrance we have a hard code with layout, queries, crutches, which sometimes can not be read.
')

How it helps


What will the system gain as a result of refactoring?
- Structured and modular code.
- Separation of UI and logic.
- Ability to write UNIT tests.
- The ability to write APIs only for applications authorized in the API.
- A simpler approach to transfer to another database.

A particular example of the case "how to do it and where to start?". I propose to deal with such corrections consistently, and at the same time, without completing the second stage, do not begin to take on the third. We will fix the code in the working system; it is better to write functional tests in advance in order to understand that everything works as it should.

Stages


1. Algorithmic approach .
Let's look at our horrible source code with a layout that cannot be tested (and probably repeats in other places). Simple and straightforward, but the sequence of such a code generates a bunch of known troubles.

index.php
<h1></h1> <? $DB = new DBConnector; $DB->query('SELECT * FROM users LIMIT 10'); if($DB->get_num_rows()){ while($user = $DB->fetch_row()){ echo '<p>'.$user['name'].'</p>'; } } ?> 


2. Procedural approach .
Why not write a function and call it in other places of this system? The transition to procedural programming will help to reduce the code, although with the example of 1 call - it looks worse.

functions.php
 $DB = new DBConnector function GetUsers(){ global $DB; $DB->query('SELECT * FROM users LIMIT 10'); if($DB->get_num_rows()){ while($user[] = $DB->fetch_row()); return $users; } else { return array(); } } ?> 


index.php
 <h1></h1> <? include 'functions.php'; $users = GetUsers(); foreach($users as $u) { echo '<p>'.$u.'</p>'; } ?> 


Comment: Already better. If you translate all requests to such a view, then you’ll get a list of some functions.php working with some module. Thus, the model is already separated from the UI and each function can be tested, as well as a sequence of calls to such functions.

3. Object-oriented approach .
Why do I need to connect all the functions in the system, if it is enough to work only with the necessary set of functionality? For example, on the simple user list page - there is no need to connect the entire list of functions, such as, for example, a task, or a project. Why not create a class for working with a module, from which the object of working only with this module will be created? You can build the architecture in such a way that the inheritance of functions of the same parameters of the Get (add / edit) methods will simply be inherited from some kind of superclass that generates classes of working with modules. Also, there is a possibility that it will not always be necessary to refer to the database for any functionality, so it means that we will be able to connect to it in general.

Let's write a basic abstract superclass that will connect to the database and have a method for getting a list of some records. From it we inherit the class of work with users, create an object and get records.

BaseClass.php
 abstract class BaseClass{ protected $dataBase; protected $moduleName; function __construct(){ $this->dataBase = new DBConnector; } //        function Get($limit=10){ $DB->query('SELECT * FROM '.$this->moduleName.' LIMIT '.$limit); if($DB->get_num_rows()){ while($user[] = $DB->fetch_row()); return $users; } else { return array(); } } } 


UserClass.php
 include 'BaseClass.php' class UserClass extend BaseClass{ function __construct (){ parent::construct('users'); } } 


index.php
 <h1></h1> <? include 'UserClass.php'; $users = new UserClass; $users = $users->get(); //  foreach($users as $u) { echo '<p>'.$u.'</p>'; } ?> 


Comment: Even better, though a lot of code, right? The fact is that these two classes are the first step to writing an API. Why is that? The fact is that requests through the RESTFull API are made to the address and very often there are module names. Depending on the address, you can connect any class (or connect a ClassLoader, since they are a pond now) and call the set of functions that this user is allowed (you can insert a rights or role handler, but this is another story).

Salt


The essence of the article is that refactoring is almost always possible and often necessary even in the most hopeless situation. Everyone loves beautiful code, but often there is no time to write it and this is undoubtedly a bad practice.

In this case, there are 3 stages, as a result of which, the system will receive a distinction between UI and application logic. This will allow you to prepare your system, which is overloaded with curved code, for the possibility of writing Unit tests, writing a RESTfull API, and to a sense of self-satisfaction for the work done. For many, this, of course, may be obvious, but when asked to write a third-party application, or to provide an API, sometimes you have to blush.

PS: I will be glad to any comments and, possibly, links to other methods and solutions for a problem of a similar nature.

Source: https://habr.com/ru/post/147416/


All Articles