In depth understanding of JavaScript series (37) : a detailed explanation of design patterns

  • 2020-05-10 17:38:46
  • OfStack

introduce

Enjoy mode (Flyweight), run sharing technology to effectively support a large number of fine-grained objects, avoid the large number of small classes with the same content overhead (such as memory consumption), let everyone share a class (metaclass).

The schema can avoid the overhead of a large number of very similar classes. In programming, it is sometimes necessary to produce a large number of fine-grained class instances to represent data. If you can find that these instances have basically the same overhead except for a few parameters, you can significantly reduce the number of classes that need to be instantiated. If you can move those parameters out of the class instance and pass them in when the method is called, you can dramatically reduce the number of instances by sharing them.

What if you apply the enjoy mode to JavaScript? There are two ways, the first is to apply on the data layer, mainly on a large number of similar objects in memory; The second is applied on the DOM layer, where the browser can be used on the central event manager to avoid attaching event handles to each child element in the parent container.

Enjoy the metadata and data layer

There are two important concepts in Flyweight -- the internal state intrinsic and the external state extrinsic. The internal state is managed through internal methods in the object, while the external information can be deleted or saved externally.

Said white spots, is to tweak one of the original model, and then with different situations and environments, to produce different typical concrete model, obviously, here need to produce different new objects, so often Factory Flyweight mode, Flyweight internal state is used to share, Flyweight factory is responsible for maintaining a Flyweight pool pool (mode) to hold the internal state of the object.

Use enjoy mode

Let's demonstrate 1. If we let the system manage all the books through a class library, the metadata of each book is tentatively as follows:


ID
Title
Author
Genre
Page count
Publisher ID
ISBN

We also need to define when each book will be lent out and by whom, as well as when and if it will be available:

checkoutDate
checkoutMember
dueReturnDate
availability

Because the book object is set to the following code, note that this code has not yet been optimized:

var Book = function( id, title, author, genre, pageCount,publisherID, ISBN, checkoutDate, checkoutMember, dueReturnDate,availability ){
   this.id = id;
   this.title = title;
   this.author = author;
   this.genre = genre;
   this.pageCount = pageCount;
   this.publisherID = publisherID;
   this.ISBN = ISBN;
   this.checkoutDate = checkoutDate;
   this.checkoutMember = checkoutMember;
   this.dueReturnDate = dueReturnDate;
   this.availability = availability;
};
Book.prototype = {
   getTitle:function(){
       return this.title;
   },
   getAuthor: function(){
       return this.author;
   },
   getISBN: function(){
       return this.ISBN;
   },
/* other get The method is not shown here */ // Update lending status
updateCheckoutStatus: function(bookID, newStatus, checkoutDate,checkoutMember, newReturnDate){
   this.id  = bookID;
   this.availability = newStatus;
   this.checkoutDate = checkoutDate;
   this.checkoutMember = checkoutMember;
   this.dueReturnDate = newReturnDate;
},
// renew
extendCheckoutPeriod: function(bookID, newReturnDate){
    this.id =  bookID;
    this.dueReturnDate = newReturnDate;
},
// Whether due to
isPastDue: function(bookID){
   var currentDate = new Date();
   return currentDate.getTime() > Date.parse(this.dueReturnDate);
 }
};

The program may be fine at first, but as time goes on, the books may increase in volume, and each book has a different version and number, you will find that the system becomes slower and slower. With thousands of book objects in memory, you can imagine that we need to optimize them using the enjoy mode.

We can divide the data into internal and external data. Data related to book objects (title, author, etc.) can be attributed to internal attributes, while (checkoutMember, dueReturnDate, etc.) can be attributed to external attributes. In this way, the following code can share the same object in the same book, because no matter who borrowed the book, as long as the book is the same book, the basic information is the same:


/* Enjoy mode optimization code */
var Book = function(title, author, genre, pageCount, publisherID, ISBN){
   this.title = title;
   this.author = author;
   this.genre = genre;
   this.pageCount = pageCount;
   this.publisherID = publisherID;
   this.ISBN = ISBN;
};

Define the base plant

Let's define a basic factory to check if the book object was created before, return if it was, and recreate and store it for later access. This ensures that we only create one object for each type of book:


/* Book The factory The singleton */
var BookFactory = (function(){
   var existingBooks = {};
   return{
       createBook: function(title, author, genre,pageCount,publisherID,ISBN){
       /* Find whether it was created before */
           var existingBook = existingBooks[ISBN];
           if(existingBook){
                   return existingBook;
               }else{
               /* If not, create 1 One, and save it */
               var book = new Book(title, author, genre,pageCount,publisherID,ISBN);
               existingBooks[ISBN] =  book;
               return book;
           }
       }
   }
});

Managing external state
External state is relatively simple. Except for the book we have encapsulated, everything else needs to be managed here:

/*BookRecordManager Library management The singleton */
var BookRecordManager = (function(){
   var bookRecordDatabase = {};
   return{
       /* Add a borrowing record */
       addBookRecord: function(id, title, author, genre,pageCount,publisherID,ISBN, checkoutDate, checkoutMember, dueReturnDate, availability){
           var book = bookFactory.createBook(title, author, genre,pageCount,publisherID,ISBN);
            bookRecordDatabase[id] ={
               checkoutMember: checkoutMember,
               checkoutDate: checkoutDate,
               dueReturnDate: dueReturnDate,
               availability: availability,
               book: book;            };
       },
    updateCheckoutStatus: function(bookID, newStatus, checkoutDate, checkoutMember,     newReturnDate){
        var record = bookRecordDatabase[bookID];
        record.availability = newStatus;
        record.checkoutDate = checkoutDate;
        record.checkoutMember = checkoutMember;
        record.dueReturnDate = newReturnDate;
   },
   extendCheckoutPeriod: function(bookID, newReturnDate){
       bookRecordDatabase[bookID].dueReturnDate = newReturnDate;
   },
   isPastDue: function(bookID){
       var currentDate = new Date();
       return currentDate.getTime() > Date.parse(bookRecordDatabase[bookID].dueReturnDate);
   }
 };
});

In this way, we are able to save the same information of the same type of books in an bookmanager object, and only save one copy; Compared to the previous code, you can find a lot of memory savings.

Enjoy yuan mode with DOM

I won't say much more about the bubbling incident of DOM. I believe that everyone knows about it. Let's give two examples.

Example 1: centralized event management

, for example, if we have a lot of similar type of elements or structures (such as menu, or in multiple ul li) need to monitor his click events, so need to each element in event, if there is a very, very much element, the performance is all too clear, and combining the knowledge of the bubble, any one child have the event trigger, the trigger events will bubble to the upper level 1 element, so use this feature, we can use the flyweight pattern, we can the parent element of the similar elements event monitoring, Then determine which of the child elements has an event triggered, and proceed to the next step.

Here we combine jQuery's bind/unbind method with 1 as an example.

HTML:


<div id="container">
   <div class="toggle" href="#"> For more information ( address )
       <span class="info">
          Here's more
       </span></div>
   <div class="toggle" href="#"> For more information ( The map )
       <span class="info">
          <iframe src="http://www.map-generator.net/extmap.php?name=London&amp;address=london%2C%20england&amp;width=500...gt;"</iframe>
       </span>
   </div>
</div>

JavaScript:

stateManager = {
   fly: function(){
       var self =  this;
       $('#container').unbind().bind("click", function(e){
           var target = $(e.originalTarget || e.srcElement);
           // Determine what is 1 child
           if(target.is("div.toggle")){
               self.handleClick(target);
           }
       });
   },    handleClick: function(elem){
       elem.find('span').toggle('slow');
   }
});

Example 2: apply the enjoy mode to improve performance

Another example is still related to jQuery. Generally, we use element object in the callback function of the event after the event, and we often use the form of $(this). In fact, it repeatedly creates new object, because this in the callback function is already DOM element itself, we need to use the following code:


$('div').bind('click', function(){
 console.log('You clicked: ' + $(this).attr('id'));
});
// Above the code, to avoid using, to avoid again DOM Element generation jQuery Object, because you can use it directly here DOM The element itself.
$('div').bind('click', function(){
 console.log('You clicked: ' + this.id);
});

In fact, if we have to use the form $(this), we can also implement our own version of single-instance mode. For example, we can implement a function like jQuery.signle (this) to return the DOM element itself:

jQuery.single = (function(o){    var collection = jQuery([1]);
   return function(element) {        // Put the elements in the set
       collection[0] = element;         // Returns the collection
       return collection;    };
 });
 

Usage:

checkoutDate
checkoutMember
dueReturnDate
availability
0
This returns the DOM element itself as it is, and no jQuery object is created.

conclusion

Flyweight mode is a mode that improves the efficiency and performance of the application, which will greatly speed up the application. For example, if you want to read 1 series strings from a database, many of these strings are duplicates, then we can store these strings in Flyweight pool (pool).

If an application USES a large number of objects, and these large Numbers of objects make for a large amount of storage, you should consider using the enjoy mode. Also, most of the state of the object can be external state. If the external state of the object is deleted, then many group objects can be replaced with relatively few Shared objects.


Related articles: