🔍 搜尋結果:kevin

🔍 搜尋結果:kevin

SOLID-簡單的理解方式

你好呀!你最近怎麼樣?你沒事吧?但願如此! 今天我要談一個每個人都在談論或寫到的主題。但有時要理解每一個原則是很困難的。我說的是固體。 當我問及 SOLID 時,很多人可能總是記得第一個原則(單一職責原則)。但當我問起另一個人時,有些人不記得了,或覺得很難解釋。我明白了。 確實,如果不編碼或重新修改每個原則的定義,就很難解釋。但在本文中,我想以簡單的方式介紹每個原則。所以我會用Typescript來舉例。 那麼就讓我們開始吧! 單一職責原則 - SRP ------------ 更容易理解和記住的原則。 當我們編碼時,很容易發現我們何時忘記了原則。 假設我們有一個 TaskManager 類別: ``` class TaskManager { constructor() {} connectAPI(): void {} createTask(): void { console.log("Create Task"); } updateTask(): void { console.log("Update Task"); } removeTask(): void { console.log("Remove Task"); } sendNotification(): void { console.log("Send Notification"); } sendReport(): void { console.log("Send Report"); } } ``` 好的!也許你注意到你的問題了,不是嗎? TaskManager 類別有很多不屬於她的職責。例如:sendNotification 和 sendReport 方法。 現在,讓我們重構並應用該解決方案: ``` class APIConnector { constructor() {} connectAPI(): void {} } class Report { constructor() {} sendReport(): void { console.log("Send Report"); } } class Notificator { constructor() {} sendNotification(): void { console.log("Send Notification"); } } class TaskManager { constructor() {} createTask(): void { console.log("Create Task"); } updateTask(): void { console.log("Update Task"); } removeTask(): void { console.log("Remove Task"); } } ``` 很簡單,不是嗎?我們只是將通知和報告分離到指定的類別中。現在我們尊重單一原則責任! 定義: `Each class must have one, and only one, reason to change` 。 開閉原則-OCP -------- 第二個原則。另外,我認為很容易理解。給您一個提示,如果您發現在某種方法中需要滿足很多條件來驗證某些內容,那麼您可能遇到了 OCP。 讓我們想像一下下面的考試類別範例: ``` type ExamType = { type: "BLOOD" | "XRay"; }; class ExamApprove { constructor() {} approveRequestExam(exam: ExamType): void { if (exam.type === "BLOOD") { if (this.verifyConditionsBlood(exam)) { console.log("Blood Exam Approved"); } } else if (exam.type === "XRay") { if (this.verifyConditionsXRay(exam)) { console.log("XRay Exam Approved!"); } } } verifyConditionsBlood(exam: ExamType): boolean { return true; } verifyConditionsXRay(exam: ExamType): boolean { return false; } } ``` 是的,您可能已經多次看到這段程式碼了。首先,我們打破了SRP的第一原則,並提出了許多條件。 現在想像一下如果出現另一種類型的檢查,例如超音波。我們需要加入另一個方法來驗證和另一個條件。 讓我們重構一下這段程式碼: ``` type ExamType = { type: "BLOOD" | "XRay"; }; interface ExamApprove { approveRequestExam(exam: NewExamType): void; verifyConditionExam(exam: NewExamType): boolean; } class BloodExamApprove implements ExamApprove { approveRequestExam(exam: ExamApprove): void { if (this.verifyConditionExam(exam)) { console.log("Blood Exam Approved"); } } verifyConditionExam(exam: ExamApprove): boolean { return true; } } class RayXExamApprove implements ExamApprove { approveRequestExam(exam: ExamApprove): void { if (this.verifyConditionExam(exam)) { console.log("RayX Exam Approved"); } } verifyConditionExam(exam: NewExamType): boolean { return true; } } ``` 哇,好多了!現在,如果出現另一種類型的檢查,我們只需實作`ExamApprove`介面。如果出現另一種類型的考試驗證,我們只更新介面。 定義: `Software entities (such as classes and methods) must be open for extension but closed for modification` 里氏替換原理 - LSP ------------ 理解和解釋比較複雜的一種。但我怎麼說,我會讓你更容易理解。 想像一下,您有一所大學和兩種類型的學生。學生和研究生。 ``` class Student { constructor(public name: string) {} study(): void { console.log(`${this.name} is studying`); } deliverTCC() { /** Problem: Post graduate Students don't delivery TCC */ } } class PostgraduateStudent extends Student { study(): void { console.log(`${this.name} is studying and searching`); } } ``` 我們這裡有一個問題,我們正在延長學生的時間,但研究生不需要提供 TCC。他只是研究和尋找。 我們要怎麼解決這個問題呢?簡單的!讓我們建立一個學生類,並將畢業學生和畢業後學生分開: ``` class Student { constructor(public name: string) {} study(): void { console.log(`${this.name} is studying`); } } class StudentGraduation extends Student { study(): void { console.log(`${this.name} is studying`); } deliverTCC() {} } class StudentPosGraduation extends Student { study(): void { console.log(`${this.name} is studying and searching`); } } ``` 現在我們有更好的方法來分離他們各自的職責。這個原理的名字可能很可怕,但它的原理很簡單。 定義: `Derived classes (or child classes) must be able to replace their base classes (or parent classes)` 介面隔離原則-ISP ---------- 要理解這個原理,訣竅是記住定義。不應強迫類別實作不會使用的方法。 因此,假設您有一個類別實作了一個從未使用過的介面。 讓我們想像一下某個商店的賣家和接待員的場景。賣家和接待員都有薪水,但只有賣家有佣金。 讓我們看看問題: ``` interface Employee { salary(): number; generateCommission(): void; } class Seller implements Employee { salary(): number { return 1000; } generateCommission(): void { console.log("Generating Commission"); } } class Receptionist implements Employee { salary(): number { return 1000; } generateCommission(): void { /** Problem: Receptionist don't have commission */ } } ``` 兩者都實現了 Employee 接口,但接待員沒有佣金。所以我們被迫實施一種永遠不會被使用的方法。 所以解決方法: ``` interface Employee { salary(): number; } interface Commissionable { generateCommission(): void; } class Seller implements Employee, Commissionable { salary(): number { return 1000; } generateCommission(): void { console.log("Generating Commission"); } } class Receptionist implements Employee { salary(): number { return 1000; } } ``` 輕鬆輕鬆!現在我們有兩個介面了!雇主類別和佣金介面。現在只有賣方將實現將獲得佣金的兩個介面。接待員不僅負責員工的工作。因此,接待員不必被迫實施永遠不會使用的方法。 定義: `A class should not be forced to implement interfaces and methods that will not be used.` 依賴倒置原則—DIP ---------- 最後一個!光看名字就會覺得很難記!但也許你每次都已經看到這個原則了。 想像一下,您有一個 Service 類,它與一個將呼叫資料庫的 Repository 類別集成,例如 Postgress。但是,例如,如果 MongoDB 的儲存庫類別發生變化並且資料庫發生變化。 讓我們來看看例子: ``` interface Order { id: number; name: string; } class OrderRepository { constructor() {} saveOrder(order: Order) {} } class OrderService { private orderRepository: OrderRepository; constructor() { this.orderRepository = new OrderRepository(); } processOrder(order: Order) { this.orderRepository.saveOrder(order); } } ``` 我們注意到,儲存庫是 OrderService 類,直接耦合到 OrderRepository 類別的具體實作。 讓我們重構一下這個例子: ``` interface Order { id: number; name: string; } class OrderRepository { constructor() {} saveOrder(order: Order) {} } class OrderService { private orderRepository: OrderRepository; constructor(repository: OrderRepository) { this.orderRepository = repository; } processOrder(order: Order) { this.orderRepository.saveOrder(order); } } ``` 好的!好多了!現在我們接收儲存庫作為建構函數的參數來實例化和使用。現在我們依賴抽象,我們不需要知道我們正在使用哪個儲存庫。 定義: `depend on abstractions rather than concrete implementations` 結論 --- 那你現在感覺怎麼樣?我希望透過這個簡單的範例,您可以記住並理解在程式碼中使用這些原則的內容和原因。除了應用乾淨的程式碼之外,它還更容易理解和擴展。 我希望你喜歡! 非常感謝您,祝您永遠健康! 聯絡方式: 領英:https://www.linkedin.com/in/kevin-uehara/ Instagram:https://www.instagram.com/uehara\_kevin/ 推特:https://twitter.com/ueharaDev Github:https://github.com/kevinuehara dev.to:https://dev.to/kevin-uehara Youtube:https://www.youtube.com/@ueharakevin/ --- 原文出處:https://dev.to/kevin-uehara/solid-the-simple-way-to-understand-47im

JavaScript 系列七:第2課 ── 體驗一下 Reactivity 的效果與便利

## 課程目標 - 體驗一下 Reactivity 的效果與便利 ## 課程內容 來學 Vue 之中 Reactivity 的基本觀念 - https://vuejs.org/guide/essentials/reactivity-fundamentals.html 老樣子,大部份看不懂沒關係,稍微有個印象就好 記得之前的課程,每次更新完 state,你都要接著去執行 `render()` 函式嗎? 所謂 Reactivity 就只是說:當你更新完 application state 之後,Vue 會自動幫你更新 UI 上的內容 就只是這樣,不要把它想得太複雜 ``` <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> <div id="app"> 嗨,我是 {{ user.name }} <button @click="changeNewName"> 改名字 </button> </div> <script> const { createApp } = Vue createApp({ data() { return { user: { name: "John Doe" } } }, mounted() { this.user.name = "Frank" }, methods: { changeNewName() { this.user.name = "Kevin" } } }).mount('#app') </script> ``` 到 jsfiddle 跑跑看,看看畫面上出現什麼 `mounted` 是 vue 將畫面第一次載入之後,可以安排任務的地方,通常會放一些「系統初始化任務」 `@click` 跟 `methods` 那些,就只是操作事件的寫法而已,先照做就好 以上兩點,後面課程會詳談 --- 在 Vue 文件中,你會到處看到 `this` 這個關鍵字 要存取 application state,有時前面要加 `this.` 有時又不用 關於 `this` 關鍵字,它是一個 JavaScript 程式語言裡面過時又晦澀難懂的觀念 我不想細談,你也先不用研究。就先照做、程式能跑就好了 反正改天你真的遇到問題,大概知道要往這方向研究就是了 --- 接著來學 Vue 之中 Computed 屬性的觀念 - https://vuejs.org/guide/essentials/computed.html 老話一句,看不懂沒關係 雖然大家都說用這寫法,效能會比較好、程式碼會比較優雅 但實際上,我自己在好幾個專案,就完全不使用任何 `computed` 功能 一律通通只用 `methods` 來寫程式,根本也不會怎麼樣 --- 在整門系列課程,會常常像這樣,我叫你讀官網文件的很多內容 但我接著又會說,看不懂沒差,其中 XXX 根本沒用,其中 YYY 也沒必要用,只學 ZZZ 就好 這些都是我個人的主觀意見。如果你在網路上找資料,會看到許多工程師,有跟我完全相反的建議 這是軟體工程師圈子的常態,充滿各種主觀喜好、偏見,你要習慣一下 (如果你很好奇我是怎麼做出這些判斷的,請重新閱讀此系列課程,第一篇文章:前言,裡面的最後一段) 你不需要完全同意我的觀點,在研究過程中,如果你有發現比較喜歡的寫法,就直接用沒關係 如同我多次提醒過:只要能解決問題就好,你就隨便寫沒關係 各種做法細微的差異,將來有一天你會慢慢理解 ## 課後作業 承接上一課的作業 這一課來簡單體驗一下 reactivity 的效果就好 也就是簡單更新一下 application state 就好 請加入 `mounted` 函式,在裡面把「第一個記事」的內容更新為「多出門、到處走走、也要多運動」 完成之後,你會發現,你的春節行程安排,看起來積極多了~ 做出以上功能,你就完成這次的課程目標了!