設計模式 | 適配器模式

適配器模式(Adapter Pattern)?是結構型設計模式中的連接器大師,它允許不兼容接口的類能夠協同工作。本文將深入探索適配器模式的核心思想、實現技巧以及在C++中的高效實踐,解決現實開發中的接口兼容性問題。

為什么需要適配器模式

在軟件開發中,我們經常遇到接口不兼容的情況:

  • 遺留系統與現代框架的集成

  • 第三方庫與自有系統的對接

  • 不同子系統之間的數據交換

  • API升級導致的接口變更

直接修改已有代碼通常不可行:

  • 第三方庫無法修改

  • 舊系統代碼難以重構

  • 新老系統需要并行運行

  • 修改可能引入新錯誤

適配器模式通過創建中間轉換層解決了這些問題,成為系統集成的關鍵橋梁。

適配器模式的核心概念

模式結構解析

[客戶端] --> [目標接口]↑[適配器] ---> [被適配者]

關鍵角色定義

  1. 目標接口(Target)

    • 客戶端期望的接口

  2. 被適配者(Adaptee)

    • 需要適配的現有組件

  3. 適配器(Adapter)

    • 轉換接口的中間件

    • 實現目標接口

    • 包裝被適配者

C++實現:電源適配器系統

讓我們通過電源適配器系統展示適配器模式的實際應用:

#include <iostream>
#include <memory>
#include <cmath>
#include <iomanip>// ================ 目標接口:現代電源標準 ================
class ModernPowerOutlet {
public:virtual ~ModernPowerOutlet() = default;// 輸出標準USB-C電源virtual void supplyPower() const = 0;// 獲取電源規格virtual void getSpecs() const {std::cout << "標準: USB-C PD 3.1\n";std::cout << "電壓: 5V-48V\n";std::cout << "功率: 最高240W\n";}
};// ================ 被適配者:舊式電源接口 ================
class LegacyPowerOutlet {
public:// 舊式交流電源輸出void outputACPower(int voltage, int current) const {std::cout << "輸出交流電: " << voltage << "V, " << current << "A\n";}// 獲取舊式電源規格void showLegacySpecs() const {std::cout << "=== 舊式電源規格 ===\n";std::cout << "類型: 交流電\n";std::cout << "電壓: 110V/220V\n";std::cout << "頻率: 50/60Hz\n";}
};// ================ 類適配器(多重繼承) ================
class PowerClassAdapter : public ModernPowerOutlet, private LegacyPowerOutlet {
public:void supplyPower() const override {std::cout << "轉換交流到直流...\n";// 調用被適配者的方法outputACPower(220, 5); std::cout << "輸出: USB-C 20V/5A (100W)\n";}void getSpecs() const override {ModernPowerOutlet::getSpecs();std::cout << "適配器類型: 類適配器\n";showLegacySpecs();}
};// ================ 對象適配器(組合方式) ================
class PowerObjectAdapter : public ModernPowerOutlet {
public:// 通過構造函數注入被適配對象explicit PowerObjectAdapter(LegacyPowerOutlet* outlet) : legacyOutlet_(outlet) {}void supplyPower() const override {std::cout << "轉換交流到直流...\n";// 調用被適配者的方法legacyOutlet_->outputACPower(220, 5);std::cout << "輸出: USB-C 20V/5A (100W)\n";}void getSpecs() const override {ModernPowerOutlet::getSpecs();std::cout << "適配器類型: 對象適配器\n";legacyOutlet_->showLegacySpecs();}private:LegacyPowerOutlet* legacyOutlet_;
};// ================ 客戶端代碼 ================
void chargeDevice(ModernPowerOutlet* outlet) {std::cout << "\n=== 開始充電 ===\n";outlet->getSpecs();outlet->supplyPower();std::cout << "設備充電中...\n";std::cout << "=== 充電完成 ===\n";
}int main() {// 創建被適配對象LegacyPowerOutlet legacyOutlet;// 使用類適配器std::cout << "===== 使用類適配器 =====\n";PowerClassAdapter classAdapter;chargeDevice(&classAdapter);// 使用對象適配器std::cout << "\n\n===== 使用對象適配器 =====\n";PowerObjectAdapter objectAdapter(&legacyOutlet);chargeDevice(&objectAdapter);return 0;
}

適配器模式的兩種實現方式

1. 類適配器(繼承方式)

class ClassAdapter : public Target, private Adaptee {
public:void request() override {// 轉換并調用被適配者的方法specificRequest();}
};

特點

  • 使用多重繼承

  • 適配器繼承目標接口和被適配者

  • 靜態綁定,編譯時確定

  • 可能違反單一職責原則

2. 對象適配器(組合方式)

class ObjectAdapter : public Target {
public:ObjectAdapter(Adaptee* adaptee) : adaptee_(adaptee) {}void request() override {// 轉換并調用被適配者的方法adaptee_->specificRequest();}private:Adaptee* adaptee_;
};

特點

  • 使用對象組合

  • 更靈活,支持運行時適配

  • 符合合成復用原則

  • 可以適配多個對象

  • 現代C++更推薦的方式

適配器模式的高級應用

1. 雙向適配器

class BiDirectionalAdapter : public NewInterface, public OldInterface {
public:BiDirectionalAdapter(OldSystem* old, NewSystem* newSys) : oldSystem_(old), newSystem_(newSys) {}// 實現新接口void newMethod() override {oldSystem_->legacyMethod();}// 實現舊接口void legacyMethod() override {newSystem_->newMethod();}private:OldSystem* oldSystem_;NewSystem* newSystem_;
};

2. 參數適配器

class FunctionAdapter {
public:using NewSignature = void(int, double);explicit FunctionAdapter(std::function<void(std::string, float)> oldFunc): oldFunc_(oldFunc) {}void operator()(int a, double b) {// 轉換參數并調用舊函數oldFunc_(std::to_string(a), static_cast<float>(b));}private:std::function<void(std::string, float)> oldFunc_;
};// 使用
void legacyPrint(std::string s, float f) {std::cout << s << " : " << f << "\n";
}int main() {FunctionAdapter adapter(legacyPrint);adapter(42, 3.14); // 自動轉換參數類型
}

3. STL中的適配器

#include <vector>
#include <stack>
#include <queue>
#include <functional>// 容器適配器示例
std::stack<int> s;              // deque適配為棧
std::queue<double> q;           // deque適配為隊列
std::priority_queue<int> pq;    // vector適配為優先隊列// 函數對象適配器
auto negate = std::negate<int>();
auto plus10 = std::bind(std::plus<int>(), std::placeholders::_1, 10);
auto isEven = [](int x) { return x % 2 == 0; };
auto isOdd = std::not1(std::function<bool(int)>(isEven));

適配器模式的現實應用場景

1. 圖形渲染接口適配

// OpenGL接口
class OpenGLRenderer {
public:void glClearColor(float r, float g, float b, float a) {std::cout << "OpenGL: 設置清除顏色 (" << r << ", " << g << ", " << b << ", " << a << ")\n";}// 其他OpenGL方法...
};// Vulkan接口適配器
class VulkanToOpenGLAdapter : public OpenGLRenderer {
public:void vkCmdClearColor(float r, float g, float b, float a) {// 將Vulkan命令轉換為OpenGL調用glClearColor(r, g, b, a);}// 其他適配方法...
};

2. 支付系統集成

// 統一支付接口
class PaymentProcessor {
public:virtual void processPayment(double amount, const std::string& currency) = 0;
};// PayPal適配器
class PayPalAdapter : public PaymentProcessor {
public:PayPalAdapter(PayPalService* paypal) : paypal_(paypal) {}void processPayment(double amount, const std::string& currency) override {// 轉換到PayPal要求的格式paypal_->sendPayment(amount * 100, currency + "_MICRO");}private:PayPalService* paypal_;
};// Stripe適配器
class StripeAdapter : public PaymentProcessor {
public:void processPayment(double amount, const std::string& currency) override {stripe::Charge::create({{"amount", static_cast<int>(amount * 100)},{"currency", currency}});}
};

3. 傳感器數據標準化

// 統一傳感器接口
class Sensor {
public:virtual double read() const = 0;virtual std::string getUnit() const = 0;
};// 溫度傳感器適配器
class TemperatureAdapter : public Sensor {
public:TemperatureAdapter(LegacyThermometer* thermo) : thermo_(thermo) {}double read() const override {// 華氏度轉攝氏度return (thermo_->getFahrenheit() - 32) * 5/9;}std::string getUnit() const override {return "°C";}private:LegacyThermometer* thermo_;
};// 壓力傳感器適配器
class PressureAdapter : public Sensor {
public:PressureAdapter(BarometricSensor* sensor) : sensor_(sensor) {}double read() const override {// mmHg轉hPareturn sensor_->getmmHg() * 1.33322;}std::string getUnit() const override {return "hPa";}private:BarometricSensor* sensor_;
};

適配器模式的五大優勢

  1. 接口兼容性

    // 舊接口
    void legacyPrint(int x, float y);// 新接口適配器
    class PrinterAdapter {
    public:void print(double a, double b) {legacyPrint(static_cast<int>(a), static_cast<float>(b));}
    };
  2. 代碼復用

    // 復用現有實現
    class NewServiceAdapter : public NewServiceInterface {
    public:NewServiceAdapter(OldService* service) : service_(service) {}void newMethod() override {service_->oldMethod(); // 復用舊實現}
    };
  3. 解耦系統

    // 客戶端只依賴抽象接口
    void clientCode(DataProcessor* processor) {processor->process(data);
    }// 適配不同實現
    clientCode(new XMLProcessorAdapter(xmlParser));
    clientCode(new JSONProcessorAdapter(jsonParser));
  4. 增量遷移

    // 逐步替換舊系統
    class HybridSystem : public NewSystem {
    public:HybridSystem(OldSystem* old) : oldAdapter_(old) {}void newOperation() override {if (useOldSystem) {oldAdapter_.legacyOperation();} else {NewSystem::newOperation();}}
    };
  5. 多平臺支持

    // 跨平臺文件系統適配
    class FileSystemAdapter {
    public:#ifdef _WIN32WindowsFileSys winFS;#elif __APPLE__MacFileSys macFS;#elif __linux__LinuxFileSys linuxFS;#endifstd::string readFile(const std::string& path) {#ifdef _WIN32return winFS.readWinFile(path);#elif __APPLE__return macFS.readMacFile(path);// ...#endif}
    };

適配器模式的最佳實踐

1. 優先使用對象適配器

// 更靈活,支持運行時配置
class FlexibleAdapter : public Target {
public:FlexibleAdapter(Adaptee* adaptee) : adaptee_(adaptee) {}// 實現目標接口...private:Adaptee* adaptee_; // 組合優于繼承
};

2. 使用智能指針管理資源

class SafeAdapter : public Target {
public:SafeAdapter(std::shared_ptr<Adaptee> adaptee) : adaptee_(std::move(adaptee)) {}// 實現目標接口...private:std::shared_ptr<Adaptee> adaptee_;
};// 使用
auto adaptee = std::make_shared<LegacyComponent>();
SafeAdapter adapter(adaptee);

3. 保持適配器輕量級

// 只包含必要的轉換邏輯
class MinimalAdapter : public NewInterface {
public:MinimalAdapter(OldComponent* comp) : comp_(comp) {}void newMethod() override {// 只做必要轉換comp_->oldMethod();}// 不實現不需要的方法
};

4. 適配器命名規范

// 清晰表達適配關系
class LegacyToModernAdapter   // 舊系統到新系統
class XMLToJSONAdapter       // XML到JSON轉換
class FahrenheitToCelsiusAdapter // 溫度單位轉換
class PayPalPaymentAdapter   // PayPal支付適配

適配器模式與其他模式的關系

模式關系區別
裝飾器模式都使用包裝裝飾器添加功能,適配器轉換接口
外觀模式都簡化接口外觀定義新接口,適配器復用現有接口
橋接模式都解耦抽象與實現橋接預先設計,適配器事后補救
代理模式都使用間接訪問代理控制訪問,適配器轉換接口

組合使用示例

// 適配器 + 工廠模式
class PaymentAdapterFactory {
public:static PaymentProcessor* createAdapter(PaymentType type) {switch (type) {case PAYPAL: return new PayPalAdapter(new PayPalService);case STRIPE: return new StripeAdapter;case CRYPTO: return new CryptoPaymentAdapter;default: throw std::invalid_argument("不支持的支付類型");}}
};// 客戶端代碼
auto processor = PaymentAdapterFactory::createAdapter(PAYPAL);
processor->processPayment(99.99, "USD");

應用案例

1. 數據庫訪問層適配

// 統一數據庫接口
class Database {
public:virtual void execute(const std::string& query) = 0;
};// MySQL適配器
class MySQLAdapter : public Database {
public:MySQLAdapter(MySQLConn* conn) : conn_(conn) {}void execute(const std::string& query) override {conn_->mysql_exec(query.c_str());}
};// PostgreSQL適配器
class PostgresAdapter : public Database {
public:void execute(const std::string& query) override {PGresult* res = PQexec(conn_, query.c_str());// 處理結果...}
};// SQLite適配器
class SQLiteAdapter : public Database {
public:void execute(const std::string& query) override {sqlite3_exec(db_, query.c_str(), nullptr, nullptr, nullptr);}
};

2. 日志系統集成

// 統一日志接口
class Logger {
public:virtual void log(LogLevel level, const std::string& message) = 0;
};// Log4cpp適配器
class Log4cppAdapter : public Logger {
public:Log4cppAdapter(log4cpp::Category* cat) : category_(cat) {}void log(LogLevel level, const std::string& msg) override {switch (level) {case DEBUG: category_->debug(msg); break;case INFO: category_->info(msg); break;case WARN: category_->warn(msg); break;case ERROR: category_->error(msg); break;}}
};// Boost.Log適配器
class BoostLogAdapter : public Logger {
public:void log(LogLevel level, const std::string& msg) override {switch (level) {case DEBUG: BOOST_LOG_TRIVIAL(debug) << msg; break;case INFO: BOOST_LOG_TRIVIAL(info) << msg; break;case WARN: BOOST_LOG_TRIVIAL(warning) << msg; break;case ERROR: BOOST_LOG_TRIVIAL(error) << msg; break;}}
};

3. 跨平臺UI框架

// 統一UI組件接口
class UIButton {
public:virtual void render(int x, int y, int width, int height) = 0;
};// Windows按鈕適配器
class WinButtonAdapter : public UIButton {
public:void render(int x, int y, int w, int h) override {HWND button = CreateWindow("BUTTON", "Click", WS_VISIBLE | WS_CHILD,x, y, w, h, parent, NULL, NULL, NULL);}
};// macOS按鈕適配器
class MacButtonAdapter : public UIButton {
public:void render(int x, int y, int w, int h) override {NSButton* button = [[NSButton alloc] initWithFrame:NSMakeRect(x, y, w, h)];[button setTitle:@"Click"];[parent addSubview:button];}
};// Linux按鈕適配器
class LinuxButtonAdapter : public UIButton {
public:void render(int x, int y, int w, int h) override {GtkWidget* button = gtk_button_new_with_label("Click");gtk_fixed_put(GTK_FIXED(container), button, x, y);gtk_widget_set_size_request(button, w, h);}
};

適配器模式的挑戰與解決方案

挑戰解決方案
接口差異過大使用雙向適配器或中間抽象層
適配器過多導致混亂使用工廠模式管理適配器創建
性能開銷優化轉換邏輯,緩存常用結果
鏈式適配問題避免多層嵌套,使用組合適配器

性能優化示例

class CachingAdapter : public Target {
public:void request() override {if (!cached) {result = adaptee_->computeExpensiveOperation();cached = true;}// 使用緩存結果processResult(result);}
};

總結

適配器模式是解決接口不兼容問題的終極武器,它通過:

  1. 無縫集成:連接不兼容的接口

  2. 代碼復用:利用現有實現

  3. 解耦系統:減少組件間依賴

  4. 增量演進:支持系統逐步遷移

  5. 標準化接口:統一多樣化實現

使用時機

  • 需要使用現有類但其接口不符合需求

  • 需要創建可復用的類與不相關類協同工作

  • 需要集成多個第三方庫提供統一接口

  • 舊系統遷移過程中需要與新系統共存

"適配器模式不是修改齒輪的齒形,而是在齒輪間添加傳動裝置。它是面向對象設計中解決接口兼容問題的優雅方案。" - 設計模式實踐者

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

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

相關文章

RTL 級機器人電機控制器的 FPGA 設計

借助Verilog&#xff0c;在FPGA中實現了帶編碼器的兩臺電機的電機控制系統的RTL級設計。 介紹 借助硬件描述語言 (HDL) Verilog 和 AMD Vivado 設計套件&#xff0c;在 AMD Spartan-7 FPGA 中實現帶編碼器的兩個電機的控制器系統的 RTL 設計。 在這個項目中&#xff0c;使用了搭…

4_Flink CEP

Flink CEP 1、何為CEP&#xff1f; CEP&#xff0c;全稱為復雜事件處理&#xff08;Complex Event Processing&#xff09;&#xff0c;是一種用于實時監測和分析數據流的技術。 CEP詳細講解&#xff1a; CEP是基于動態環境的事件流的分析技術&#xff0c;事件是狀態變化&am…

容器基礎知識2-K8s 和 Docker 的關系與管理邏輯詳解

K8s 和 Docker 的關系與管理邏輯詳解 一、先搞懂&#xff1a;Docker 和 K8s 分別是做什么的&#xff1f; Docker&#xff08;容器工具&#xff09;&#xff1a;好比「集裝箱工廠」&#xff0c;負責把應用和依賴打包成標準化容器&#xff08;類似集裝箱&#xff09;&#xff0…

QT MaintenanceTool 登錄無法找到 QtAccount 憑據

親測有效&#xff1a;QT6 Maintenance Tool 登錄問題_qt6 maintenancetool-CSDN博客 將ini這個配置文件移出文件夾后&#xff0c;在切換自己賬戶登錄即可

華為云Flexus+DeepSeek征文|利用華為云一鍵部署 Dify 平臺并接入 DeepSeek 大模型,構建長篇文章生成助手

目錄 前言 1 華為云一鍵部署 Dify 平臺 1.1 華為云 Dify 平臺介紹 1.2 部署過程介紹 1.3 登錄 Dify 平臺 2 接入華為云 ModelArts Studio 中的 DeepSeek 大模型 3 構建長篇文章生成助手 3.1 簡要介紹長篇文章生成助手 3.2 開始節點 3.3 生成標題和大綱&#xff08;LL…

js的一些基礎概念總結

1.變量聲明 首先js變量聲明有三種&#xff0c;var&#xff0c;const&#xff0c;let&#xff0c;這三種變量聲明中我們第一優先使用const&#xff0c;需要改變這個值的時候我們用ley&#xff0c;var是盡量不去使用。 那么我們現在來總結一下三種聲明變量的區別。首先是var let …

防 XSS和CSRF 過濾器(Filter)

會話管理存在問題&#xff1a; 1.服務集群部署或者是分布式服務如何實現會話共享 2.會話的不同存儲地方的安全性問題 答&#xff1a; 會話共享 可以使用后端集中管理(redis)或者客戶端管理 &#xff08;jwt&#xff09;&#xff1b; 存儲安全性 這個還真的沒有太好的方式&…

鴻蒙容器組件 WaterFlow、FlowItem解析:動態瀑布流布局實踐

一、引言&#xff1a;不規則布局的智能化解決方案 在圖片社交、電商導購、資訊聚合等現代應用場景中&#xff0c;瀑布流布局以其靈活的空間利用率和自然的視覺流動感成為界面設計的重要選擇。鴻蒙提供的 WaterFlow 與 FlowItem 組件&#xff0c;通過智能布局算法與聲明式語法&…

概率密度基本概念

概率密度&#xff08;Probability Density&#xff09;是概率論中用于描述隨機變量分布的一種方式&#xff0c;特別適用于連續隨機變量。它并不是一個概率值&#xff0c;而是表示單位范圍內的概率大小或“濃度”。更具體地說&#xff0c;概率密度表示在某個特定值附近&#xff…

10-1 MySQL 索引優化與查詢優化

10-1 MySQL 索引優化與查詢優化 文章目錄 10-1 MySQL 索引優化與查詢優化1. 數據準備2. 索引失效案例2.1 索引字段&#xff1a;全值匹配最優2.2 索引字段&#xff1a;最佳左前綴法則2.3 主鍵插入順序2.4 索引字段進行了&#xff1a;計算、函數、類型轉換(自動或手動)導致索引失…

基于目標驅動的分布式敏捷開發

研究結論 風險對項目目標的影響 時間目標&#xff1a;需求管理不當&#xff08;如需求優先級不明確、多產品負責人需求沖突&#xff09;、架構變更導致的返工、跨站點協調問題&#xff08;如第三方依賴、通信基礎設施不足&#xff09;是影響項目時間的主要風險因素。質量目標&…

高通手機跑AI系列之——穿衣試裝算法

環境準備 手機 測試手機型號&#xff1a;Redmi K60 Pro 處理器&#xff1a;第二代驍龍8移動--8gen2 運行內存&#xff1a;8.0GB &#xff0c;LPDDR5X-8400&#xff0c;67.0 GB/s 攝像頭&#xff1a;前置16MP后置50MP8MP2MP AI算力&#xff1a;NPU 48Tops INT8 &&…

opencv入門(5)圖像像素的讀寫操作和算術運算

文章目錄 1 圖像遍歷與修改1.1 使用數組1.2 使用指針 2 圖像的算術運算2.1 一般算術操作2.2 算術API 1 圖像遍歷與修改 C中支持 數組遍歷 和 指針方式遍歷 1.1 使用數組 訪問使用 image.at(row,col) 進行訪問 如果是單通道灰度圖&#xff0c;就使用image.at進行讀取 如果是三…

Stable Diffusion入門-ControlNet 深入理解-第三課:結構類模型大揭秘——深度、分割與法線貼圖

大家好,歡迎回到Stable Diffusion入門-ControlNet 深入理解系列的第三課! 在上一課中,我們深入探討了 ControlNet 文件的命名規則,以及線條類 ControlNet模型的控制方法。如果你還沒有看過第二篇,趕緊點這里補課:Stable Diffusion入門-ControlNet 深入理解 第二課:Contr…

噴油嘴深凹槽內輪廓測量的方法探究 —— 激光頻率梳 3D 輪廓測量

引言 噴油嘴作為燃油噴射系統核心部件&#xff0c;其深凹槽內輪廓精度直接影響燃油霧化效果與發動機排放性能。噴油嘴深凹槽具有深徑比大&#xff08;可達 30:1&#xff09;、孔徑小&#xff08;φ0.5 - 2mm&#xff09;、表面質量要求高&#xff08;Ra≤0.2μm&#xff09;等…

上證ETF50期權交易規則一文詳解

50ETF期權&#xff0c;首先這是期權交易&#xff0c;所以50ETF期權有期權交易的所有特征&#xff0c;其次&#xff0c;50ETF期權的標的對象是上證50&#xff0c;所以50ETF&#xff08;認購看漲&#xff09;期權的走勢和上證50的走勢是一樣的。 行權時間&#xff1a; 在行權日當…

Oracle獲取執行計劃之10046 技術詳解

Oracle 的 10046 事件是性能調優中最常用的工具之一&#xff0c;通過跟蹤會話的 SQL 執行細節&#xff0c;生成包含執行計劃、等待事件、綁定變量等信息的跟蹤文件&#xff0c;幫助定位性能瓶頸。以下是技術詳解&#xff1a; 一、10046 事件基礎 10046 是 Oracle 內部事件&…

Linux 日志監控工具對比:從 syslog 到 ELK 實戰指南

更多云服務器知識&#xff0c;盡在hostol.com 你有沒有被 Linux 上滿屏飛滾的日志整崩潰過&#xff1f;看著 /var/log 目錄越來越肥&#xff0c;關鍵日志像大海撈針一樣藏在里面&#xff0c;每次出故障就像拆盲盒&#xff0c;賭你能不能第一眼看出問題。 日志系統&#xff0c…

本地服務器部署后外網怎么訪問不了?內網地址映射互聯網上無法連接問題的排查

我的網站部署搭建在本地服務器上的&#xff0c;在內網可以正常訪問&#xff0c;但是外網無法訪問&#xff0c;該怎么排查&#xff1f;局域網內部經過路由器的&#xff0c;有設置了虛擬服務器轉發規則&#xff0c;在互聯網公網上還是無法訪問服務器怎么辦&#xff1f;相信很多人…

如何免費正確安裝微軟的office全家桶

記錄一下如何正確安裝微軟的office全家桶 找到安裝包傻瓜式安裝 找到安裝包 安裝包在附件&#xff0c;大家可以自行進行下載 傻瓜式安裝 操作一目了然&#xff0c;點你需要的就行了