重構 - 保持函式的單一職責

守候i發表於2018-05-14

1.前言

上篇文章寫了新增擴充套件性方面的重構,講到了開放封閉原則。學習的步伐不能就此停止,今天的文章,將會提及開發的另一個原則:單一職責原則。通俗點說就是一個函式只做一件事,下面將會通過幾個例項,探究單一職責原則。

2.單一職責表現形式

單一職責的定義可以理解為:一個物件或者方法,只做一件事。

遵守單一職責的例項太多了,下面簡單列舉一下。

原生的API方面

trimRight()和trimLeft():trimRight 只負責去除右邊的空白,其它地方一概不管。 trimLeft 只負責去除右邊的空白,其它地方也一概不關。

concat(): concat 只負責連線兩個或更多的陣列,並返回結果。不會涉及刪除陣列的操作。

toFixed(): toFixed 只把 Number 型別的值四捨五入為指定小數位數的數字。不會執行其它操作。

JQuery 的 API

$.each() 只負責遍歷,要處理什麼,自己再動手操作。

css() 只負責設定 DOM 的 style ,不會設定 innerHTML 。

animate() 只負責執行 CSS 屬性集的自定義動畫,不會涉及其它操作。

說是這樣說,但是大家看著可能會有點懵,看不出來遵守單一原則有什麼好處,下面看一個例項。

3.例項-陣列處理

如下例子:

現有一批的錄入學生資訊,但是資料有重複,需要把資料根據 id 進行去重。然後把為空的資訊,改成'--'。

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))
複製程式碼

重構 - 保持函式的單一職責

執行結果沒有問題,但是大家想一下,

1.如果改了需求,比如,學生資訊不會再有重複的記錄,要求把去重的函式去掉,無論,就是整個函式都要改了,還影響到下面的操作。

2.如果專案另一個地方也是同樣的操作,但是不需要去重。這樣只能再寫一個基本一樣的函式,因為上面的函式無法複用。如下

function handle1(arr) {
    //陣列深拷貝
    let _arr=JSON.parse(JSON.stringify(arr));
    //遍歷替換
    _arr.map(item=>{
        for(let key in item){
            if(item[key]===''){
                item[key]='--';
            }
        }
    });
    return _arr;
}
複製程式碼

3.如果專案有一個地方還需要根據 ID 排序。這樣還是得寫一個函式,因為在不能在上面的函式上面排序。

function handle2(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]='--';
            }
        }
    });
    //根據ID排序
    _arr.sort((item1,item2)=>item1.id-item2.id);
    return _arr;
}
複製程式碼

這樣的問題就是在於,面對需求的變化,不能靈活的處理。函式也基本沒辦法複用。

下面使用單一原則構造一下

let 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排序
    sortForId(arr){
        return arr.sort((item1,item2)=>item1.id-item2.id);
    }
};
//去重
students=handle.removeRepeat(students);
//設定資訊
students=handle.setInfo(students);
console.log(students);
複製程式碼

重構 - 保持函式的單一職責

結果一樣,而且這樣的方式,可以使得方法可以組合使用,更加的靈活,也方便複用。

如果還需要根據ID排序,就在上面程式碼執行結果的基礎上,再加一行程式碼即可。

//根據ID排序
students=handle.sortForId(students);
console.log(students);
複製程式碼

重構 - 保持函式的單一職責

如果原始資料不需要去重,設定完資訊之後,直接排序

let students=[
    {
        id:5,
        name:'守候',
        sex:'男',
        age:'',
    },
    {
        id:2,
        name:'浪跡天涯',
        sex:'男',
        age:''
    },
    {
        id:5,
        name:'守候',
        sex:'',
        age:''
    },
    {
        id:3,
        name:'鴻雁',
        sex:'',
        age:'20'
    }
];
//設定資訊
students=handle.setInfo(students);
//根據ID排序
students=handle.sortForId(students);
複製程式碼

重構 - 保持函式的單一職責

這樣操作起來,即使以後需求有改動,在可控的範圍內,可以靈活的組合使用,函式也可以複用。

如果覺得要讓 students 連續賦值麻煩,可以借鑑 JQuery 的鏈式呼叫方式。

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);
    }
})();
let students=[
    {
        id:5,
        name:'守候',
        sex:'男',
        age:'',
    },
    {
        id:2,
        name:'浪跡天涯',
        sex:'男',
        age:''
    },
    {
        id:5,
        name:'守候',
        sex:'',
        age:''
    },
    {
        id:3,
        name:'鴻雁',
        sex:'',
        age:'20'
    }
];
//根據id去重和設定'--'
students=ec(students).unique().setInfo().end();
console.log(students)
複製程式碼

重構 - 保持函式的單一職責

結果還是一樣,只是增加了一個方法,方便鏈式呼叫。

關於實現鏈式呼叫,這個肯定是會增加程式碼的,如果呼叫的方法並不是一些常用,通用的方法的話,只是處理一些特殊格式的資料的方法(如上例項),不建議花費時間,實現鏈式呼叫,普通呼叫就好。如果是一些常用的函式的封裝,就建議使用鏈式呼叫。

4.違反單一職責原則

在上面的例項裡面,相信大家都看到了,遵守單一職責的好處,但是單一職責也有缺點,就是會增加程式碼的複雜程度。

在市面上,也有API是違反單一職責的。

JQuery 的 html() 方法,既可以獲取 innerHTML ,也可以設定 innerHTML 。 attr ()既可以獲取 DOM 元素的某一個屬性,也可以設定 DOM 元素的某一個屬性。

在維護上面,這樣的程式碼,可能會給維護增加難度,但是對於使用者而言,這樣簡化了使用。這應該是一個取捨關係,取什麼,舍什麼。這個就是具體情況具體分析。

5.小結

今天的例子就到這裡了,這個例子,解釋降解函式單一原則會有什麼好處。這個例子比上篇文章的例子還要簡單。大家看不明白,把程式碼拷貝在瀏覽器上執行,就很好理解。如果大家對這個例子有什麼更好的建議,或者程式碼上有什麼問題,歡迎在評論區留言,大家多交流,相互學習。

---------------------華麗的分割線---------------------

想了解更多,關注關注我的微信公眾號:守候書閣

重構 - 保持函式的單一職責

相關文章