Книга: Functional Programming in JavaScript
Назад: Mixing functional and object-oriented programming in JavaScript
Дальше: Mixins

Functional inheritance

Perhaps the most accessible way to apply functional programming to JavaScript applications is to use a mostly functional style within OOP principles, such as inheritance.

To explore how this might work, let's build a simple application that calculates the price of a product. First, we'll need some product classes:

var Shirt = function(size) {   this.size = size; };  var TShirt = function(size) {   this.size = size; }; TShirt.prototype = Object.create(Shirt.prototype); TShirt.prototype.constructor = TShirt; TShirt.prototype.getPrice = function(){   if (this.size == 'small') {     return 5;   }   else {     return 10;   } }  var ExpensiveShirt = function(size) {   this.size = size; } ExpensiveShirt.prototype = Object.create(Shirt.prototype); ExpensiveShirt.prototype.constructor = ExpensiveShirt; ExpensiveShirt.prototype.getPrice = function() {   if (this.size == 'small') {     return 20;   }   else {     return 30;   } }

We can then organize them within a class as follows:

var Store = function(products) {   this.products = products; } Store.prototype.calculateTotal = function(){   return this.products.reduce(function(sum,product) {     return sum + product.getPrice();   }, 10) * TAX; // start with $10 markup, times global TAX var };  var TAX = 1.08; var p1 = new TShirt('small'); var p2 = new ExpensiveShirt('large'); var s = new Store([p1,p2]); console.log(s.calculateTotal()); // Output: 35

The method uses the array's function to cleanly sum together the prices of the products.

This works just fine, but what if we need a dynamic way to calculate the markup value? For this, we can turn to a concept called Strategy Pattern.

Strategy Pattern

Strategy Pattern is a method for defining a family of interchangeable algorithms. It is used by OOP programmers to manipulate behavior at runtime, but it is based on a few functional programming principles:

  • Separation of logic and data
  • Composition of functions
  • Functions as first-class objects

And a couple of OOP principles as well:

  • Encapsulation
  • Inheritance

In our example application for calculating product cost, explained previously, let's say we want to give preferential treatment to certain customers, and that the markup will have to be adjusted to reflect this.

So let's create some customer classes:

var Customer = function(){}; Customer.prototype.calculateTotal = function(products) {   return products.reduce(function(total, product) {     return total + product.getPrice();   }, 10) * TAX; };  var RepeatCustomer = function(){}; RepeatCustomer.prototype = Object.create(Customer.prototype); RepeatCustomer.prototype.constructor = RepeatCustomer; RepeatCustomer.prototype.calculateTotal = function(products) {   return products.reduce(function(total, product) {     return total + product.getPrice();   }, 5) * TAX; };  var TaxExemptCustomer = function(){}; TaxExemptCustomer.prototype = Object.create(Customer.prototype); TaxExemptCustomer.prototype.constructor = TaxExemptCustomer; TaxExemptCustomer.prototype.calculateTotal = function(products) {   return products.reduce(function(total, product) {     return total + product.getPrice();   }, 10); };

Each class encapsulates the algorithm. Now we just need the class to call the class's method.

var Store = function(products) {   this.products = products;   this.customer = new Customer();   // bonus exercise: use Maybes from Chapter 5 instead of a default customer instance } Store.prototype.setCustomer = function(customer) {   this.customer = customer; } Store.prototype.getTotal = function(){   return this.customer.calculateTotal(this.products); };  var p1 = new TShirt('small'); var p2 = new ExpensiveShirt('large'); var s = new Store([p1,p2]); var c = new TaxExemptCustomer(); s.setCustomer(c); s.getTotal(); // Output: 45

The classes do the calculating, the classes hold the data (the prices), and the class maintains the context. This achieves a very high level of cohesion and a very good mixture of object-oriented programming and functional programming. JavaScript's high level of expressiveness makes this possible and quite easy.

Назад: Mixing functional and object-oriented programming in JavaScript
Дальше: Mixins

bsn
thank
Vesa Karvonen
I hope you don't mind, but I’d like to point you and your readers to my high-performance optics library for JavaScript that is in production use in multiple projects, has comprehensive support for partial optics and interactive documentation: https://calmm-js.github.io/partial.lenses/ (this takes a moment to load — be patient!)