


In-depth understanding of JavaScript series (6): Single Responsibility SRP_javascript skills of the five principles of S.O.L.I.D
Foreword
Uncle Bob proposed and carried forward the five principles of S.O.L.I.D to better implement object-oriented programming. The five principles are:
The Single Responsibility Principle (Single Responsibility SRP)
The Open/ Closed Principle (OCP)
The Liskov Substitution Principle (LSP)
The Interface Segregation Principle (ISP)
The Dependency Inversion Principle (DIP)
I believe the five principles have been discussed in the blog community, especially the implementation of C#, but compared to JavaScript, a prototype-based dynamic type language, there are still few. This series will be divided into 5 articles. Demonstrates the application of the five principles based on the JavaScript programming language. OK, let’s start our first article: single responsibility.
English original text: http://freshbrewedcode.com/derekgreer/2011/12/08/solid-javascript-single-responsibility-principle/
Single responsibility
Description of single responsibility As follows:
A class should have only one reason to change
There should be only one reason for a class change
Copy code
A class (should be an object under JavaScript) should have a close set of What does the relevant behavior mean? The advantage of adhering to a single responsibility is that it allows us to easily maintain this object. When an object encapsulates many responsibilities, once one responsibility needs to be modified, it will inevitably affect the code of other responsibilities of the object. Through decoupling, each responsible worker can be made more flexible to change.
However, how do we know that multiple behaviors of an object construct multiple responsibilities or a single responsibility? We can decide by referring to the concept of Role Stereotypes proposed in the book Object Design: Roles, Responsibilies, and Collaborations, which proposes the following Role Stereotypes to distinguish responsibilities:
Information holder – This object is designed to store objects and Provide object information to other objects.
Structurer - This object is designed to maintain the relationship between objects and information
Service provider - This object is designed to process work and provide services to other objects
Controller - This object is designed to control a series of responsible decisions Task Processing
Coordinator - This object does not do any decision-making processing work, it just delegates work to other objects
Interfacer - This object is designed to transform information (or requests) in various parts of the system
Once you know With these concepts, it is easy to know whether your code has multiple responsibilities or a single responsibility.
Example code
This example code demonstrates adding items to the shopping cart. The code is very bad. The code is as follows:
function Product(id, description) {
this.getId = function () {
return id;
};
this.getDescription = function () {
return description;
};
}
function Cart(eventAggregator) {
var items = [];
this.addItem = function (item) {
items.push(item);
};
}
(function () {
var products = [new Product (1, "Star Wars Lego Ship"),
new Product(2, "Barbie Doll"),
new Product(3, "Remote Control Airplane")],
cart = new Cart() ;
function addToCart() {
var productId = $(this).attr('id');
var product = $.grep(products, function (x) {
return x.getId() == productId;
})[0];
cart.addItem(product);
var newItem = $(' ').html(product.getDescription()).attr('id-cart', product.getId()).appendTo("#cart");
}
products.forEach(function (product) {
var newItem = $('').html(product.getDescription())
.attr('id', product.getId())
.dblclick(addToCart)
.appendTo("#products");
});
})();
This code declares 2 functions respectively Used to describe products and carts, and the responsibility of the anonymous function is to update the screen and interact with the user. This is not a very complex example, but the anonymous function contains many unrelated responsibilities. Let's take a look at how many responsibilities there are. :
First, there is a declaration of the product collection
Secondly, there is a code that binds the product collection to the #product element, and also attaches an event handler for adding to the shopping cart
Third, there is the function of displaying the Cart shopping cart
Fourth, there is the function of adding product items to the shopping cart and displaying them
Refactoring the code
Let us break it down so that the codes can be stored in their own objects. , To this end, we refer to the event aggregator (Event Aggregator) theory of martinfowler to process the code to communicate between objects.
First of all, let’s implement the event aggregation function. This function is divided into two parts, one is Event, which is used for Handler callback code, and the other is EventAggregator, which is used to subscribe and publish Events. The code is as follows:
function Event(name) {
var handlers = [];
this.getName = function () {
return name;
};
this.addHandler = function (handler) {
handlers.push(handler);
};
this.removeHandler = function (handler) {
for (var i = 0; i < handlers.length; i ) {
if (handlers[i] == handler) {
handlers.splice(i, 1);
break;
}
}
};
this.fire = function (eventArgs) {
handlers.forEach(function (h) {
h(eventArgs);
});
};
}
function EventAggregator() {
var events = [];
function getEvent(eventName) {
return $.grep(events, function (event) {
return event.getName() === eventName;
})[0];
}
this.publish = function (eventName, eventArgs) {
var event = getEvent(eventName);
if (!event) {
event = new Event(eventName);
events.push(event);
}
event.fire(eventArgs);
};
this.subscribe = function (eventName, handler) {
var event = getEvent(eventName);
if (!event) {
event = new Event(eventName);
events.push(event);
}
event.addHandler(handler);
};
}
然后,我们来声明Product对象,代码如下:
function Product(id, description) {
this.getId = function () {
return id;
};
this.getDescription = function () {
return description;
};
}
接着来声明Cart对象,该对象的addItem的function里我们要触发发布一个事件itemAdded,然后将item作为参数传进去。
function Cart(eventAggregator) {
var items = [];
this.addItem = function (item) {
items.push(item);
eventAggregator.publish("itemAdded", item);
};
}
CartController主要是接受cart对象和事件聚合器,通过订阅itemAdded来增加一个li元素节点,通过订阅productSelected事件来添加product。
function CartController(cart, eventAggregator) {
eventAggregator.subscribe("itemAdded", function (eventArgs) {
var newItem = $('').html(eventArgs.getDescription()).attr('id-cart', eventArgs.getId()).appendTo("#cart");
});
eventAggregator.subscribe("productSelected", function (eventArgs) {
cart.addItem(eventArgs.product);
});
}
Repository的目的是为了获取数据(可以从ajax里获取),然后暴露get数据的方法。
function ProductRepository() {
var products = [new Product(1, "Star Wars Lego Ship"),
new Product(2, "Barbie Doll"),
new Product(3, "Remote Control Airplane")];
this.getProducts = function () {
return products;
}
}
ProductController里定义了一个onProductSelect方法,主要是发布触发productSelected事件,forEach主要是用于绑定数据到产品列表上,代码如下:
function ProductController(eventAggregator, productRepository) {
var products = productRepository.getProducts();
function onProductSelected() {
var productId = $(this).attr('id');
var product = $.grep(products, function (x) {
return x.getId() == productId;
})[0];
eventAggregator.publish("productSelected", {
product: product
});
}
products.forEach(function (product) {
var newItem = $('').html(product.getDescription())
.attr('id', product.getId())
.dblclick(onProductSelected)
.appendTo("#products");
});
}
最后声明匿名函数:
(function () {
var eventAggregator = new EventAggregator(),
cart = new Cart(eventAggregator),
cartController = new CartController(cart, eventAggregator),
productRepository = new ProductRepository(),
productController = new ProductController(eventAggregator, productRepository);
})();
You can see that the code of the anonymous function has been reduced a lot, mainly one The instantiation code of the object. In the code, we introduced the concept of Controller, which receives information and then passes it to the action. We also introduced the concept of Repository, which is mainly used to handle the display of products. The result of refactoring is to write a lot of Object declaration, but the advantage is that each object has its own clear responsibilities. The display data should be displayed, and the processing collection should be changed to process the collection, so the degree of coupling is very low.
Final code
function Event(name ) {
var handlers = [];
this.getName = function () {
return name;
};
this.addHandler = function (handler) {
handlers.push(handler);
};
this.removeHandler = function (handler) {
for (var i = 0; i < handlers.length; i ) {
if (handlers[i] == handler) {
handlers.splice(i, 1);
break;
}
}
};
this.fire = function (eventArgs) {
handlers.forEach(function (h) {
h(eventArgs);
});
};
}
function EventAggregator() {
var events = [];
function getEvent(eventName) {
return $.grep(events, function (event) {
return event.getName( ) === eventName;
})[0];
}
this.publish = function (eventName, eventArgs) {
var event = getEvent(eventName);
if (!event) {
event = new Event(eventName);
events.push(event);
}
event.fire(eventArgs);
};
this.subscribe = function (eventName, handler) {
var event = getEvent(eventName);
if (!event) {
event = new Event(eventName);
events.push(event);
}
event.addHandler(handler);
};
}
function Product(id, description) {
this.getId = function () {
return id;
};
this.getDescription = function () {
return description;
};
}
function Cart(eventAggregator) {
var items = [];
this.addItem = function (item) {
items.push(item);
eventAggregator.publish ("itemAdded", item);
};
}
function CartController(cart, eventAggregator) {
eventAggregator.subscribe("itemAdded", function (eventArgs) {
var newItem = $('').html(eventArgs.getDescription()).attr('id-cart', eventArgs.getId()).appendTo("#cart");
});
eventAggregator.subscribe("productSelected", function (eventArgs) {
cart.addItem(eventArgs.product);
});
}
function ProductRepository() {
var products = [new Product(1, "Star Wars Lego Ship"),
new Product(2, "Barbie Doll"),
new Product(3, "Remote Control Airplane")];
this.getProducts = function () {
return products;
}
}
function ProductController(eventAggregator, productRepository) {
var products = productRepository.getProducts();
function onProductSelected() {
var productId = $(this).attr('id');
var product = $.grep (products, function (x) {
return x.getId() == productId;
})[0];
eventAggregator.publish("productSelected", {
product: product
});
}
products.forEach(function (product) {
var newItem = $('').html(product.getDescription( ))
.attr('id', product.getId())
.dblclick(onProductSelected)
.appendTo("#products");
});
}
(function () {
var eventAggregator = new EventAggregator(),
cart = new Cart(eventAggregator),
cartController = new CartController(cart, eventAggregator),
productRepository = new ProductRepository(),
productController = new ProductController(eventAggregator, productRepository);
})();
Summary
Seeing this refactoring result, some bloggers may want to I asked, is it really necessary to make it so complicated? All I can say is: whether you should do this or not depends on the circumstances of your project.
If your project is a very small project and there is not a lot of code, there is actually no need to refactor it so complicatedly, but if your project is a very complex large-scale project, or your small project If it may grow very fast in the future, you must consider the SRP principle for separation of responsibilities in the early stage, so as to facilitate future maintenance.

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics





Frequently Asked Questions and Solutions for Front-end Thermal Paper Ticket Printing In Front-end Development, Ticket Printing is a common requirement. However, many developers are implementing...

There is no absolute salary for Python and JavaScript developers, depending on skills and industry needs. 1. Python may be paid more in data science and machine learning. 2. JavaScript has great demand in front-end and full-stack development, and its salary is also considerable. 3. Influencing factors include experience, geographical location, company size and specific skills.

JavaScript is the cornerstone of modern web development, and its main functions include event-driven programming, dynamic content generation and asynchronous programming. 1) Event-driven programming allows web pages to change dynamically according to user operations. 2) Dynamic content generation allows page content to be adjusted according to conditions. 3) Asynchronous programming ensures that the user interface is not blocked. JavaScript is widely used in web interaction, single-page application and server-side development, greatly improving the flexibility of user experience and cross-platform development.

How to merge array elements with the same ID into one object in JavaScript? When processing data, we often encounter the need to have the same ID...

Discussion on the realization of parallax scrolling and element animation effects in this article will explore how to achieve similar to Shiseido official website (https://www.shiseido.co.jp/sb/wonderland/)...

Learning JavaScript is not difficult, but it is challenging. 1) Understand basic concepts such as variables, data types, functions, etc. 2) Master asynchronous programming and implement it through event loops. 3) Use DOM operations and Promise to handle asynchronous requests. 4) Avoid common mistakes and use debugging techniques. 5) Optimize performance and follow best practices.

Explore the implementation of panel drag and drop adjustment function similar to VSCode in the front-end. In front-end development, how to implement VSCode similar to VSCode...

In-depth discussion of the root causes of the difference in console.log output. This article will analyze the differences in the output results of console.log function in a piece of code and explain the reasons behind it. �...
