」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > D - 依賴倒置原理(DIP)

D - 依賴倒置原理(DIP)

發佈於2024-11-03
瀏覽:380

D - Dependency Inversion Principle(DIP)

Before understanding DIP (Dependency Inversion Principle), it's important to know what High-Level and Low-Level modules and abstractions are.

High-Level Module:

A high-level module refers to the parts of an application that manage core features or business logic. These modules usually handle larger and more critical functions of the application. High-level modules generally perform broader and more complex tasks, and they might depend on other modules to get their work done.

For example, if you're building an e-commerce application, the Order Management System would be a high-level module. It handles major tasks such as taking orders, processing them, managing deliveries, and so on.

Low-Level Module:

Low-level modules, on the other hand, are responsible for specific and smaller tasks. These modules work at a fundamental level of the application and perform more detailed work, like storing data in the database, configuring network settings, etc. Low-level modules typically help the high-level modules carry out their tasks.

Continuing with the e-commerce example, the Payment Gateway Integration would be a low-level module. Its job is specifically to process payments, which is a more specialized task in comparison to the broader functionalities of the order management system.

Abstraction:

In simple terms, abstraction refers to a general idea or representation of a system or process. It defines what should happen but does not specify how it will happen. It focuses on the essential functionality without delving into the underlying implementation details.

Example:

Imagine you are going to drive a car. The concept of driving the car (i.e., abstraction) includes:

  • Starting the car
  • Stopping the car

However, the abstraction doesn't specify how these actions will be performed. It doesn't tell you what type of car it is, what kind of engine it has, or the specific mechanisms that make the car start or stop. It only provides the general idea of what needs to be done.

Abstraction in Software Design:

In software design, abstraction means creating a general contract or structure using an interface or an abstract class that defines a set of rules or actions, but the specific implementation of those actions is left separate. This allows you to define what needs to be done without specifying how it will be done.

Example:

You could have a PaymentInterface that declares a method for processing payments. The interface specifies that a payment must be processed, but it doesn't define how the payment will be processed. That can be done through different implementations such as PayPal, Stripe, or other payment methods.

In essence, abstraction focuses on the general functionality while leaving the implementation flexible, making it easier to change or extend in the future.

What is Dependency Inversion Principle(DIP) ?

According to the Dependency Inversion Principle (DIP), high-level modules should not depend directly on low-level modules. Instead, both should rely on abstractions, such as interfaces or abstract classes. This allows both high-level and low-level modules to work independently of each other, making the system more flexible and reducing the impact of changes.

Simplified Explanation:

Imagine you have an application with multiple features. If one feature directly depends on another, then modifying one will require changes across many parts of the code. The DIP suggests that instead of direct dependency, you should make different parts of your application work through a common interface or abstraction. This way, each module can function independently, and changes to one feature will have minimal impact on the rest of the system.

Two Key Points of DIP:

  • High-level modules should not depend on low-level modules. Instead, both should rely on abstractions.

  • There should be a dependency on abstractions, not on concrete (specific implementations).

Example:

Consider a payment system where you can use both PayPal and Stripe as payment methods. If you work directly with PayPal or Stripe, adding a new payment gateway later would require extensive code changes throughout your application.

However, if you follow DIP, you would use a general PaymentInterface. Both PayPal and Stripe would implement this interface. This way, if you want to add a new payment gateway in the future, you simply need to implement the existing interface, making the process much easier.

By following this principle, DIP enhances the maintainability and flexibility of your code, allowing for smoother adaptations and extensions without major disruptions.

Example 1:

Let's consider an application where you need to start and stop different types of vehicles. You can create a Vehicle interface that serves as an abstraction. Any vehicle can implement this interface to provide its own functionality.

Java Code:

 // Abstraction: Vehicle interface
interface Vehicle {
    void start();
    void stop();
}

// Car class implements Vehicle interface
class Car implements Vehicle {
    public void start() {
        System.out.println("Car started");
    }

    public void stop() {
        System.out.println("Car stopped");
    }
}

// Bike class implements Vehicle interface
class Bike implements Vehicle {
    public void start() {
        System.out.println("Bike started");
    }

    public void stop() {
        System.out.println("Bike stopped");
    }
}

// Main class
public class Main {
    public static void main(String[] args) {
        Vehicle car = new Car(); // Car as Vehicle
        car.start();
        car.stop();

        Vehicle bike = new Bike(); // Bike as Vehicle
        bike.start();
        bike.stop();
    }
}


Explanation:

Here, Vehicle serves as an abstraction that specifies what actions need to be performed (i.e., start and stop), but it does not define how those actions should be executed. The Car and Bike classes implement these methods according to their own logic. This design allows for the easy addition of new vehicle types, such as Truck, without altering the existing code.

Adding a New Vehicle: Truck
To add a new vehicle, you simply need to create a Truck class that implements the Vehicle interface and provides its own implementation of the start() and stop() methods.

Java Code for Truck:


class Truck implements Vehicle {
    public void start() {
        System.out.println("Truck started");
    }
    public void stop() {
        System.out.println("Truck stopped");
    }
}


Using Truck in the Main Class Without Any Changes:
Now, to include the Truck in the Main class, you can create an instance of Truck and use it as a Vehicle. No changes are necessary in the Main class or any other parts of the code.

Java Code for Main Class:


public class Main {
public static void main(String[] args) {
Vehicle car = new Car(); // Car as Vehicle
car.start();
car.stop();

    Vehicle bike = new Bike(); // Bike as Vehicle
    bike.start();
    bike.stop();

    // Adding Truck
    Vehicle truck = new Truck(); // Truck as Vehicle
    truck.start();
    truck.stop();
}

}

Output:
Copy code
Car started
Car stopped
Bike started
Bike stopped
Truck started
Truck stopped

Enter fullscreen modeExit fullscreen mode




Explanation:

Here, the Truck has been added without making any changes to the existing code. The Truck class simply implements its own versions of the start() and stop() functions according to the Vehicle interface. There was no need to modify the Main class or any other part of the application. This demonstrates how the Dependency Inversion Principle (DIP) promotes flexibility and maintainability in code.

Benefits of DIP:

  • Scalability: New classes (like Truck) can be easily added to the system without affecting existing components.

  • Maintainability: The Main class and other high-level modules do not need to be modified when new features or vehicles are introduced.

  • Flexibility: It is possible to add new vehicles without relying on existing ones, allowing for easier integration of new functionality.

By adhering to the Dependency Inversion Principle, the system remains extensible and maintainable through the use of abstractions.

Importance of DIP:

In this context, Car, Bike, and Truck do not depend directly on the Main class. Instead, the Main class relies solely on the Vehicle abstraction. This means that if a new vehicle type is introduced, there is no need to make changes to the Main class. As a result, the code is much easier to maintain, and adaptability is significantly enhanced.

This approach not only reduces the risk of introducing bugs during modifications but also encourages better design practices by promoting loose coupling between components.

Example 2:

Let's say you want to create a notification system that can send different types of notifications, such as Email, SMS, or Push Notifications. If you directly rely on the Email or SMS classes, adding a new notification method would require changes throughout your code, violating the Dependency Inversion Principle (DIP).

Solution:

We will create an abstraction called Notifier, which will only specify what needs to be done (send notification) without defining how to do it. Different notifier classes (Email, SMS, Push) will implement this functionality in their own way.

JavaScript Code:


// Abstraction: Notifier
class Notifier {
sendNotification(message) {
throw new Error("Method not implemented.");
}
}

// EmailNotifier implements Notifier
class EmailNotifier extends Notifier {
sendNotification(message) {
console.log(Sending email: ${message});
}
}

// SMSNotifier implements Notifier
class SMSNotifier extends Notifier {
sendNotification(message) {
console.log(Sending SMS: ${message});
}
}

// PushNotifier implements Notifier
class PushNotifier extends Notifier {
sendNotification(message) {
console.log(Sending Push Notification: ${message});
}
}

// Usage
function sendAlert(notifier, message) {
notifier.sendNotification(message);
}

// Test different notifiers
const emailNotifier = new EmailNotifier();
const smsNotifier = new SMSNotifier();
const pushNotifier = new PushNotifier();

sendAlert(emailNotifier, "Server is down!"); // Sending email
sendAlert(smsNotifier, "Server is down!"); // Sending SMS
sendAlert(pushNotifier,"Server is down!"); // Sending Push Notification




Explanation:

  • Notifier is the abstraction that specifies that a notification needs to be sent.

  • EmailNotifier, SMSNotifier, and PushNotifier implement the Notifier rules in their own ways.

  • The sendAlert function only depends on the Notifier abstraction, not on any specific notifier. Therefore, if we want to add a new notifier, there will be no need to change the existing code.

Why DIP is Important Here:

To add a new notification method (like WhatsApp), we simply need to create a new class that implements Notifier, and the rest of the code will remain unchanged.

Example 3:

Let’s say you are creating an e-commerce site where payments need to be processed using various payment gateways (like PayPal and Stripe). If you directly rely on PayPal or Stripe, making changes or adding a new gateway would require significant alterations to your code, violating the Dependency Inversion Principle (DIP).

Solution:

We will create an abstraction called PaymentGateway. Any payment gateway will operate according to this abstraction, so adding a new gateway will not require changes to other parts of the code.

JavaScript Code:


// Abstraction: PaymentGateway
class PaymentGateway {
processPayment(amount) {
throw new Error("Method not implemented.");
}
}

// PayPal class implements PaymentGateway
class PayPal extends PaymentGateway {
processPayment(amount) {
console.log(Processing $${amount} payment through PayPal);
}
}

// Stripe class implements PaymentGateway
class Stripe extends PaymentGateway {
processPayment(amount) {
console.log(Processing $${amount} payment through Stripe);
}
}

// Usage
function processOrder(paymentGateway, amount) {
paymentGateway.processPayment(amount);
}

// Test different payment gateways
const paypal = new PayPal();
const stripe = new Stripe();

processOrder(paypal, 100); // Processing $100 payment through PayPal
processOrder(stripe, 200); // Processing $200 payment through Stripe




Explanation:

  • PaymentGateway is an abstraction that specifies the requirement to process payments.

  • The PayPal and Stripe classes implement this abstraction in their own ways.

  • The processOrder function only depends on the PaymentGateway abstraction, so if you want to add a new gateway (like Bitcoin), you don’t need to make any changes to the existing code.

DIP is Important Here:

If you need to add a new payment gateway, you simply create a new class that implements the PaymentGateway, and there will be no changes to the core code. This makes the code more maintainable and flexible.

DIP কেন গুরুত্বপূর্ণ?

1. Maintainability: নতুন কিছু যোগ করতে বা পরিবর্তন করতে হলে বড় কোড পরিবর্তন করতে হয় না। DIP মেনে abstraction ব্যবহার করে সিস্টেমকে সহজে মেইনটেইন করা যায়।

2. Flexibility: নতুন ফিচার যোগ করা অনেক সহজ হয়ে যায় কারণ high-level module এবং low-level module আলাদা থাকে।

3. Scalability: DIP এর মাধ্যমে সিস্টেমে নতুন ফিচার বা ফাংশনালিটি যোগ করা সহজ এবং দ্রুত করা যায়।

4. Decoupling: High-level module এবং low-level module সরাসরি একে অপরের উপর নির্ভর না করে abstraction এর মাধ্যমে কাজ করে, যা সিস্টেমের dependencies কমিয়ে দেয়।

Dependency Inversion Principle(DIP) in React

The Dependency Inversion Principle (DIP) can make React applications more modular and maintainable. The core idea of DIP is that high-level components should not depend on low-level components or specific implementations; instead, they should work with a common rule or structure (abstraction).

To implement this concept in React, we typically use props, context, or custom hooks. As a result, components are not directly tied to specific data or logic but operate through abstractions, which facilitates easier changes or the addition of new features in the future.

Example:

  • If you are building an authentication system, instead of directly relying on Firebase, create an abstraction called AuthService. Any authentication service (like Firebase or Auth0) can work according to this abstraction, allowing you to change the authentication system without altering the entire codebase.

  • Similarly, when making API calls, instead of depending directly on fetch or Axios, you can use an abstraction called ApiService. This ensures that the rules for making API calls remain consistent while allowing changes to the implementation.

By adhering to DIP, your components remain reusable, flexible, and maintainable.

Example 1: Authentication Service

You are building a React application where users need to log in using various authentication services (like Firebase and Auth0). If you directly work with Firebase or Auth0, changing the service would require significant code modifications.

Solution:

By using the AuthService abstraction and adhering to the Dependency Inversion Principle (DIP), you can create an authentication system that allows different authentication services to work together.

JSX Code:


import React, { createContext, useContext } from "react";

// Abstraction: AuthService
const AuthServiceContext = createContext();

// FirebaseAuth implementation
const firebaseAuth = {
login: (username, password) => {
console.log(Logging in ${username} using Firebase);
},
logout: () => {
console.log("Logging out from Firebase");
}
};

// Auth0 implementation
const auth0Auth = {
login: (username, password) => {
console.log(Logging in ${username} using Auth0);
},
logout: () => {
console.log("Logging out from Auth0");
}
};

// AuthProvider component for dependency injection
const AuthProvider = ({ children, authService }) => {
return (

{children}

);
};

// Custom hook to access the AuthService
const useAuthService = () => {
return useContext(AuthServiceContext);
};

// Login component that depends on abstraction
const Login = () => {
const authService = useAuthService();

const handleLogin = () => {
authService.login("username", "password");
};

return ;
};

// App component
const App = () => {
return (



);
};

export default App;




Explanation:

  • AuthServiceContext acts as a context that serves as an abstraction for the authentication service.

  • The AuthProvider component is responsible for injecting a specific authentication service (either Firebase or Auth0) into the context, allowing any child components to access this service.

  • The Login component does not directly depend on any specific service; instead, it uses the useAuthService hook to access the authentication abstraction. This means that if you change the authentication service in the AuthProvider, the Login component will remain unchanged and continue to work seamlessly.

This design pattern adheres to the Dependency Inversion Principle (DIP), promoting flexibility and maintainability in the application.

Example 2: API Service Layer with Functional Components

You are making various API calls using fetch or Axios. If you work directly with fetch or Axios, changing the API service would require numerous code changes.

Solution:

We will create an abstraction called ApiService, which will adhere to the Dependency Inversion Principle (DIP) for making API calls. This way, if the service changes, the components will remain unchanged.

JSX Code:


import React, { createContext, useContext } from "react";

// Abstraction: ApiService
const ApiServiceContext = createContext();

// Fetch API implementation
const fetchApiService = {
get: async (url) => {
const response = await fetch(url);
return response.json();
},
post: async (url, data) => {
const response = await fetch(url, {
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" }
});
return response.json();
}
};

// Axios API implementation (for example purposes, similar API interface)
const axiosApiService = {
get: async (url) => {
const response = await axios.get(url);
return response.data;
},
post: async (url, data) => {
const response = await axios.post(url, data);
return response.data;
}
};

// ApiProvider for injecting the service
const ApiProvider = ({ children, apiService }) => {
return (

{children}

);
};

// Custom hook to use the ApiService
const useApiService = () => {
return useContext(ApiServiceContext);
};

// Component using ApiService abstraction
const DataFetcher = () => {
const apiService = useApiService();

const fetchData = async () => {
const data = await apiService.get("https://jsonplaceholder.typicode.com/todos");
console.log(data);
};

return ;
};

// App component
const App = () => {
return (



);
};

export default App;




Explanation:

  • ApiServiceContext is a context that serves as an abstraction for making API calls.

  • The ApiProvider component injects a specific API service.

  • The DataFetcher component does not directly depend on fetch or Axios; it relies on the abstraction instead. If you need to switch from fetch to Axios, you won't need to modify the component.

Example 3: Form Validation with Functional Components

You have created a React app where form validation is required. If you write the validation logic directly inside the component, any changes to the validation logic would require modifications throughout the code.

Solution:

We will create an abstraction called FormValidator. Different validation libraries (such as Yup or custom validation) will work according to this abstraction.

JSX Code:


import React from "react";

// Abstraction: FormValidator
const FormValidatorContext = React.createContext();

// Yup validator implementation
const yupValidator = {
validate: (formData) => {
console.log("Validating using Yup");
return true; // Dummy validation
}
};

// Custom validator implementation
const customValidator = {
validate: (formData) => {
console.log("Validating using custom logic");
return true; // Dummy validation
}
};

// FormValidatorProvider for injecting the service
const FormValidatorProvider = ({ children, validatorService }) => {
return (

{children}

);
};

// Custom hook to use the FormValidator
const useFormValidator = () => {
return React.useContext(FormValidatorContext);
};

// Form component using FormValidator abstraction
const Form = () => {
const validator = useFormValidator();

const handleSubmit = () => {
const formData = { name: "John Doe" };
const isValid = validator.validate(formData);
console.log("Is form valid?", isValid);
};

return ;
};

// App component
const App = () => {
return (



);
};

export default App;




Explanation:

  • FormValidatorContext acts as an abstraction for form validation.

  • The FormValidatorProvider component injects a specific validation logic (e.g., Yup or custom logic).

  • The Form component does not depend directly on Yup or custom validation. Instead, it works with the abstraction. If the validation logic needs to be changed, such as switching from Yup to custom validation, the Form component will remain unchanged.

Disadvantages of Dependency Inversion Principle (DIP):

While the Dependency Inversion Principle (DIP) is a highly beneficial design principle, it also comes with some limitations or disadvantages. Below are some of the key drawbacks of using DIP:

1. Increased Complexity: Following DIP requires creating additional interfaces or abstract classes. This increases the structure's complexity, especially in smaller projects. Directly using low-level modules makes the code simpler, but adhering to DIP requires introducing multiple abstractions, which can add complexity.

2. Overhead of Managing Dependencies: Since high-level and low-level modules are not directly connected, additional design patterns like dependency injection or context are needed to manage dependencies. This increases the maintenance overhead of the project.

3. Unnecessary for Small Projects: In smaller projects or in cases with fewer dependencies or low complexity, using DIP can be unnecessary. Implementing DIP creates additional abstractions that make the code more complicated, while directly using low-level modules can be simpler and more effective.

4. Performance Overhead: By introducing abstractions between high-level and low-level modules, there can be some performance overhead, especially if there are many abstraction layers. Each abstraction adds extra processing, which can slightly impact performance.

5. Misuse of Abstraction: Excessive or incorrect use of abstraction can reduce the readability and maintainability of the code. If abstractions are not thoughtfully implemented, they can create more disadvantages than the intended benefits of DIP.

6. Harder to Debug: Due to the use of abstractions and interfaces, debugging can become more challenging. Without directly working with the implementation, identifying where a problem originates from can take more time.

7. Dependency Injection Tools Required: Implementing DIP often requires using dependency injection frameworks or tools, which take time and effort to learn. Additionally, the use of these frameworks increases the complexity of the project.

Conclusion:

Although DIP is a powerful and beneficial principle, it does have its limitations. In smaller projects or less complex contexts, adhering to DIP may be unnecessary. Therefore, proper analysis is needed to determine when and where to apply this principle for the best results.

? Connect with me on LinkedIn:

I regularly share insights on JavaScript, Node.js, React, Next.js, software engineering, data structures, algorithms, and more. Let’s connect, learn, and grow together!

Follow me: Nozibul Islam

版本聲明 本文轉載於:https://dev.to/nozibul_islam_113b1d5334f/d-dependency-inversion-principledip-3m0?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • 尋找經濟實惠的同日格蘭尼公寓(附 Pillar Build Granny Flats)
    尋找經濟實惠的同日格蘭尼公寓(附 Pillar Build Granny Flats)
    在 Pillar Build Granny Flats,我們為您提供祖母屋解決方案的精英服務,滿足您的獨特需求。無論是房主、承包商還是投資者,我們都可以幫助您在當天購買後院公寓,效果非常好,為您節省寶貴的時間,而且不用說,預算也很實惠。我們的祖母房建造者將在每一步工作,以確保您的專案以最精確和細心的...
    程式設計 發佈於2024-11-05
  • 如何使用 botoith Google Colab 和 AWS 集成
    如何使用 botoith Google Colab 和 AWS 集成
    您有沒有想過,在實施AWS Lambda時,想要一一確認程式碼的運作情況? 您可能認為在 AWS 控制台上實施很痛苦,因為您必須執行 Lambda 函數並且每次都會產生成本。 因此,我將向您展示您的擔憂的解決方案。 它是透過 Google Colab 和 AWS 整合實現的。 步驟如下: ...
    程式設計 發佈於2024-11-05
  • (高效能 Web 應用程式的要求
    (高效能 Web 應用程式的要求
    “高性能网络应用程序”或“前端”到底是什么? 自从 Internet Explorer 时代衰落以来,JavaScript 生态系统变得越来越强大,“前端”一词已成为高性能、现代 Web 客户端的代名词。这个“前端”世界的核心是 React。事实上,在前端开发中不使用 React 常常会让一个人看...
    程式設計 發佈於2024-11-05
  • 如何將單一輸入欄位設定為分區輸入?
    如何將單一輸入欄位設定為分區輸入?
    將輸入欄位設為分區輸入有多種方法可用於建立一系列分區輸入欄位。一種方法利用「字母間距」來分隔單一輸入欄位內的字元。此外,「background-image」和「border-bottom」樣式可以進一步增強多個輸入欄位的錯覺。 CSS Snippet以下 CSS 程式碼示範如何建立所需的效果:#pa...
    程式設計 發佈於2024-11-05
  • 用 Go 建構一個簡單的負載平衡器
    用 Go 建構一個簡單的負載平衡器
    负载均衡器在现代软件开发中至关重要。如果您曾经想知道如何在多个服务器之间分配请求,或者为什么某些网站即使在流量大的情况下也感觉更快,答案通常在于高效的负载平衡。 在这篇文章中,我们将使用 Go 中的循环算法构建一个简单的应用程序负载均衡器。这篇文章的目的是逐步了解负载均衡器的工作原理。 ...
    程式設計 發佈於2024-11-05
  • 如何以超連結方式開啟本機目錄?
    如何以超連結方式開啟本機目錄?
    透過超連結導航本地目錄嘗試在連結互動時啟動本地目錄視圖時,您可能會遇到限制。然而,有一個解決方案可以解決這個問題,並且可以在各種瀏覽器之間無縫運作。 實作方法因為從HTML 頁面直接開啟路徑或啟動瀏覽器是由於安全原因受到限制,更可行的方法是提供可下載的連結( .URL 或.LNK)。 建議路徑:.U...
    程式設計 發佈於2024-11-05
  • 為什麼 Makefile 會拋出 Go 指令的權限被拒絕錯誤?
    為什麼 Makefile 會拋出 Go 指令的權限被拒絕錯誤?
    在執行Go 時Makefile 中出現權限被拒絕錯誤透過Makefile 執行Go 指令時可能會遇到「權限被拒絕」錯誤,即使你可以直接執行它們。這種差異是由於 GNU make 中的問題引起的。 原因:當您的 PATH 上有一個目錄包含名為“go.gnu”的子目錄時,就會出現此錯誤。 ”例如,如果您...
    程式設計 發佈於2024-11-05
  • parseInt 函數中 Radix 參數的意義是什麼?
    parseInt 函數中 Radix 參數的意義是什麼?
    parseInt 函數中 Radix 的作用parseInt 函數將字串轉換為整數。然而,它並不總是採用以 10 為基數的數字系統。若要指定所需的基數,請使用基數參數。 理解基數基數是指單一數字表示的值的數量。例如,十六進制的基數為 16,八進制的基數為 8,二進制的基數為 2。 為什麼要用基數? ...
    程式設計 發佈於2024-11-05
  • 如何使用 JavaScript 將連結保留在同一選項卡中?
    如何使用 JavaScript 將連結保留在同一選項卡中?
    在同一分頁和視窗中導覽連結您可能會遇到想要在同一視窗和分頁中開啟連結的情況作為當前頁面。但是,使用 window.open 函數通常會導致在新分頁中開啟連結。為了解決這個問題,您可以使用name 屬性,如下所示:window.open("https://www.youraddress.co...
    程式設計 發佈於2024-11-05
  • 如何解決Python中的循環依賴?
    如何解決Python中的循環依賴?
    Python 中的循環依賴使用 Python 模組時遇到循環依賴可能是一個令人沮喪的問題。在這個特定場景中,我們有兩個文件,node.py 和 path.py,分別包含 Node 和 Path 類別。 最初,path.py 使用 from node.py import * 導入 node.py。但是...
    程式設計 發佈於2024-11-05
  • MariaDB 與 MySQL:開發人員需要了解什麼
    MariaDB 與 MySQL:開發人員需要了解什麼
    MariaDB 和 MySQL 是著名的開源 RDBMS,但儘管它們有著共同的歷史,但它們在功能和效能方面卻有所不同。本文快速強調了主要差異,幫助開發人員決定哪個資料庫最適合他們的需求。 差異和範例 儲存引擎,MariaDB 對 Aria 和 MyRocks 等引擎的擴充支援提供了...
    程式設計 發佈於2024-11-05
  • 為什麼我的 Goroutine 遞增變數會產生意外的結果?
    為什麼我的 Goroutine 遞增變數會產生意外的結果?
    這是編譯器最佳化的結果嗎? 在此程式碼片段中,啟動了一個 goroutine 並重複遞增變數 i:package main import "time" func main() { i := 1 go func() { for { ...
    程式設計 發佈於2024-11-05
  • 利用 AI 快速學習 Node.js - 第 4 天
    利用 AI 快速學習 Node.js - 第 4 天
    今天,借助ChatGPT繼續學習Node.js,重點是非同步程式設計。這是 Node.js 中最重要的概念之一,我很高興能夠開始掌握它。 理論 在 Node.js 中,非同步程式設計因其非阻塞、事件驅動的架構而至關重要。這意味著文件讀取、資料庫查詢或網路請求等操作在等待結果時不會阻塞其他程式碼的執...
    程式設計 發佈於2024-11-05
  • Java 可以定義帶有嵌入引號的字串而不轉義嗎?
    Java 可以定義帶有嵌入引號的字串而不轉義嗎?
    揭開Java 使用嵌入式引號定義字串的替代方法在Java 中處理字串時,您常常會在文字中遇到大量引號,導致繁瑣的轉義和可讀性挑戰。雖然其他語言提供了處理這種情況的語法,但 Java 缺乏類似的選項。 問題: Java 是否提供了另一種方法來定義帶有嵌入引號的字串而不訴諸轉義? 答案: 雖然Java ...
    程式設計 發佈於2024-11-05
  • 耐用的 Python:建立防彈的長期運作工作流程,變得簡單
    耐用的 Python:建立防彈的長期運作工作流程,變得簡單
    在现代软件开发中,创建强大的工作流程来连接来自各种服务的 API 并处理同步和异步事件是一个常见的挑战。传统方法涉及使用队列、微服务和状态管理系统的组合来构建可扩展的应用程序。虽然有效,但这种架构带来了巨大的开销:设置和维护消息队列等基础设施、运行服务器或 lambda 函数、管理数据库中的状态以及...
    程式設計 發佈於2024-11-05

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3