02. A Solution For Code Decoupling

It will take about 5 minutes to finish reading this article.

1. Background

A common code coupling scenario is as follows:

The code in module A needs to call the method doSomething() in module B. If it is called directly in A, it will cause A to have a dependency on B. Yes, most of the code dependencies in development come from this.

2. How to decouple it?

(1) We can create a protocol called BService and class BServiceImp, They store the definition and implementation of the doSomething() method respectively. That’s right, at this time we have completely moved the doSomething() method to BServiceImp class, But usually, BServiceImp should be still at the level of business Code layer and should not be sinked.

(2) Since we have created BService and BServiceImp, so, at this time, we can create a management class to manage all Protocols and ServiceImps, which is called DecoupleServiceManager. DecoupleServiceManager can help us initialize BServiceImp through the protocol BService, as shown in the following figure:

In the beginning, the Binding relationship between the protocol BService and class BServiceImp should be registered at an earlier stage. So when we want to call doSomething(), we can first obtain the instance of the BServiceImp with the help of Protocol BService, like that:

1
2
3

id b_instance = (id<Protocol>)[[DecoupleServiceManager shared] createInstanceWithService:@protocol(BService)]

(3) Let’s take a look at how the doSomething() method is finally called. As shown in the figure below, when module A calls doSomething(), it is now called through the DecoupleServiceManager instead of directly calling the code of module B. In this way, A’s dependence on B is relieved with the help of this third-party DecoupleServiceManager.

Note that DecoupleServiceManager and protocol BService should be stored in Common Component Layer.

3. Precautions

(1) The Binding relationship between the protocol BService and class BServiceImp can be stored in a plist file, when the app starts, DecoupleServiceManager should be launched, and all of the binding relationships must be registered into DecoupleServiceManager.

(2) I should let you know that, this method of code decoupling has a disadvantage. Because BServiceImp is found through the protocol BService and the doSomething() method is called in it, this process will span the code level (business layer code and component layer code). If the method doSomething() needs to pass parameters, as follows:

1
2
3
4
5

- (void)doSomething:(NSString *)name;

- (void)doSomething:(Person *)person;

If this parameter is a Basic data type, like ‘NSString’, ‘NSDictionary’, or ‘NSInteger’, They do not affect the call.
But if this parameter is a business abstract type, it will be a little complex. But we can tackle it with the help of a dictionary.

1
2
3
4
5
6
7
8
9
10

- (void)doSomethingWithParam:(NSDictionary *)params {
Person *p = [params objectForKey:"person"];
[self doSomething:p];
}

- (void)doSomething:(Person *)person {

}

So what we need to do next is called ‘- (void)doSomethingWithParam:(NSDictionary *)params’ to take the place of ‘- (void)doSomething:(Person *)person’.

(3) All the Services files like ‘BService’ can be stored together with ‘DecoupleServiceManager’, But it is recommended that all the Services files can be stored in a separate Pod in the common component layer.

(4) The relevant code address is here: https://github.com/zitao0206/DecoupleManager.