对于函数单一原则的实践,让后期维护更加的顺眼

什么以单一职责

函数的唯一性就是函数的单一职责。

单一职责原则(SRP:Single responsibility principle)又称单一功能原则,面向对象五大原则之一

最初的出处是鲍勃大叔 于《敏捷软件开发:原则、模式与实践》一书(来自百度百科)

A class should have only one reason to change

单一职责原则,简答的说,就是函数或者方法只做一件事情。如在原生JS提供的API中:

  • concat():只负责连接两个或更多的数组,并返回结果。不会涉及删除数组的操作
  • toFixed(): toFixed 只把 Number 类型的值四舍五入为指定小数位数的数字。不会执行其它操作
  • animate(): 只负责动画,不帮你添加dom,不能帮你触发事件
  • css(): 只负责样式,而不能解析innerHtml

怎么做,举个栗子

基础

let students=[
    {
        id: 5,
        name: '守候',
        sex: '男',
        age: '',
    },
    {
        id: 2,
        name: '浪迹天涯',
        sex: '男',
        age: ''
    },
    {
        id: 5,
        name: '守候',
        sex: '',
        age: ''
    },
    {
        id: 3,
        name: '鸿雁',
        sex: '',
        age: '20'
    }
]

function handle(arr) {
    // 数组去重
    let _arr=[], _arrIds=[]
    for(let i = 0; i < arr.length; i++){
        if(_arrIds.indexOf(arr[i].id) === -1){
            _arrIds.push(arr[i].id)
            _arr.push(arr[i])
        }
    }
    // 遍历替换
    _arr.map(item => {
        for(let key in item){
            if(item[key] === ''){
                item[key] = '--'
            }
        }
    })
    return _arr
}
console.log(handle(students))
复制代码

这个也是绝大多数人都这么写,但是这个时候数据如果不会出现重复的时候,需要去掉去重的操作的时候,你咋弄,删除掉函数中的数组去重的方法么?那下面遍历替换就需要重新写逻辑。耽误时间,浪费生命!

如果这个时候加需求了,需要商户删除掉age大于20岁的数据,你又在函数后面加上判断么?如果之后需要排序呢?需求越来越多,这个函数只会越来越长,又臭又长会便秘的!!!

如果这个时候在别的页面地方也需要同样的方法, 你怎么复用呢?接着改是么?那么你的工作效率怎么提高呢?

而如果遵循单一职责的话,从函数/方法的角度出发的话,整个函数就做了两件事:

  1. 数组去重
  2. 遍历替换
const handle = {
    // 数组去重
    removeRepeat(arr) {
        let _arr = [], _arrIds = []
        for(let i = 0; i < arr.length; i++) {
            if(_arrIds.indexOf(arr[i].id) === -1){
                _arrIds.push(arr[i].id)
                _arr.push(arr[i])
            }
        }
        return _arr
    },
    // 遍历替换
    setInfo(arr) {
        arr.map(item => {
            for(let key in item) {
                if (item[key] === '') {
                    item[key] = '--'
                }
            }
        })
        return arr
    },
    // 根据id排序
    so rtForId(arr){
        return arr.sort((item1, item2) => item1.id-item2.id)
    }
}
// 去重
students = handle.removeRepeat(students)
// 设置信息
students = handle.setInfo(students)
console.log(students)
复制代码

如果这个时候需要链式操作,可以仿造JQ的写法来写。

let ec=(function () {
    let handle=function (obj) {
        this.obj=JSON.parse(JSON.stringify(obj))
    }
    handle.prototype={
        /**
         * @description 去重
         */
        unique() {
            //根据id数组去重
            let _arr = [], _arrIds = []
            for(let i = 0; i < this.obj.length; i++) {
                if(_arrIds.indexOf(this.obj[i].id) === -1) {
                    _arrIds.push(this.obj[i].id)
                    _arr.push(this.obj[i])
                }
            }
            this.obj = _arr
            return this
        },
        /**
         * @description 设置保密信息
         */
        setInfo(){
            this.obj.map(item => {
                for(let key in item) {
                    if(item[key] === '') {
                        item[key] = '--'
                    }
                }
            })
            return this
        },
        sortForId() {
            this.obj.sort((item1,item2) => item1.id - item2.id)
            return this
        },
        /**
         * @description 返回处理结果
         * @return {Array|*}
         */
        end() {
            return this.obj
        }
    }
    // 暴露构造函数接口
    return function (obj) {
        return new handle(obj)
    }
})()

// 根据id去重和设置'--'
students=ec(students).unique().setInfo().end();
console.log(students)
复制代码

这段代码都看不懂话,去重学JS吧。

扫描二维码关注公众号,回复: 13630787 查看本文章

关于实现链式调用,这个肯定是会增加代码的,如果调用的方法并不是一些常用,通用的方法的话,只是处理一些特殊格式的数据的方法(如上实例),不建议花费时间,实现链式调用,普通调用就好。如果是一些常用的函数的封装,就建议使用链式调用。

进阶

下面是w3c的实栗:

将商品添加到购物车,触发添加到购物车的操作直接是window.onload之后直接运行,所以下面用来匿名函数。

function Product(id, description) {
    this.getId = function () {
        return id;
    };
    this.getDescription = function () {
        return description;
    };
}

function Cart(eventAggregator) {
    let items = [];

    this.addItem = function (item) {
        items.push(item);
    };
}

(function () {
    let products = [new Product(1, "Star Wars Lego Ship"),
            new Product(2, "Barbie Doll"),
            new Product(3, "Remote Control Airplane")],
        cart = new Cart();

    function addToCart() {
        let productId = $(this).attr('id');
        let product = $.grep(products, function (x) {
            return x.getId() == productId;
        })[0];
        cart.addItem(product);

        let newItem = $('<li></li>').html(product.getDescription()).attr('id-cart', product.getId()).appendTo("#cart")
    }

    products.forEach(function (product) {
        let newItem = $('<li></li>').html(product.getDescription())
                                    .attr('id', product.getId())
                                    .dblclick(addToCart)
                                    .appendTo("#products")
    });
})();
复制代码

该代码声明了2个function分别用来描述product和cart,而匿名函数的职责是更新屏幕和用户交互,这还不是一个很复杂的例子,但匿名函数里却包含了很多不相关的职责,让我们来看看到底有多少职责:

首先,有product的集合的声明 其次,有一个将product集合绑定到#product元素的代码,而且还附件了一个添加到购物车的事件处理 第三,有Cart购物车的展示功能 第四,有添加product item到购物车并显示的功能

     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);
        })();
复制代码

完整的内容可以上w3c去看。

为什么要遵循单一职责

其实可以不遵循的。因为在项目中,比如React的开发当中,一个功能很少会被复用到,所以为了减少代码的复杂程度,是可以违背单一职责的。虽然在维护上面会增加难度,但是在使用当中,其他组员可以不用关心内部的实现,直接进行调用。

如果项目非常的小,而公司没有内部通用的方法库或者组件库的话,可以不用拆分和精炼到这种程度。如果预感到后期需求会加得很多的话,那么在前期就应该对项目进行重构了。

猜你喜欢

转载自juejin.im/post/7048132186142146573
今日推荐