June 19, 2025

Zhou Ligong teaches you to learn programming techniques: do a layered design of software modules, callback functions should be written like this

Chapter Two focuses on programming technology, and this section discusses subsection 2.1.3, which is about callback functions. The arrow symbols (>>>) indicate the progression into more specific topics within this chapter. The first major topic under discussion is the concept of layered design. Layered design involves dividing software into modules that have a hierarchical relationship. This approach ensures that each layer remains relatively independent. By defining the interfaces between these layers, developers can work on each part separately, thus minimizing the coupling between different modules. For instance, consider designing a secure electronic lock. The hardware components might include a keypad, display, buzzer, lock, and memory drive circuit. Therefore, the software could be divided into three layers: the hardware driver layer, the virtual layer, and the application layer. Each larger module can further be broken down into smaller submodules. Let's illustrate this with an example of keyboard scanning. The hardware driver layer is at the bottommost level and interacts directly with the hardware. Its primary responsibility is identifying which key has been pressed. This layer implements low-level software closely tied to the hardware circuitry. More complex functionalities are handled at higher levels. While the hardware driver layer can directly interact with the application layer, doing so would mean the application layer would become dependent on the hardware specifics. To avoid this, a virtual layer is typically introduced to manage hardware changes. Clearly, if the keyboard scanning method remains consistent, the generated key values won't change, meaning the virtual layer’s software won’t need modification. The virtual layer sits above the hardware driver layer and is designed based on the needs of the application layer. It serves to abstract away the intricate details and variations of the objects, allowing the application layer to operate uniformly. Even if the control methods change, the application layer code doesn’t require rewriting. At the top is the application layer, which directly implements the functions. For example, externally, the application layer might only have one "human-computer interaction" module. Internally, it could be divided into several submodules for use. The flow of data between these three layers is quite straightforward: application layer → virtual layer → hardware driver layer. As depicted in Figure 2.2, the solid lines in the diagram represent dependencies, indicating that the application layer relies on the virtual layer, and the virtual layer depends on the hardware driver layer. A layered architecture offers several benefits: 1. Reduces system complexity: Since each layer is relatively independent and communicates via well-defined interfaces, each layer can be developed separately, thus decreasing the coupling between modules. 2. Isolates changes: Software modifications usually happen at the top and bottom layers. The top layer is the graphical user interface, and changes in requirements typically impact the UI directly. Most updates to software versions show significant differences in the user interface. At the bottom, hardware changes occur faster than software development. Layered design separates these changes, ensuring that modifications in one part don’t significantly affect others. 3. Facilitates automated testing: Each layer has distinct features, making it easier to write test cases. 4. Enhances program portability: Different platform components are placed in separate layers. For instance, the lower module acts as a wrapper for the operating system interface, while the upper layer implements a graphical user interface for different platforms. When moving to different platforms, only the varying parts need implementation, while the middle layer can be reused. Figure 2.2 shows the three-layer structure, emphasizing the dependency relationships. The application layer, positioned at the top, directly implements the functions. Externally, it might only have one "human-computer interaction" module. Internally, it could be divided into several submodules. The data flow between the three layers is clear: application layer → virtual layer → hardware driver layer. Solid lines in Figure 2.2 denote dependencies: the application layer depends on the virtual layer, and the virtual layer depends on the hardware driver layer. Layered architecture provides numerous advantages. Another key aspect discussed is the isolation of changes. One principle is the Hollywood Principle, which states, "Don't call me, I'll call you." This principle minimizes coupling between layers. The upper layer can call functions from the lower layer, but the reverse isn't allowed, preventing circular dependencies. Circular dependencies hinder software reusability and scalability, making each layer unable to act as an independently reusable component. Even though the upper layer can call adjacent lower layers, it cannot jump across layers. Lower modules implement interfaces declared by upper modules and are called by higher modules. This creates a more flexible, durable, and modifiable structure. There are two ways to utilize callback functions: 1. Directly calling the callback function C during the execution of the upper module A invoking lower module B. 2. Using a registration method where the lower module calls the callback function upon an event occurrence. Callback functions allow dynamic binding, enabling the upper module to monitor and interfere with the operations of the lower module. This essentially means the upper module calls the lower module dynamically. For example, consider sorting data using a bubble sort function. The comparison function plays a crucial role in determining the order of data. For integer data types, the comparison function returns a negative number if *e1 is less than *e2, a positive number if *e1 is greater than *e2, and zero if they are equal. To generalize, the comparison function takes void* pointers as parameters. The caller decides the type of data and the method of operation at runtime, writing the appropriate data comparison function. For instance, a typical comparison function might look like this: ```c int compare_int(const void *e1, const void *e2) { return (*((int *)e1) - *((int *)e2)); } ``` Similarly, for descending order, the function would return the reverse result. When dealing with strings, the `strcmp` function can be used: ```c int compare_vstrcmp(const void *e1, const void *e2) { return strcmp(*(char **)e1, *(char **)e2); } ``` Bubble sort functions also utilize function pointers. Their prototypes evolve to accommodate different data types and sizes: ```c void bubbleSort(void *base, size_t nmemb, size_t size, COMPARE compare); ``` Here, `base` is the starting address of the array, `nmemb` is the number of elements, and `size` is the size of each element. The `COMPARE` type is a function pointer type. Finally, callback functions help reduce coupling between layers, allowing for independent modifications without affecting other layers. This is particularly useful for maintaining flexibility in software development. In conclusion, callback functions play a vital role in layered software design, promoting modularity, flexibility, and maintainability.

Digital Display Controller

Digital Display Controller,Controller For Solar Energy,Off Grid Pwm Solar Charging Controller,Solar Controller Home Solar Power

GuangZhou HanFong New Energy Technology Co. , Ltd. , https://www.gzinverter.com