Day932.5 steps to efficiently promote the reconstruction of component architecture - system reconstruction practice

5 steps to efficiently promote the reconstruction of component architecture

Hi, I am 阿昌, today the learning notes are all about 5个步骤,高效推动组件化架构重构the content.

The architectural design of the project is one thing, and the code implementation is another 很多架构设计最终都只是落在了 PPT 上.

  • On the one hand, it may be because of the follow-up 构腐化, 缺少守护;

  • On the other hand, it is the transformation link that actually lands on the code 复杂度比纸上画图高得多.

The refactoring transformation process is divided into 5 steps, and the large-scale architecture refactoring is carried out safely and efficiently, and it is guarded by automated means.

insert image description here

As shown in the figure above, the five steps are design, guard, decoupling, move and acceptance.


1. Design and identify cohesive components

How to conduct component analysis and design? The types of components are divided, and a panoramic component sorting of Sharing is also carried out, and the components are divided into three categories 业务组件: , , 功能组件and 技术组件.

insert image description here

Refer to the design on the UI to divide 业务组件, usually when the product is designed, relatively cohesive functions will be organized on one page 便于用户使用.

So you can start with the page, and then gradually analyze the classes that this page depends on, and divide these classes into unified business components.

功能组件The usual feature is that it is reused by multiple business components, so according to the dependency of the code, it can be judged whether the function that is referenced in multiple places belongs to the functional component.

Analyze the key entry classes that are depended on by business components, and at the same time find the related classes that this class depends on, and divide these classes into unified functional components.

For 技术组件most applications, frameworks provided by third parties may be used.

If the project has related technical components developed by itself, you can refer to the identification method of functional components for analysis, but it should be noted that technical components have nothing to do with specific businesses and can be used in multiple applications.

Finally, it should be noted that usually some classes may be wrongly divided into certain components in the first step, but in the subsequent dedependency, you can still continue to readjust the division of some classes.


2. Guard and increase automated testing

Since the code needs to be refactored and adjusted in the third step, although this process will use the IDE for safe refactoring, but because the code adjustment will be relatively large, it is necessary to add basic automated tests before moving the code to ensure that the refactoring The structure does not destroy the original function.

So what automated tests need to be supplemented?

There are two types of automated tests to be supplemented in this step. The first is the automated test of the architecture guard. Refer to the practice of using automated tools to diagnose and analyze the Sharing project using ArchUnit to cover the architecture guard test for Sharing. The second is the automated testing of functions. How do we improve the testability of legacy system code? When talking about testing strategies, we once gave a strategy for legacy system coverage automation testing, that is, first consider covering medium and large-scale tests, and then refactor the code. After the refactoring is completed, it will be timely Complements small and medium tests.


This is done for two reasons:

  • One is because it also 未重构的遗留系统可测性很低covers 小型自动化测试的成本太高;
  • The second is that the internal structure of the code will be adjusted after refactoring. If the small test is covered first, the subsequent ones will be followed 测试代码也要相应再次调整.

So generally speaking, guard tests are medium and large UI automation tests, because refactoring will not change the user's process of using the software.

As for how to cover medium and large automated tests, refer to the content of automated tests .


3. Decoupling, removing exception dependencies

For Sharing projects, the code has been organized according to the new package structure.

insert image description here
But if you want to Modularizemove these codes to a separate module by , the IDE will prompt a warning.

insert image description here

The main problem here is that the code to be moved depends on the code in the current project. As long as this dependency is not removed, the fourth step of moving cannot be performed.

So how to remove the dependency?

The following four commonly used ways to remove dependencies are class sinking, dependency interface, event bus and routing.


1. Class sinking

Class sinking refers to moving dependent classes to public functional components or technical components.

This method of de-dependency is applicable to the classes on which the business components depend are public components.

The operation steps are also relatively simple, namely:

  • Move the concrete classes into appropriate public components.
  • The calling component adds a dependency on this common component.

Since the file module depends on LogUtils, before moving the file module to an independent business component, classes such as LogUtils and NetUtil need to be moved to an independent technical component.

insert image description here

In the project, it should be noted that the codes moved to functional or technical components need to be strictly reviewed to avoid forcibly moving some classes belonging to business components to lower-level components for decoupling, resulting in coupling of business components in lower-level components.


2. Dependency interface

Dependent interface refers to the decoupling of the original direct dependence on the specific implementation into a stable abstract interface.

This method of de-dependency applies to dependencies between business components.

The direct dependencies of business components can be refactored to depend on an abstract interface, which sinks into the base, and the specific implementation remains in the respective business components, followed by the operation steps.

  1. Extract independent methods.
  2. Move the method into a separate class.
  3. Extract the interface and adjust the calling class to the calling interface.
  4. Inject concrete implementations.

Six commonly used security refactoring techniques for legacy systems have demonstrated how to extract interfaces.

Here is a review of the examples and operation diagrams at that time.

The code before refactoring is like this:

public void show() {
    
    
    String url = "http://XXX";
    Bitmap bitmap = new Picasso().load(url);
    showImage(bitmap);
}

The operation animation is as follows.

insert image description here

The refactored code looks like this.

private IImageLoader imageLoader;
public void show() {
    
    
    String url = "http://XXX";
    Bitmap bitmap = imageLoader.getBitmap(url);
    showImage(bitmap);
}

In the project, it is necessary to pay attention to maintaining the stability of the interface. If the interface is frequently modified, it means that all business components that depend on this interface must also be modified synchronously, which is no different from relying on specific implementations.


3. Event bus

The event bus refers to unified message management. After the publisher publishes the message, the receiver can obtain the data by subscribing to the message. This method of removing dependencies is also applicable to scenarios where there is behavior or data monitoring between business components. For example, after the user information is successfully modified in the personal center, it needs to be displayed synchronously in the message module.

At this time, it is recommended to use a mature event bus management framework for management, such as EventBus . The specific operation steps are as follows:

  1. Define events.
  2. The sender sends the corresponding event.
  3. The receiver subscribes to the event to execute the corresponding logic.
  4. Remove unnecessary event listeners in time.

Combined with the above example of personal center modification information synchronization, we can first define the synchronization event, and the code is as follows.

class UpdateUserInfo{
    
    
    private UserInfo userInfo;
    public UpdateUserInfo(UserInfo userInfo) {
    
    
        this.userInfo = userInfo;
    }
    public UserInfo getUserInfo() {
    
    
        return userInfo;
    }
}

Then, the corresponding event occurs after the user information is successfully modified in the personal center.

EventBus.getDefault().post(new UpdateUserInfo(new UserInfo()));

Then define the reception of events in the message module, and register related listeners, and remove the listeners at the same time.

//注册
EventBus.getDefault().register(this);
//监听
@Subscribe(threadMode = ThreadMode.MAIN)
public void userInfoUpdateEventBus(UserInfo userInfo){
    
    
  //刷新相关的数据及页面展示
}
//移除监听
EventBus.getDefault().unregister(this);

Like the dependent interface, the event bus needs to maintain the stability of the event model. If the event model is frequently modified, all components that listen to the event must also be modified synchronously.


4. Routing

Routing refers to managing page URLs and page jumps through unified routing. This method of removing dependencies is suitable for scenarios such as removing route jumps from business component pages.

At present, mature routing framework solutions in the industry can be used for management, such as ARouter , WMRotuer and other frameworks. The operation steps are as follows: First, we need to define the corresponding mapping path in the jump class. Then, use the corresponding path to jump at the call site.

Let's take a look at an example of page redirection defined by ARouter.

//没有使用路由
fragments.add(FileFragment.newInstance());
 
//使用路由

//声明
@Route(path = "/feature/file")
public class FileFragment extends Fragment
//调用
fragments.add((Fragment) ARouter.getInstance().build("/feature/file").navigation();

Finally, you can judge whether all abnormal dependencies have been released by running the architecture guard test case or using the Dependencies function.

If all dependencies are removed, the underlined prompt will not appear when moving to step 4.


4. Mobile, mobile code and resources

After decoupling the dependencies, you can use Modularize to move the entire package into a separate module.


Five, acceptance: decoupling acceptance

The last step is to accept the decoupled components, where three basic acceptance conditions must be met.

  • After the compilation is passed, the installation package can be packaged.
  • Architecture guards use case execution passes.
  • Acceptance automated test execution passed.

When these three conditions are met, basic manual exploratory testing can be carried out. If no abnormalities are found, the code can be submitted for review.


6. Summary

The process of componentized architecture reconstruction includes five steps: design, guard, decoupling, move, and acceptance.

Among them, decoupling is a key step in the implementation of the entire code, and four commonly used methods for removing dependencies are provided.

The following summarizes the definitions, usage scenarios, and precautions of these four methods.

insert image description here


Guess you like

Origin blog.csdn.net/qq_43284469/article/details/129889051