Deep understanding of the JavaScript series (6): The single responsibility of S.O.L.I.D Five principles srp_javascript Skills

Source: Internet
Author: User
Tags advantage
Objective
Uncle Bob proposed and carried forward the five principles of S.O.L.I.D for better object-oriented programming, the Five Principles are:

The single Responsibility principle (SRP for sole responsibility)
The open/closed principle (opening and closing principle OCP)
The Liskov substitution principle (on the Richter replacement principle LSP)
The Interface segregation principle (ISP for interface separation principle)
The Dependency inversion principle (dependency reversal principle dip)
The five principles, I believe, have been discussed in the blog park, especially the implementation of C #, but in contrast to JavaScript, a prototype based dynamic type language, the series will be divided into 5 articles based on JavaScript programming language to demonstrate the application of the Five Principles. OK, start our first article: a single duty.

Original English: http://freshbrewedcode.com/derekgreer/2011/12/08/solid-javascript-single-responsibility-principle/

Single duty
A description of the single responsibility is as follows:

A class should have only one reason to change
The reason for a class change is that there should be only one
Copy Code
A class (JavaScript should be an object) should have a set of closely related behavior meaning what? The advantage of adhering to a single responsibility is that it makes it easy to maintain this object, and when an object encapsulates a lot of responsibilities, once a responsibility needs to be modified, it's bound to affect other code of responsibility that the object wants. Decoupling allows each job to change more flexibly.

But how do we know that multiple behaviors of an object construct multiple responsibilities or individual responsibilities? We can decide by reference to the role stereotypes concept presented in the book Object Design:roles, Responsibilies, and collaborations, which proposes the following role Stereotypes to differentiate 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 handle work and provide services to other objects
controller– This object is designed to control the decision of a series of responsible task processing
coordinator– This object does not make any decision processing work, just delegate work to other objects
interfacer– This object is designed to transform information (or requests) in various parts of the system
Once you know these concepts, it's easy to know if your code is more or more of a single responsibility.

Instance Code
The example code demonstrates adding a product to a shopping cart, the code is very bad, the code is as follows:
Copy Code code 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 = $ (' <li></li> '). HTML (product.getdescription ()). attr (' Id-cart ', Product.getid ()). Appendto ( "#cart");
}

Products.foreach (function (product) {
var newitem = $ (' <li></li> '). HTML (product.getdescription ())
. attr (' id ', Product.getid ())
. DblClick (AddToCart)
. Appendto ("#products");
});
})();

The code declares that 2 functions are used to describe product and cart, while the duty of the anonymous function is to update the screen and user interaction, which is not a very complicated example, but the anonymous function contains a lot of unrelated responsibilities, let's see how many responsibilities:

First, there is a declaration of the set of product
Second, there is a code that binds the product collection to the #product element, and it also has an attachment to an event handler added to the shopping cart
Third, there is a cart shopping cart display function
Four, there are features to add Product item to the shopping cart and display
Refactoring code
Let's break it down so that the code can be stored in its own object, and for that reason, we refer to the Martinfowler event aggregator theory to process code to communicate between objects.

First we implement the function of event aggregation, which is divided into 2 parts, one is event, the code for handler callback, the other is eventaggregator used to subscribe and publish the event, the code is as follows:
Copy Code code 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);
};
}

Then we'll declare the product object, and the code is as follows:
Copy Code code as follows:

function Product (ID, description) {
This.getid = function () {
return ID;
};
This.getdescription = function () {
return description;
};
}

Then we declare the Cart object, the object's AddItem function where we trigger the release of an event itemadded, and then we pass the item in as an argument.
Copy Code code as follows:

function Cart (eventaggregator) {
var items = [];

This.additem = function (item) {
Items.push (item);
Eventaggregator.publish ("itemadded", item);
};
}

Cartcontroller mainly accepts the Cart object and the event aggregator, adding an LI element node by subscribing to the ItemAdded to add the product by subscribing to the Productselected event.
Copy Code code as follows:

function Cartcontroller (cart, eventaggregator) {
Eventaggregator.subscribe ("ItemAdded", function (EventArgs) {
var newitem = $ (' <li></li> '). HTML (eventargs.getdescription ()). attr (' Id-cart ', Eventargs.getid ()). Appendto ("#cart");
});

Eventaggregator.subscribe ("productselected", function (EventArgs) {
Cart.additem (eventargs.product);
});
}

The goal of repository is to get data (which can be obtained from Ajax) and then expose the method of getting data.
Copy Code code as follows:

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 defines a Onproductselect method, mainly the release trigger Productselected event, foreach is mainly used to bind data to the product list, the code is as follows:
Copy Code code as follows:

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 = $ (' <li></li> '). HTML (product.getdescription ())
. attr (' id ', Product.getid ())
. DblClick (onproductselected)
. Appendto ("#products");
});
}

Finally, declare the anonymous function:
Copy Code code as follows:

(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 for anonymous functions is reduced by a lot, primarily an instantiated code of an object, in which we introduce the concept of controller, he accepts information and then passes it to action, and we also introduce the concept of repository, which is mainly used to handle product display, The result of refactoring is to write a lot of object declarations, but the advantage is that each object has its own clear responsibility, the display data display data, change processing set of processing set, so the coupling is very low.

Final code
Copy Code code 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);
};
}

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 = $ (' <li></li> '). 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 = $ (' <li></li> '). 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);
})();

Summarize
See this refactoring result, have Bo friend may ask, really need to do so complex? All I can say is that it depends on your project.

If your project is a very small project, there's not much code, but it's not necessarily refactoring that much, but if your project is a big, complex project, or if your small project is likely to grow quickly in the future, you'll have to consider the SRP principle for separation in the first place, This will be conducive to future maintenance.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.