二十三種設計模式

2 工廠方法模式

工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。

  • 在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,并且是通過使用一個共同的接口來指向新創建的對象。

《設計模式》一書中,工廠模式被分為了三種:簡單工廠、工廠方法和抽象工廠。(不過,在書中作者將簡單工廠模式看作是工廠方法模式的一種特例。)

接下來我會介紹三種工廠模式的原理及使用

  • 簡單工廠模式(不屬于GOF的23種經典設計模式)
  • 工廠方法模式
  • 抽象工廠模式

2.1 需求: 模擬發放獎品業務

需求: 為了讓我們的案例更加貼近實際開發, 這里我們來模擬一下互聯網電商中促銷拉新下的業務場景, 新用戶注冊立即參與抽獎活動 ,獎品的種類有: 打折券, 免費優酷會員,小禮品.

2.2 原始開發方式

不考慮設計原則,不使用設計模式的方式進行開發

在不考慮任何代碼的可擴展性的前提下,只為了盡快滿足需求.我們可以這樣去設計這個業務的代碼結構:

1) 實體類

名稱描述
AwardInfo獲獎信息對應實體類
DiscountInfo打折券信息對應實體類
YouKuMember優酷會員對應實體類
SmallGiftInfo小禮品信息對應實體類
DiscountResult打折券操作響應結果封裝類
public class AwardInfo {private String uid; //用戶唯一IDprivate Integer awardType; //獎品類型: 1 打折券 ,2 優酷會員,3 小禮品private String awardNumber; //獎品編號Map<String, String> extMap; //額外信息}public class DiscountInfo {//屬性信息省略......
}public class YouKuMember {//屬性信息省略......
}public class SmallGiftInfo {private String userName;              // 用戶姓名private String userPhone;             // 用戶手機private String orderId;               // 訂單IDprivate String relAddress;            // 收貨地址}public class DiscountResult {private String status; // 狀態碼private String message; // 信息
}

2) 服務層

名稱功能描述
DiscountServiceDiscountResult sendDiscount(String uid,String number)模擬打折券服務
YouKuMemberServicevoid openMember(String bindMobile , String number)模擬贈送優酷會員服務
SmallGiftServiceBoolean giveSmallGift(SmallGiftInfo smallGiftInfo)模擬禮品服務
public class DiscountService {public DiscountResult sendDiscount(String uid, String number){System.out.println("向用戶發放打折券一張: " + uid + " , " + number);return new DiscountResult("200","發放打折券成功");}
}public class YouKuMemberService {public void openMember(String bindMobile , String number){System.out.println("發放優酷會員: " + bindMobile + " , " + number);}
}public class SmallGiftService {public Boolean giveSmallGift(SmallGiftInfo smallGiftInfo){System.out.println("小禮品已發貨,獲獎用戶注意查收! " + JSON.toJSON(smallGiftInfo));return true;}
}

3) 控制層

名稱功能描述
DeliverControllerResponseResult awardToUser(AwardInfo awardInfo)按照類型的不同發放商品
獎品類型: 1 打折券 ,2 優酷會員,3 小禮品
public class DeliverController {/*** 按照類型的不同發放商品*     獎品類型: 1 打折券 ,2 優酷會員,3 小禮品*/public void awardToUser(AwardInfo awardInfo){if(awardInfo.getAwardType() == 1){ //打折券DiscountService discountService = new DiscountService();DiscountResult result = discountService.sendDiscount(awardInfo.getUid(), awardInfo.getAwardNumber());System.out.println("打折券發放成功!"+ JSON.toJSON(result));}else if(awardInfo.getAwardType() == 2){ //優酷會員//獲取用戶手機號String bindMobile = awardInfo.getExtMap().get("phone");//調用serviceYouKuMemberService youKuMemberService = new YouKuMemberService();youKuMemberService.openMember(bindMobile,awardInfo.getAwardNumber());System.out.println("優酷會員發放成功!");}else if(awardInfo.getAwardType() == 3){ /*小禮品封裝收貨用戶信息*/SmallGiftInfo smallGiftInfo = new SmallGiftInfo();smallGiftInfo.setUserName(awardInfo.getExtMap().get("username"));smallGiftInfo.setOrderId(UUID.randomUUID().toString());smallGiftInfo.setRelAddress(awardInfo.getExtMap().get("adderss"));SmallGiftService smallGiftService = new SmallGiftService();Boolean isSuccess = smallGiftService.giveSmallGift(smallGiftInfo);System.out.println("小禮品發放成功!" + isSuccess);}}}

4) 測試

通過單元測試,來對上面的接口進行測試,驗證代碼質量.

public class TestApi01 {//測試發放獎品接口@Testpublic void test01(){DeliverController deliverController = new DeliverController();//1. 發放打折券優惠AwardInfo info1 = new AwardInfo();info1.setUid("1001");info1.setAwardType(1);info1.setAwardNumber("DEL12345");deliverController.awardToUser(info1);//2. 發放優酷會員AwardInfo info2 = new AwardInfo();info2.setUid("1002");info2.setAwardType(2);info2.setAwardNumber("DW12345");Map<String,String> map = new HashMap<>();map.put("phone","13512341234");info2.setExtMap(map);deliverController.awardToUser(info2);//2. 發放小禮品AwardInfo info3 = new AwardInfo();info3.setUid("1003");info3.setAwardType(3);info3.setAwardNumber("SM12345");Map<String,String> map2 = new HashMap<>();map2.put("username","大遠");map2.put("phone","13512341234");map2.put("address","北京天安門");info3.setExtMap(map2);deliverController.awardToUser(info3);}
}

對于上面的實現方式,如果我們有想要添加的新的獎品時,勢必要改動DeliverController的代碼,違反開閉原則.而且如果有的抽獎接口出現問題,那么對其進行重構的成本會非常高.

除此之外代碼中有一組if分支判斷邏輯,現在看起來還可以,但是如果經歷幾次迭代和拓展,后續ifelse肯定還會增加.到時候接手這段代碼的研發將會十分痛苦.

2.3 簡單工廠模式

2.3.1 簡單工廠模式介紹

簡單工廠不是一種設計模式,反而比較像是一種編程習慣。簡單工廠模式又叫做靜態工廠方法模式(static Factory Method pattern),它是通過使用靜態方法接收不同的參數來返回不同的實例對象.

實現方式:

? 定義一個工廠類,根據傳入的參數不同返回不同的實例,被創建的實例具有共同的父類或接口。

適用場景:
  (1)需要創建的對象較少。
  (2)客戶端不關心對象的創建過程。

2.3.2 簡單工廠原理

簡單工廠包含如下角色:

  • 抽象產品 :定義了產品的規范,描述了產品的主要特性和功能。
  • 具體產品 :實現或者繼承抽象產品的子類
  • 具體工廠 :提供了創建產品的方法,調用者通過該方法來獲取產品。

2.3.3 簡單工廠模式重構代碼

1) service

/*** 免費商品發放接口* @author spikeCong* @date 2022/9/8**/
public interface IFreeGoods {ResponseResult sendFreeGoods(AwardInfo awardInfo);}
/*** 模擬打折券服務* @author spikeCong* @date 2022/9/8**/
public class DiscountFreeGoods implements IFreeGoods {@Overridepublic ResponseResult sendFreeGoods(AwardInfo awardInfo) {System.out.println("向用戶發放一張打折券: " + awardInfo.getUid() + " , " + awardInfo.getAwardNumber());return new ResponseResult("200","打折券發放成功!");}
}/*** 小禮品發放服務* @author spikeCong* @date 2022/9/8**/
public class SmallGiftFreeGoods implements IFreeGoods {@Overridepublic ResponseResult sendFreeGoods(AwardInfo awardInfo) {SmallGiftInfo smallGiftInfo = new SmallGiftInfo();smallGiftInfo.setUserPhone(awardInfo.getExtMap().get("phone"));smallGiftInfo.setUserName(awardInfo.getExtMap().get("username"));smallGiftInfo.setAddress(awardInfo.getExtMap().get("address"));smallGiftInfo.setOrderId(UUID.randomUUID().toString());System.out.println("小禮品發放成,請注意查收: " + JSON.toJSON(smallGiftInfo));return new ResponseResult("200","小禮品發送成功",smallGiftInfo);}
}/*** 優酷 會員服務* @author spikeCong* @date 2022/9/8**/
public class YouKuMemberFreeGoods implements IFreeGoods {@Overridepublic ResponseResult sendFreeGoods(AwardInfo awardInfo) {String phone = awardInfo.getExtMap().get("phone");System.out.println("發放優酷會員成功,綁定手機號: " + phone);return new ResponseResult("200","優酷會員發放成功!");}
}

2) factory

/*** 具體工廠: 生成免費商品* @author spikeCong* @date 2022/9/9**/
public class FreeGoodsFactory {public static IFreeGoods getInstance(Integer awardType){IFreeGoods iFreeGoods = null;if(awardType == 1){  //打折券iFreeGoods = new DiscountFreeGoods();}else if(awardType == 2){ //優酷會員iFreeGoods = new YouKuMemberFreeGoods();}else if(awardType == 3){ //小禮品iFreeGoods = new SmallGiftFreeGoods();}return iFreeGoods;}
}

3)controller

public class DeliverController {//發放獎品public ResponseResult awardToUser(AwardInfo awardInfo){try {IFreeGoods freeGoods = FreeGoodsFactory.getInstance(awardInfo.getAwardTypes());ResponseResult responseResult = freeGoods.sendFreeGoods(awardInfo);return responseResult;} catch (Exception e) {e.printStackTrace();return new ResponseResult("201","獎品發放失敗!");}}
}

4) 測試

通過單元測試,來對上面的接口進行測試,驗證代碼質量.

public class TestApi02 {DeliverController deliverController = new DeliverController();@Testpublic void test01(){//1. 發放打折券優惠AwardInfo info1 = new AwardInfo();info1.setUid("1001");info1.setAwardTypes(1);info1.setAwardNumber("DEL12345");ResponseResult result = deliverController.awardToUser(info1);System.out.println(result);}@Testpublic void test02(){//2. 發放優酷會員AwardInfo info2 = new AwardInfo();info2.setUid("1002");info2.setAwardTypes(2);info2.setAwardNumber("DW12345");Map<String,String> map = new HashMap<>();map.put("phone","13512341234");info2.setExtMap(map);ResponseResult result1 = deliverController.awardToUser(info2);System.out.println(result1);}@Testpublic void test03(){//3. 發放小禮品AwardInfo info3 = new AwardInfo();info3.setUid("1003");info3.setAwardTypes(3);info3.setAwardNumber("SM12345");Map<String,String> map2 = new HashMap<>();map2.put("username","大遠");map2.put("phone","13512341234");map2.put("address","北京天安門");info3.setExtMap(map2);ResponseResult result2 = deliverController.awardToUser(info3);System.out.println(result2);}
}
2.3.4 簡單工廠模式總結

優點:

封裝了創建對象的過程,可以通過參數直接獲取對象。把對象的創建和業務邏輯層分開,這樣以后就避免了修改客戶代碼,如果要實現新產品直接修改工廠類,而不需要在原代碼中修改,這樣就降低了客戶代碼修改的可能性,更加容易擴展。

缺點:

增加新產品時還是需要修改工廠類的代碼,違背了“開閉原則”。

2.4 工廠方法模式

2.4.1 工廠方法模式介紹

工廠方法模式 Factory Method pattern,屬于創建型模式.

概念: 定義一個用于創建對象的接口,讓子類決定實例化哪個產品類對象。工廠方法使一個產品類的實例化延遲到其工廠的子類。

2.4.2 工廠方法模式原理

工廠方法模式的目的很簡單,就是封裝對象創建的過程,提升創建對象方法的可復用性

工廠方法模式的主要角色:

  • 抽象工廠:提供了創建產品的接口,調用者通過它訪問具體工廠的工廠方法來創建產品。
  • 具體工廠:主要是實現抽象工廠中的抽象方法,完成具體產品的創建。
  • 抽象產品:定義了產品的規范,描述了產品的主要特性和功能。
  • 具體產品:實現了抽象產品角色所定義的接口,由具體工廠來創建,它同具體工廠之間一一對應。

我們直接來看看工廠方法模式的 UML 圖:

2.4.3 工廠方法模式重構代碼

為了提高代碼擴展性,我們需要將簡單工廠中的if分支邏輯去掉,通過增加抽象工廠(生產工廠的工廠)的方式,讓具體工廠去進行實現,由具體工廠來決定實例化哪一個具體的產品對象.

抽象工廠

public interface FreeGoodsFactory {IFreeGoods getInstance();
}

具體工廠

public class DiscountFreeGoodsFactory implements FreeGoodsFactory {@Overridepublic IFreeGoods getInstance() {return new DiscountFreeGoods();}
}public class SmallGiftFreeGoodsFactory implements FreeGoodsFactory {@Overridepublic IFreeGoods getInstance() {return new SmallGiftFreeGoods();}
}

Controller

public class DeliverController {/*** 按照類型的不同發放商品*/public ResponseResult awardToUser(AwardInfo awardInfo){FreeGoodsFactory freeGoodsFactory = null;if(awardInfo.getAwardType() == 1){freeGoodsFactory = new DiscountFreeGoodsFactory();}else if(awardInfo.getAwardType() == 2){freeGoodsFactory = new SmallGiftFreeGoodsFactory();}IFreeGoods freeGoods = freeGoodsFactory.getInstance();System.out.println("=====工廠方法模式========");ResponseResult result = freeGoods.sendFreeGoods(awardInfo);return result;}}

從上面的代碼實現來看,工廠類對象的創建邏輯又耦合進了 awardToUser() 方法中,跟我們最初的代碼版本非常相似,引入工廠方法非但沒有解決問題,反倒讓設計變得更加復雜了。

那怎么 來解決這個問題呢?

我們可以為工廠類再創建一個簡單工廠,也就是工廠的工廠,用來創建工廠類對象。

/*** 用簡單方法模式實現: 工廠的工廠,作用是不需要每次創建新的工廠對象* @author spikeCong* @date 2022/9/9**/
public class FreeGoodsFactoryMap {private static final Map<Integer,FreeGoodsFactory> cachedFactories = new HashMap<>();static{cachedFactories.put(1, new DiscountFreeGoodsFactory());cachedFactories.put(2, new SmallGiftFreeGoodsFactory());}public static FreeGoodsFactory getParserFactory(Integer type){if(type == 1){FreeGoodsFactory freeGoodsFactory = cachedFactories.get(1);return freeGoodsFactory;}else if(type ==2){FreeGoodsFactory freeGoodsFactory = cachedFactories.get(2);return freeGoodsFactory;}return null;}
}

Controller

/*** 發放獎品接口* @author spikeCong* @date 2022/9/7**/
public class DeliverController {/*** 按照類型的不同發放商品*/public ResponseResult awardToUser(AwardInfo awardInfo){//根據類型獲取工廠FreeGoodsFactory goodsFactory = FreeGoodsFactoryMap.getParserFactory(awardInfo.getAwardType());//從工廠中獲取對應實例IFreeGoods freeGoods = goodsFactory.getInstance();System.out.println("=====工廠方法模式========");ResponseResult result = freeGoods.sendFreeGoods(awardInfo);return result;}
}

現在我們的代碼已經基本上符合了開閉原則,當有新增的產品時,我們需要做的事情包括:

  1. 創建新的產品類,并且讓該產品實現抽象產品接口
  2. 創建產品類對應的具體工廠,并讓具體工廠實現抽象工廠
  3. 將新的具體工廠對象,添加到FreeGoodsFactoryMap的cachedFactories中即可,需要改動的代碼改動的非常少.
2.4.4 工廠方法模式總結

工廠方法模優缺點

優點:

  • 用戶只需要知道具體工廠的名稱就可得到所要的產品,無須知道產品的具體創建過程;
  • 在系統增加新的產品時只需要添加具體產品類和對應的具體工廠類,無須對原工廠進行任何修改,滿足開閉原則;

缺點:

  • 每增加一個產品就要增加一個具體產品類和一個對應的具體工廠類,這增加了系統的復雜度。

什么時候使用工廠方法模式

  • 需要使用很多重復代碼創建對象時,比如,DAO 層的數據對象、API 層的 VO 對象等。
  • 創建對象要訪問外部信息或資源時,比如,讀取數據庫字段,獲取訪問授權 token 信息,配置文件等。
  • 創建需要統一管理生命周期的對象時,比如,會話信息、用戶網頁瀏覽軌跡對象等。
  • 創建池化對象時,比如,連接池對象、線程池對象、日志對象等。這些對象的特性是:有限、可重用,使用工廠方法模式可以有效節約資源。
  • 希望隱藏對象的真實類型時,比如,不希望使用者知道對象的真實構造函數參數等。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/70936.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/70936.shtml
英文地址,請注明出處:http://en.pswp.cn/web/70936.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于C語言對CAPL語法基礎的理解

.CAPL是基于C語言開發的&#xff0c;專門用于CANalyzer和CANoe工具環境&#xff0c;但是CAPL簡化了C語言&#xff0c;移除了復雜的指針概念&#xff0c;和一些不常用的關鍵字。 2.CAPL 腳本是基于事件驅動的&#xff0c;任何事件都有可能觸發CAPL腳本的執行&#xff0c;比如&a…

【Java SE】Java中String的內存原理

參考筆記&#xff1a; Java String 類深度解析&#xff1a;內存模型、常量池與核心機制_java stringx、-CSDN博客 解析java中String的內存原理_string s1 new string("ab");內存分析-CSDN博客 目錄 1.String初識 2.字符串字面量 3.內存原理圖 4. 示例驗證 4.…

Prometheus + Grafana 監控

Prometheus Grafana 監控 官網介紹&#xff1a;Prometheus 是一個開源系統 監控和警報工具包最初由 SoundCloud 構建。自 2012 年成立以來&#xff0c;許多 公司和組織已經采用了 Prometheus&#xff0c;并且該項目具有非常 活躍的開發人員和用戶社區。它現在是一個獨立的開源…

【Python爬蟲(95)】Python爬蟲進階:構建大型垂直領域爬蟲系統

【Python爬蟲】專欄簡介:本專欄是 Python 爬蟲領域的集大成之作,共 100 章節。從 Python 基礎語法、爬蟲入門知識講起,深入探討反爬蟲、多線程、分布式等進階技術。以大量實例為支撐,覆蓋網頁、圖片、音頻等各類數據爬取,還涉及數據處理與分析。無論是新手小白還是進階開發…

Node.js定義以及性能優化

Node.js Node.js 是一個基于 Chrome V8 引擎的 JavaScript 運行時&#xff0c;廣泛用于構建高性能的網絡應用。以下是一些常見的 Node.js 面試題及其解答&#xff0c;幫助你準備面試&#xff1a; 1. 什么是 Node.js&#xff1f; Node.js 是一個基于 Chrome V8 引擎的 JavaSc…

開源|Documind協同文檔(接入deepseek-r1、支持實時聊天)

Documind &#x1f680; 項目介紹 Documind 一個支持實時聊天和接入deepseek-r1模型AI助手的協同文檔編輯項目 前端&#xff1a;NextJS React TailwindCSS ShadcnUl Tiptap Zustand后端&#xff1a;NextJS Convex Liveblocks Clerk項目預覽&#xff1a;Documind 預覽…

JVM內存模型詳解:各個區域的作用與原理

引言 Java虛擬機&#xff08;JVM&#xff09;是Java程序運行的核心環境&#xff0c;它負責管理程序的內存、執行字節碼以及提供跨平臺的支持。理解JVM的內存模型對于編寫高效、穩定的Java程序至關重要。本文將詳細介紹JVM的內存模型&#xff0c;并深入探討各個內存區域的作用和…

機器學習之集成學習思維導圖

學習筆記—機器學習-集成學習思維導圖 20250227&#xff0c;以后復習看&#xff08;周老師的集成學習&#xff09; PS&#xff1a;圖片看不清&#xff0c;可以下載下來看。 往期思維導圖&#xff1a; 機器學習之集成學習Bagging&#xff08;隨機深林、VR-樹、極端隨機樹&…

【http://noi.openjudge.cn/】4.3算法之圖論——1538:Gopher II

[【http://noi.openjudge.cn/】4.3算法之圖論——1538:Gopher II] 題目 查看提交統計提問 總時間限制: 2000ms 內存限制: 65536kB 描述 The gopher family, having averted the canine threat, must face a new predator. The are n gophers and m gopher holes, each at di…

Apache Spark中的依賴關系與任務調度機制解析

Apache Spark中的依賴關系與任務調度機制解析 在Spark的分布式計算框架中,RDD(彈性分布式數據集)的依賴關系是理解任務調度、性能優化及容錯機制的關鍵。寬依賴(Wide Dependency)與窄依賴(Narrow Dependency)作為兩種核心依賴類型,直接影響Stage劃分、Shuffle操作及容…

【計算機網絡】TCP協議相關總結,TCP可靠性的生動講解

TCP 可靠性 確保快遞不丟、不亂、不過載 機制作用&#xff08;快遞類比&#xff09;防止的問題檢驗和檢查包裹是否損壞&#xff0c;損壞就重新發數據出錯序列號給每個包裹編號&#xff0c;按順序整理亂序、重復確認應答每送到一件&#xff0c;就讓收件人簽收丟失滑動窗口控制…

Go基于協程池的延遲任務調度器

原理 通過用一個goroutine以及堆來存儲要待調度的延遲任務&#xff0c;當達到調度時間后&#xff0c;將其添加到協程池中去執行。 主要是使用了chan、Mutex、atomic及ants協程池來實現。 用途 主要是用于高并發及大量定時任務要處理的情況&#xff0c;如果使用Go協程來實現每…

杰發科技AC7801——滴答定時器獲取時間戳

1. 滴答定時器 杰發科技7801內部有一個滴答定時器&#xff0c;該定時器是M0核自帶的&#xff0c;因此可以直接用該定時器來獲取時間戳。 同樣&#xff0c;7803也可以使用該方式獲取時間戳。 2. 滴答定時器原理 SysTick是一個24位的遞減計數器&#xff0c;它從預設的重裝載值…

湖倉一體概述

湖倉一體之前&#xff0c;數據分析經歷了數據庫、數據倉庫和數據湖分析三個時代。 首先是數據庫&#xff0c;它是一個最基礎的概念&#xff0c;主要負責聯機事務處理&#xff0c;也提供基本的數據分析能力。 隨著數據量的增長&#xff0c;出現了數據倉庫&#xff0c;它存儲的是…

第十五屆藍橋杯單片機組4T模擬賽三(第二套)

本套試題在4T平臺中的名字為第15屆藍橋杯單片機組模擬考試三&#xff0c;不知道哪套是4T的模擬賽&#xff0c;所以兩套都敲一遍練練手感。 為了代碼呈現美觀&#xff0c;本文章前面的各個模塊在main函數中的處理函數均未添加退出處理&#xff0c;在最后給出的完整代碼中體現。 …

CT技術變遷史——CT是如何誕生的?

第一代CT(平移-旋轉) X線球管為固定陽極,發射X線為直線筆形束,一個探測器,采用直線和旋轉掃描相結合,即直線掃描后,旋轉1次,再行直線掃描,旋轉180完成一層面掃描,掃描時間3~6分鐘。矩陣象素256256或320320。僅用于顱腦檢查。 第二代CT (平移-旋轉) 與第一代無質…

Virtual Box虛擬機安裝蘋果Monterey和big sur版本實踐

虛擬機安裝蘋果實踐&#xff0c;在Windows10系統&#xff0c;安裝Virtual Box7.1.6&#xff0c;安裝虛擬蘋果Monterey版本Monterey (macOS 12) 。碰到的主要問題是安裝光盤不像Windows那么容易拿到&#xff0c;而且根據網上很多文章制作的光盤&#xff0c;在viritualBox里都無法…

dify基礎之prompts

摘要&#xff1a;在大型語言模型&#xff08;LLM&#xff09;應用中&#xff0c;Prompt&#xff08;提示詞&#xff09;是連接用戶意圖與模型輸出的核心工具。本文從概念、組成、設計原則到實踐案例&#xff0c;系統講解如何通過Prompt解鎖LLM的潛能&#xff0c;提升生成內容的…

【學寫LibreCAD】0 仿寫LibreCAD簡介

一、LibreCAD 核心模塊&#xff1a; 核心模塊&#xff08;Core&#xff09; 功能&#xff1a;處理 CAD 的核心邏輯&#xff0c;如幾何計算、圖形對象管理、坐標系轉換等。關鍵組件&#xff1a; 圖形對象&#xff1a;如直線、圓、圓弧、多段線等。數學工具&#xff1a;向量、矩…

HTML元素,標簽到底指的哪塊部分?單雙標簽何時使用?

1. 標簽&#xff08;Tag&#xff09; vs 元素&#xff08;Element&#xff09; 標簽&#xff08;Tag&#xff09; 標簽是 HTML 中用于定義元素的符號&#xff0c;用尖括號 < > 包裹。例如 <img> 是標簽。元素&#xff08;Element&#xff09; 元素是由 標簽 內容…