loading...

re: Elegant patterns in modern JavaScript: Ice Factory VIEW POST

TOP OF THREAD FULL DISCUSSION
re: I really like this way of doing things (the Ice Factory Function). Thanks for writing a great article. There seems to be a fundamental difference ...

Hi Nick.

Thanks for the feedback and the question.

The Ice Factory pattern takes a decidedly OOP view of the world. It just sidesteps some of the drawbacks of inheritance and a lot of the quirkiness around JavaScript's class syntax.

It's easy to feel like anything that doesn't have the word "class" in it must be something other than OOP, but as Dave Thomas once said:

"Design with Objects, not Classes... It's OO Programming, not CO [Class-Oriented] Programming".

Having said that, FP is great. There is a lot we can learn from FP, and JavaScript is a fascinating language in that it gives us both Objects and Functions as "first class" citizens of the language, so we can take advantage of both paradigms and match the approach to the problem we are trying to solve.

In FP, we separate the data from the code that transforms the data. In our shopping cart example, the data is a list of items and the transformations include adding and removing items from the list.

An FP-style shopping cart could look something like this:

// shopping-cart-fp.js

import freezeMap from './utils/freeze-map.js';

export function addProductToCart(product, cart) {
    const newItem = Object.assign({}, product);
    newItem.qty = newItem.qty || 1;

    if (cart.has(newItem.id)) {
        newItem.qty = cart.get(newItem.id).qty + 1;
    }

    const newCart = new Map(cart);
    newCart.set(newItem.id, newItem);

    return freezeMap(newCart);
}

export function removeProductFromCart(productId, cart) {
    const newCart = new Map(cart);

    if (newCart.has(productId)) {
        const product = Object.assign({}, newCart.get(productId));

        if (product.qty < 2) {
            newCart.delete(productId);
        } else {
            --product.qty;
            newCart.set(productId, product);
        }
    }
    return freezeMap(newCart);
}

I'm using a Map here – which can't be frozen – so we need a little utility function to freeze it to ensures that no one messes with our cart data. Like so:

// freeze-map.js
export default function freezeMap(map) {
    map.set = map.clear = map.delete = () => {
        throw new Error('Attempted to change a frozen Map object.');
    };

    return map
}

Hope that's helpful.

Regards,
Bill

Code of Conduct Report abuse