"If you want to go fast, go alone. If you want to go far, go together." (with)
With this lyric line in this article, I will talk about how to properly organize the code in your application so that it can grow in height and in breadth. If you want your brain activity to be more powerful than your competitors, then you will inevitably have to invite new programmers to your team. And if you do not put the vector of scalability, then the enthusiasm of enthusiasm in a year will turn into a noodle-code and teamwork will turn every employee out of malice into a little Satan.
So ... In order for your fighters to feel comfortable together in one project, it is necessary that they do not interfere with each other and write their letters in different non-intersecting sections of code. To do this, they will need to write "Independent" components.
"Independent" - these are components that themselves control their behavior, focusing on events from the external environment. With knowledge of how your application works and what events occur in it, you can easily write such "stand-alone" components without affecting old and foreign parts of the code.
"Dependent" - components that do not know anything about the external environment, but they have a very detailed api. This component needs to explain how to behave in your application. Such components, in contrast to "independent" ones, are written for the sake of multiple use in your or public projects, such as open libraries on github, etc.
How to determine which components are needed in your application? Very simple. If the component is applicable to only one task and is not reusable, then it should be written so that it is “independent”.
Here, for example, consider the component that personifies the message input fields in the chat feed. Most likely, such an input field in your application you will use only for its intended purpose and will not use it, for example, in the form of entering a nickname or password during authorization, because there the components will have their own specifics.
Let's not pull the cat for not having to delay and analyze a specific example. Let it be imitation chatika.
Imagine that you have two programmers in a team. Petka and Tolik. And they have a scalable application core. Simple as two fingers in a two-fingered man. The kernel has network transport, the storage of the message feed as an array (in this example, we will not allocate it into a separate file with methods) and the event emitter, which in this case is the key to scalability.
As an event emitter in this example, I took Backbone.Events, although we’ll restrict ourselves to using Backbone to set an example as simple as possible.
<html> <head> <script src="http://underscorejs.org/underscore-min.js"> <script src="http://backbonejs.org/backbone-min.js"> <script src="connection.js"/> <script src="app.js"/> </head> <body> <div id="header" style="padding:10px; border:1px solid #ddd"></div> <div id="container" style="margin-top:20px;"></div> <script> var app = new App(); app.init(); app.on('new_message', function(text){ console.info('new message', text); console.info('messages length', app.messages.length); }); </script> </body> </html> //app.js var App = function(){ var app = _.extend({ init: function(){ this.connection.connect(); } }, Backbone.Events); app.connection = new Connection(), app.messages = []; app.connection.on('connected', function(){ console.info('App connected!'); }); app.connection.on('incoming_message', function(text){ app.messages.push(text); app.trigger('new_message', text) }); return app; } //connection.js var Connection = function(){ return _.extend({ connect: function(){ /* , */ var i=0; setInterval(_.bind(function(){ i++; var text = 'message_' + i; this.trigger('incoming_message', text); },this),1000); this.trigger('connected'); }, }, Backbone.Events); }
Well, we have an application in which so far no views and whose work can be tested through the browser console. By the way, if you remove all auxiliary components and views from your application and it can work through the console, then this is very good. So you have achieved to some extent a weak connection between the components and the code can be covered by automated tests. Drive on.
Now, a person well-versed in strategic plans sets the task to Petka and Tolik, they say, it is necessary for the application to show a tape of messages, and in the header there was a counter of all messages in from the tape. You might have a question ... who needs this, damn, message counter in the cap in real life? This is just for example.
Ok, think Petka and Tolik, ok. They decide to simultaneously write two different components for the application.
But he did not hear how to program a scalable application and started writing code:
//list-view.js - "" var ListView = function(){ var el = document.createElement('div'); return { el: el, addMessage: function(text){ var row = document.createElement('div'); row.innerHTML = 'message: ' + text; el.appendChild(row); } } } //app.js var App = function(){ var app = _.extend({ init: function(){ connection.connect(); } }, Backbone.Events); app.connection = new Connection(), app.messages = []; // app.listView = ListView(); document.getElementById('container').appendChild(app.listView.el); // app.connection.on('connected', function(){ console.info('App connected!'); }); app.connection.on('incoming_message', function(text){ app.messages.push(text); app.trigger('new_message', text); app.listView.addMessage(text); // }); return app; }
Petya created a component that has to be managed through methods at a higher level and, as a result, in addition to simply declaring the component, I had to dig into the app.js code and add lines to the incoming_message handler. Now you cannot just comment out the "app.listView = .." and "... appendChild (app.listView.el)" lines so that your application does not break. For app.listView.addMessage (text); will throw an exception. The application began to grow into connectedness. The kernel started to depend on the view.
He knows how to write code so as not to interfere with others:
//header-view.js var HeaderView = function(app) { var el = document.createElement('div'), span = document.createElement('span'), view = { el: el, setCounter: function(num){ span.innerHTML = num; } } el.innerHTML = '- : '; el.appendChild(span); view.setCounter(0); app.on('new_message', function(){ view.setCounter(app.messages.length); }); return view; } //app.js ... app.connection = new Connection(), app.messages = []; // app.headerView = HeaderView(app); document.getElementById('head').appendChild(app.headerView.el) // ...
What Tolik did outside of its component - it only declared the component in the app variables area, rendered it all. The component also remains available for manual testing via the console or for unit testing, as it still returns api.
The area of ​​responsibility for Tolik’s code is essentially limited to only one file header-view.js, and these edits are easier to review, because you only have to look at one file.
If Tolik also wrote a non-dependent component, then in app.js he would touch on the same pieces of code as Peter. It is difficult to keep it connected, the connectivity between the components increases. On such a small example, this may not be very noticeable, but if you have a large amount of thousands of code and write large complex features, then you can feel it well.
In the process of writing code, there will always be a choice, either to control the component at a higher level of the hierarchy, or to let the component be managed independently.
Separate and rule gentlemen, write for your applications "independent" components.
ps Although the code examples in this article were written on bare JS without the use of frameworks, this loose coupling philosophy is also valid when used, be it Backbone or React with clever methodologies of isomorphic applications like Flux and Redux, or any other frameworks.
Always strive to limit the area of ​​responsibility in the code of your programmers when they are sawing new features. If you were given a wrench such as React, then they need to tighten the screws, and not to beat them on their fingers.
The JivoSite.ru development team wishes you a clean and clear code.
Source: https://habr.com/ru/post/301020/
All Articles