摘要:為什么要面向?qū)ο竽阈枰赖拿嫦驅(qū)ο竺嫦驅(qū)ο蟛⒉皇轻槍σ环N特定的語言,而是一種編程范式。后端傳遞過來顯示工人完成狀態(tài)的字段代表未完成,代表已完成。其實這就是如何消除代碼副作用的問題將副作用隔離。
為什么要面向?qū)ο螅?/b> 你需要知道的面向?qū)ο?/b>
面向?qū)ο蟛⒉皇轻槍σ环N特定的語言,而是一種編程范式。但是每種語言在設(shè)計之初,都會強烈地支持某種編程范式,比如面向?qū)ο蟮腏ava,而Javascript并不是強烈地支持面向?qū)ο蟆?/p> 什么時候需要面向?qū)ο螅?/b>
任何一名開發(fā)人員,在編寫具體的代碼的時候,不應該為了套用某種編程范式,而去編寫代碼和改造代碼。任何編寫方式的目的是:
讓代碼邏輯清晰
可讀性良好
沒有冗余代碼
前端編寫過程中什么時候需要面向?qū)ο螅?/b>在我的日常工作中,最不想做的的就是兩點:
復制粘貼代碼
不同的代碼中具備相同的邏輯或者變量
因為這兩種方式,會讓代碼冗余,而且不易維護。為什么?
因為相同的代碼,具備相同的邏輯,也就是具備相同的業(yè)務邏輯場景,如果場景一旦改變,你將會改變兩處代碼。
ok,到這里,我們來講一個具體的業(yè)務場景。
場景1: 前端需要顯示工人的工作完成狀態(tài),如果已經(jīng)完成了,前端提供一個查看詳情的入口,如果沒有完成,提供工人去完成任務的入口。后端傳遞過來顯示工人完成狀態(tài)的字段:user_done_status:0,代表未完成,1代表已完成。前端需要實現(xiàn)這樣一個表格:
工人名字 | 完成狀態(tài) | 操作 |
---|---|---|
小王 | 已完成 | 查看詳情 |
老王 | 未完成 | 去完成 |
// status.js // 1:需要一個狀態(tài)映射表,來實現(xiàn)第二列的功能 export const statusMap = new Map([ [0, "未完成"], [1, "已完成"] ]); // 2: 需要一個動作映射表,來實現(xiàn)第三列的功能 export const actionMap = new Map([ [0, "查看詳情"], [1, "去完成"] ]); // 3: 需要一個狀態(tài)判讀函數(shù),來實現(xiàn)第三列的功能 function isUserDone(status) { return +status === 1; } const actionMap = new Map([ [status => isUserDone(status), userCanCheckResult], [status => !isUserDone(status), needUserToCompoleteWork] ]); function handleClick() { for (let [done, action] of actionMap) { if (done()) { actionMap(); return; } } }
至于第三個為什么這么寫,可以看一下這篇文章
階段二:壞代碼的味道上面的三段代碼多帶帶寫出來沒啥問題,看看下面的可能問題就出來,這相當于實現(xiàn)了三個函數(shù),那么需要在顯示在表格中就需要這樣寫:
import { statusMap, actionMap, getUserAction } from "./status.js" .... .... // 第二列 return ( { statusMap.get(status) } ); // 第三列 return ( getUserAction(status)}> actionMap.get(status) );
這樣的寫法,看起來沒啥問題,但是可讀性是很差的,主要體現(xiàn)在兩點:
三個函數(shù)都和status相關(guān),但是展現(xiàn)形式上是割裂的
每個函數(shù)都需要傳遞一個status
可能有的人會說,這樣把上面的代碼多帶帶抽離出一個文件,也沒什么問題,狀態(tài)也是比較集中的,嗯,這種說法也沒什么問題,多帶帶提取一個文件,用作處理用的狀態(tài),是一種常見的抽象方法。但是可能會遇到下面集中情況,就會讓你很難受:
后端改了下字段,那么你就需要在階段二中的第二列和第三列中傳入?yún)?shù)的地方修改對應的字段名字(估計想宰了rd吧)
業(yè)務場景變化,工人的任務狀態(tài),添加了其他限制,比如任務的時間限制,任務有未開始、進行中、已過期三種狀態(tài),只有當在任務進行中的時候,才可以展示用戶的狀態(tài),否則就展示未開始或者已過期,總結(jié)起來,需要下面的幾種狀態(tài):
未開始
已完成/未完成
已過期
那么顯然,你就需要修改代碼的邏輯,僅僅依靠一個statusMap就不能行了。當然這里有人說了,那我把map編程一個函數(shù):
const getUserStatus = (status, startTime, endTime) => { // ...do something }
這樣是不是就可以了,嗯,說的也沒什么問題,那你需要去修改之前寫的所有代碼,傳入不同的參數(shù),就算一開始你用的不是map而是函數(shù),那么你的代碼也需要再傳入兩個多余的參數(shù),start_time和end_time。
需要解決的痛點:展現(xiàn)形式的分離,需要一種集中的狀態(tài)處理
需要傳入多個參數(shù)進行判斷,業(yè)務場景的變化或者字段的變化,都需要多處修改代碼
最開始遇到這來那個問題的時候,我想的是怎么樣能夠把所有的處理集中到一起,自然而然就想到了面向?qū)ο螅瑢?strong>用戶的狀態(tài)作為一個對象,對象具備特定的屬性和對應的操作行為。
Javascript中如何編寫面向?qū)ο蟮拇a?先睹為快,我們看一下,上面的代碼在面向?qū)ο蟮膶懛ǎ苯邮褂胑s6的class
上面業(yè)務場景的面向?qū)ο蟮膶懛?/b>import moment from "moment"; class UserStatus { constructor(props) { const keys = [ user_done_status, start_time, end_time ] ; for (let key of keys) { this.[`_${key}] = (props || {})[key]; } } static StatusMap = new Map([ [0, "未完成"], [1, "已完成"] ]); static TimeMap = newMap([ [0, "未開始"], [1, "已過期"] ]); get userDoneStatus () { return this._user_done_status; } get isInWorkingTime() { const now = new Date(); return moment(now).isBetween(moment(this._start_time), moment(this._end_time)); } get isWorkStart() { const now = new Date(); return moment(now).isAfter(moment(now)); } get userStatus () { if (this.isInWorkingTime) { return UserStatus.StatusMap.get(this.userDoneStatus); } else { return UserStatus.TimeMap.get(+this.isWorkStart); } } ... ... // 省略其他的了 }
那么寫好了上面的類,我們應該在其他地方怎么引用呢?
// 第一步:直接講后端傳過來的信息,構(gòu)造一個新的對象
const userInfo = new UserStatus(info);
// 第二步:直接調(diào)用對應的方法或者參數(shù)
return (
{
userInfo.userStatus
}
);
以后無論業(yè)務場景如何改變這部分代碼都不需要重新改寫,只需要改寫對應的類的操作就可以了。
這樣看了比較干凈的是具體的view層代碼,就是簡單的html和對應的數(shù)據(jù),沒有其他操作。其實這就是如何消除代碼副作用的問題:將副作用隔離。當你把所有的副作用隔離之后,代碼看起來干凈許多,你像redux-saga就是將對應的異步操作隔離出來。
ok,看了上面的類的寫法,我們來看一下面向?qū)ο蟮膶懛☉撘趺磳懀?/p> 面向?qū)ο?/b> 面向?qū)ο蟮娜筇匦?/b>
封裝
繼承
多態(tài)
特性 | 特點 | 舉例 |
---|---|---|
封裝 | 封裝就是對具體的屬性和實現(xiàn)細節(jié)進行隱藏,形成統(tǒng)一的的整體對外部提供對應的接口 | 上面的例子就是很好的解釋 |
繼承 | 繼承就是子類可以繼承父類的屬性和行為,也可以重寫父類的行為 | 比如工人有用戶狀態(tài),老板也有用戶狀態(tài),他們都可以繼承UserStatus這一個基類 |
多態(tài) | 同一個行為在在不同的調(diào)用方式下,具備不同的行為,依賴于抽象和重寫 | 比如工人和老板都具備一個行為那就是吃飯,工人吃的是饅頭,老板吃的是海鮮,同樣是吃這個行為,產(chǎn)生了不同的表現(xiàn)形式 |
在基本的面向?qū)ο笾杏袔讉€原則SOLID原則,但是這里我不想詳細寫了,想說一下,我在封裝對象的時候會注重的幾個方面
基類與具體數(shù)據(jù)無關(guān),只封裝了特定的行為和屬性,基類只注重抽象公共的部分
類的行為對擴展是開放的,但是對于修改是不開放的(開放封閉原則),像上面的寫法是存在風險的,因為生成的對象實例中的屬性可以被隨意的修改,我加了_,就是防止這種行為,但是最好的方式應該是使用get/set方法來對屬性限制操作;對于對象的屬性,一定要明確,因為js中一個是沒有類型的限制不要出現(xiàn)下面的寫法:
class Base { constructor(props) { for (let key of props) { this[key] = props[key]; } } }
一個類只應該依賴于他繼承的類,不能依賴于其他類,這樣能最大限度地減少耦合
注意的問題注意??在js中一定小小心this的使用,假設(shè)有一個初始類:
初始類:
class Base { constructor(props) { this._a = props.a; } status() { return this._a; } }
避免下面的行為:
// 方式1: let { status } = new Base({a: 678}); status() // 會報錯
而應該使用下面的寫法:
//方式2: let info = new Base({a: 678}); info.status(); //輸出正確
根本原因就是this在作怪,第一種this指向了全局作用域。
最后也是最重要的上面的面向?qū)ο笾饕鉀Q了前文提到的兩個痛點,但是也不是所有的業(yè)務場景都適合面向?qū)ο?/strong>,當你的代碼出現(xiàn)了一些壞味道(代碼容易、代碼分散不易處理),可以考慮下面向?qū)ο螅吘?strong>適合的才是最好的
參考資料面向?qū)ο蠓庋b的五個原則)
五個原則比較形象的解釋
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/106953.html
摘要:函數(shù)式編程的哲學就是假定副作用是造成不正當行為的主要原因。函數(shù)組合面向?qū)ο笸ǔ1槐扔鳛槊~,而函數(shù)式編程是動詞。尾遞歸優(yōu)化函數(shù)式編程語言中因為不可變數(shù)據(jù)結(jié)構(gòu)的原因,沒辦法實現(xiàn)循環(huán)。 零、前言 說到函數(shù)式編程,想必各位或多或少都有所耳聞,然而對于函數(shù)式的內(nèi)涵和本質(zhì)可能又有些說不清楚。 所以本文希望針對工程師,從應用(而非學術(shù))的角度將函數(shù)式編程相關(guān)思想和實踐(以 JavaScript 為...
摘要:它大致概述并討論了前端工程的實踐如何學習它,以及在年實踐時使用什么工具。目的是每年發(fā)布一次內(nèi)容更新。前端實踐第一部分廣泛描述了前端工程的實踐。對大多數(shù)人來說,函數(shù)式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發(fā)實踐的指南。它大致概述并...
摘要:它大致概述并討論了前端工程的實踐如何學習它,以及在年實踐時使用什么工具。目的是每年發(fā)布一次內(nèi)容更新。前端實踐第一部分廣泛描述了前端工程的實踐。對大多數(shù)人來說,函數(shù)式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發(fā)實踐的指南。它大致概述并...
摘要:它大致概述并討論了前端工程的實踐如何學習它,以及在年實踐時使用什么工具。目的是每年發(fā)布一次內(nèi)容更新。前端實踐第一部分廣泛描述了前端工程的實踐。對大多數(shù)人來說,函數(shù)式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發(fā)實踐的指南。它大致概述并...
摘要:聲明式編程一種編程范式,與命令式編程相對立。常見的聲明式編程語言有數(shù)據(jù)庫查詢語言,正則表達式邏輯編程函數(shù)式編程組態(tài)管理系統(tǒng)等。函數(shù)式編程,特別是純函數(shù)式編程,嘗試最小化狀態(tài)帶來的副作用,因此被認為是聲明式的。 編程范式與函數(shù)式編程 一、編程范式的分類 常見的編程范式有:函數(shù)式編程、程序編程、面向?qū)ο缶幊獭⒅噶钍骄幊痰取T诿嫦驅(qū)ο缶幊痰氖澜纾绦蚴且幌盗邢嗷プ饔茫ǚ椒ǎ┑膶ο螅–lass...
閱讀 1185·2021-11-24 09:39
閱讀 2690·2021-09-28 09:35
閱讀 1083·2019-08-30 15:55
閱讀 1378·2019-08-30 15:44
閱讀 887·2019-08-29 17:00
閱讀 1983·2019-08-29 12:19
閱讀 3320·2019-08-28 18:28
閱讀 701·2019-08-28 18:10