📜 ⬆️ ⬇️

CORE technology

Six months ago, I wrote a post about a programming technology invented by me ( habrahabr.ru/post/163881 ), which helped me to accelerate strongly (and not only me) and to do my job better.

Whereas last time was devoted to practice and comparison with the usual development model, this time I want to talk about the theoretical foundations of technology.

To simplify the explanation from the Context-Object-Request-Event system, I’ll throw out contexts, and we’ll talk about setting tasks and how they are related to objects, events, and requests.
')


Task setting and flowing abstractions



In programming, we often have to deal with flowing abstractions. When, for example, our code does not take into account that the connection to the database may fall off, this leads to problems.

There is a more fundamental problem with flowing abstractions, and it concerns the modeling of complex behavior. It often happens that when additional requirements are added to the task, the code that has just been written becomes unusable, and everything has to be redone. This is also a flowing abstraction: we modeled the solution of the problem not in terms of the problem, but in terms of the programming language — variables, lists, references, objects, design patterns. With a small change in the requirements, the whole code turned out to be unusable.

This is because programming languages ​​are not similar to the language of the description of problems that we use in the formulation.

I argue that most practical tasks come down to two patterns: “when A is done B” and “it is necessary for C. to be done. Let us do it in the way of D”.
We introduce the following notation: A is an event, B is a reaction to an event, C is an action request, D is a method of execution.

Examples for the first case:
- when the user clicks on this button, let's show him the following animation (hmm, just like at the meeting)
- when designer Anya draws a prototype of the interface, I want to look at it and express my ideas for improvements
- let's collect statistics on each click
- every fifth run show banner

For the second:
- Need to draw a design for this idea. Let it make Anya. Well, or, Vova.
- save the current account value to the database

Notice this: the essence of the task in the first case is that B was done when A. happened. It does not matter exactly what A is, what particular context it came from, and so on. And this task is completely separate from the task where event A takes place, it is dedicated to other goals. in the second case - the opposite. it does not matter in principle how task C. will be done. It is important that it be done somehow, in any suitable way.

Why is it important to understand? Consider the following code for an abstract game (or anything else) on js:
addMoney: function(amount) { this.balance+=amount; if(this.balance > 250) { $('.you_win!').animate({css:{opacity: 1}}) } } 

This code is very bad. why? because in it the logic of money is mixed with the logic of the show. But worse:

 $('.coin').click(function(){ this.balance+=15; if(this.balance > 250) { $('.you_win").animate({css:{opacity: 1}}) } }) 


In prototypes such an example is very frequent. Touch on any aspect of the task - design, money counting, animation - all around, it will quickly turn into a mess and will be buggy. How would it be done normally? Just describe what was in the problem:
- when the user clicked on a coin, add it to the balance
- when the balance has become more than 250, show the banner that we won

We divide the task into three objects, one of which will be responsible for the display and the UI, the second for the state of the account, the third for determining the winning or losing user:
 var UI = { handleCoinClick: function() { .... }, showWinAnimation: function() { .... }, ... } var Balance = { addCoins: function() { ... }, ... } var WinWatcher = { watchForWin: function() { .... } ... } 


UI here is only responsible for the display and clicks - user interaction.

Now these components need to be connected somehow. It will be bad if we call them from each other, therefore we will connect them with events and requests
 var UI = { handleCoinClick: function() { // ,   DOM Init,   ,      .... $('.coin').click(function(){ //       Event_CoinClick ..... }); }, showWinAnimation: function() { // ,        Request_ShowUserWin $('.you_win').animate({opacity: 0}); }, ... } var Balance = { addCoins: function() { // ,    «  » Event_CoinClick this.balance+=15; //   ,     Event_BalanceChanged }, ... } var WinHandler = { watchForWin: function(balance) { // ,   ,    Event_BalanceChanged if(balance > 250) { //   ,    Request_ShowUserWin } } ... } 


Now you need to link the pieces of code where the comments "call, when ..." and "throw here / request." But here we are confronted with the very proceeding abstractions. If you call the Balance and WinHandler methods directly from the UI, then we may need to collect statistics, or some other complication, and calls related to other tasks will be added to the UI method. The method will cease to be clean.

Therefore, we will try to make the method simple. Let's provide the resolution to the event dispatcher.

Core.js


Last time I promised to make an open-source implementation. Currently there is an implementation for javascript github.com/okneigres/corejs

The library works both in the browser and under Node.js

 <script src="core.js"></script> var Game = { }; //  Game.UI = { CoinClickEvent: new Core.EventPoint, handleCoinClick: function() { Core.CatchEvent(Event.DOM.Init); $('.coin').click(function(){ new Game.UI.CoinClickEvent(); }); }, showWinAnimation: function() { Core.CatchRequest(Game.WinHandler.ShowUserWinRequest); $('.you_win').animate({opacity: 0}); }, ... } Game.Balance = { ChangedEvent: new Core.EventPoint, addCoinsOnClick: function() { Core.CatchEvent(Game.UI.CoinClickEvent) this.balance+=15; new Game.Balance.ChangedEvent; } ... } Game.WinHandler = { ShowUserWinEvent: new Core.EventPoint, ShowUserWinRequest: new Core.RequestPoint, watchForWin: function(balance) { Core.CatchEvent(Game.Balance.ChangedEvent) if(balance > 250) { new Game.WinHandler.ShowWinRequest; } } ... } Core.processNamespace(Game); 


Now it’s easy to do anything with this code: add new functionality:

 //       Game.GatherStat = { sendClick: function() { Core.CatchEvent(Game.UI.CoinClickEvent); $.post('/stat/gather/ajax', {click: 1, date: new Date}); }, sendWin: function() { Core.CatchEvent(Game.WinHandler.ShowUserWinEvent); $.post('/stat/gather/ajax', {win: 1, date: new Date}); } } 


Refactor UI (divided into two objects - UI and UIWin):

 Game.UI = { CoinClickEvent: new Core.EventPoint, handleCoinClick: function() { Core.CatchEvent(Event.DOM.Init); $('.coin').click(function(){ new Game.UI.CoinClickEvent(); }); } }; Game.UIWin = { showWinAnimation: function() { Core.CatchRequest(Game.WinHandler.ShowUserWinRequest); $('.you_win').animate({opacity: 0}); }, ... }; 


Now that the code is written in strict accordance with the logic, working with the code is easy.

Instead of conclusion



Work in such a paradigm greatly simplifies the design and content of the project. We can re-model as many times as necessary, but the logic of the problem will remain the same. Why not create code from it? And if you practice a little, to work in such a paradigm is easier than ever, because we, in fact, just have to describe the task in the words in which we think about it, that's all.

In my experience, this greatly simplifies the design of interfaces, and even server applications. To contain code in the level of abstraction that the task requires is possible and necessary.

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


All Articles