<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          設計模式:面向?qū)ο蟮幕A知識

          共 3203字,需瀏覽 7分鐘

           ·

          2021-11-15 11:24

          主流的編程范式有三種:面向過程、面向?qū)ο蠛秃瘮?shù)式編程,我們現(xiàn)在使用的主流編程語言 C# 或 Java,都是面向?qū)ο笳Z言,所以常常說的設計模式也是在面向?qū)ο笳Z言這個前提之下。

          面向?qū)ο蟮幕A知識和一些設計原則,我認為是學習設計模式的基礎,本文就聊下這些基礎知識。

          在面試時,一問到面向?qū)ο螅瑤缀趺總€人都能脫口而出:封裝、繼承、多態(tài)。但大部分只能說出一個簡單的概念,而多態(tài)還有很多連概念都說不清楚。我們學習面向?qū)ο螅恢剐枰私飧拍睿枰烂總€特性存在的意義和目的。

          對于面向?qū)ο蟮奶匦裕嫦驅(qū)ο蟮恼Z言都會給出相應的支持,不同語言可能會有細微差別,下面的示例以 C# 語言為主。

          封裝

          我們先來思考下,平時寫代碼時有哪些是屬于封裝,是不是會有下面的一些場景:

          1、將一些屬性字段放到一個類中;

          2、將一些方法放到一個類中

          3、將某些類組織到某個特定的命名空間下。

          在 C# 9.0 版本中還提供了屬性的 init 特性,可以更方便地提供封裝性:

          public?class?UserInfo
          {
          ????public?string?Name?{?get;?init;?}
          }
          UserInfo?user?=?new?UserInfo?{?Name?=?"oec2003"?};
          //當?user?初始化完了之后就不能再改變?Name?的值
          user.Name?=?"oec2004";

          除了屬性、方法和類也有對應的訪問修飾符,這些訪問修飾符的靈活運用就達到了封裝的目的,用來隱藏信息或進行數(shù)據(jù)的保護。

          試想一下,如果我們對類中屬性或方法全部都使用 public ,調(diào)用方可以任意修改屬性和調(diào)用方法,這樣會使代碼變得不可控,屬性可能被很多地方以不同的方式進行修改,代碼難以維護。而且不熟悉業(yè)務的開發(fā)人員如果隨意改動了一些關(guān)鍵屬性,可能引發(fā)嚴重的問題。

          從另一個方面來說,類的共有屬性和方法暴露的越多,對于調(diào)用者來說就會越復雜,越容易出現(xiàn)問題,合理地進行封裝,可以提高可讀性、可維護性,減少出錯。

          這時,你是不是可以想想,平時寫代碼時,屬性、方法、類是不是都不假思索地寫上了 public 了呢?

          繼承

          目前面向?qū)ο蟮恼Z言基本都支持繼承特性,只是語法上有些細微的差別,比如 C# 語言是使用冒號,Java 語言使用 extends 關(guān)鍵字。但都是表示 is-a 的關(guān)系。

          在 C# 中一個類可以繼承多個接口,但只能繼承一個父類,我們通常說的 C# 只支持單繼承指的就是 C# 只能繼承一個父類,但在 C++ 、Python 等語言中類是可以繼承多個父類的。

          我們經(jīng)常會跟開發(fā)人員講,不要到處復制代碼,代碼要做到能夠復用,發(fā)現(xiàn)同一個邏輯在兩個不同的類中的時候,可以抽象出來一個父類,讓這兩個類都繼承這個父類。這個思路沒有問題,也確實能解決我們的實際問題,提升代碼的復用性。

          但隨著功能的增加,我們需要對類的屬性和方法進行擴展,會發(fā)現(xiàn)需要新添加的屬性或方法放在父類或子類都不合適,只能繼續(xù)進行抽象,長此下去,繼承關(guān)系會變得非常復雜,變得難以維護。有條設計原則是這么說的:組合優(yōu)于繼承,其實就是為了解決這個問題。

          組合和繼承的選擇是一種權(quán)衡,當涉及的類經(jīng)常變化可能導致繼承層級向著復雜化演化時,需要考慮采用組合的方式,如果相關(guān)類比較穩(wěn)定,繼承層級不深(一般不超過 3 層),就可以放心使用繼承。

          在具體的模式中,組合模式、策略模式等就是使用組合的方式實現(xiàn),模板模式使用的是繼承方式實現(xiàn)。

          多態(tài)

          多態(tài)的字面意思就是同樣的一個語法調(diào)用,能夠表達多個不同的意思。如果說繼承的最大好處是復用,那么多態(tài)的好處就是方便擴展。

          在 C# 語言中兩個比較典型的多態(tài)場景就是方法的重寫和方法的重載:

          • 重寫:存在繼承關(guān)系的類或接口,在子類中對父類的方法進行重新構(gòu)建邏輯,但調(diào)用方法、參數(shù)、返回值保持一致,通常有下面幾種情況:
            • 普通的父類中有用 virtual 關(guān)鍵字標識的虛方法,在子類中使用 override 關(guān)鍵字進行重寫;
            • 子類對抽象類的抽象方法進行重寫;
            • 子類對接口中的方法進行實現(xiàn)。
          • 重載:類中的多個方法,方法名相同,但參數(shù)個數(shù)或類型不相同,稱之為重載方法。例如 C# 中的 File 類的 Open 方法就有三個重載,如下圖:

          方法的重寫,在實際應用中非常常見,比如零代碼平臺中的消息組件會有多種發(fā)送消息的方式,下面用一個示例代碼演示下:

          public?interface?IMessage
          {
          ????void?Send(string?msg);
          }

          public?class?EmailMessage?:?IMessage
          {
          ????public?void?Send(string?msg)
          ????{
          ????????Console.WriteLine($"send?email?message?{msg}");
          ????}
          }
          public?class?WechatMessage?:?IMessage
          {
          ????public?void?Send(string?msg)
          ????{
          ????????Console.WriteLine($"send?wechat?message?{msg}");
          ????}
          }
          class?Program
          {
          ????static?void?Main(string[]?args)
          ????{
          ????????List?messageList?=?new?List();
          ????????messageList.Add(new?EmailMessage());
          ????????messageList.Add(new?WechatMessage());
          ????????
          ????????messageList.ForEach(s=>s.Send("test?message"));
          ????}
          }

          為什么說能提高擴展性呢?如果這時消息組件需要擴展發(fā)送短信的消息種類,只需要編寫短信類型的消息類實現(xiàn) IMessage 接口的 Send 方法即可。

          還有一種場景,比如登陸的時候,有基于用戶名密碼的認證、企業(yè)微信的認證、釘釘?shù)恼J證、和對接第三方的認證,又應該怎么設計呢?

          我們雖然都在使用著面向?qū)ο蟮恼Z言,但很多的時候思維還是面向過程的,具體體現(xiàn)在:

          • 實體類的屬性直接定義為 public ,set 和 get 都安排上,外部可以任意獲取和賦值,很多時候使用代碼生成工具直接生成實體類,默認的 set 和 get 都是 public ,也沒有依據(jù)具體的業(yè)務進行修改,嚴重破壞了封裝特性;
          • 數(shù)據(jù)和行為的分離,也就是所謂的貧血模式,但真正的對象是數(shù)據(jù)和行為在一起的,我們可能每天都在寫這樣的代碼,一種面向過程式的代碼;
          • 為了代碼復用,代碼中會存在大量的 Helper 類或者 Utils、Common 類,這些類通常是靜態(tài)類,里面有各種各樣的靜態(tài)方法,在往里面添加方法時需要思考下,真的必須放到這里嗎?這種類隨著時間的推移很容易變成巨型類,變得難以維護;
          • 按照功能驅(qū)動,比如頁面上的一個按鈕操作,對應了一個 API 接口,不管你的代碼是如何設計和分層,都是一層層往下直到數(shù)據(jù)庫訪問。

          所以不要以為使用了面向?qū)ο蟮恼Z言就是在使用面向?qū)ο缶幊蹋匾氖浅橄蟮乃季S,這種抽象需要我們?nèi)ニ伎迹トP考慮,相比較面向過程顯得更難,所以懶惰的程序員更容易寫出面向過程的代碼。

          面向?qū)ο蟮幕A知識是學習設計模式的根基,掌握基礎知識,然后愿意去思考,總結(jié)才能夠?qū)W習好設計模式,并將其應用到實際的工作中。下一篇將介紹面向?qū)ο笾械某S迷O計原則,設計模式也都是基于這些設計原則演化而來。

          希望本文對您有所幫助!


          瀏覽 68
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91视频色 | 亚洲欧洲日本不卡视频在线观看 | 亚洲系列日韩 | 7799精品视频天天看 | 色逼逼综合网 |