How does componentization decouple
-
Put code from the same module together
-
Code is the code of two modules, can not be placed in the same module how to do.
Question 1 is very simple, is to do the code level by module. For example, all the code of module A is put in module A, and then when it wants to go outside, module A releases the external interface to other modules to call. For example, the logging module, it can be independent of other modules, it does not depend on other modules, so only need to be responsible for writing the log code in a logging module, so that others want to output logs. You can import the log module and output logs through the interface of the log module. There is no coupling, so there is no need for decoupling.
Question 2: For example, module A uses the methods of module B, and then module B may use the code of module A, but it cannot combine A and B into one module. It is not possible to compile all the modules in A, however, because the compilation will prompt the missing methods in B. Similarly, the B module is the same, without the A module, he can not compile.
Here you need to decouple module A from module B, and module B also needs to decouple module A.
There are a lot of ways to decouple on the web. The two typical ones are as follows:
-
CTMediator’s target-action mode
-
BeeHive uses protocol to implement inter-module invocation
Here reference BeeHive thought, with Swift to achieve a decoupling example.
define
- A module
- B module
- Interface module (Save the external Interface of each module, such as the external Interface of module A or B, are stored here)
- The main project
Let’s look at the final dependency graph
Module A is going to call the Interface of module B, so it doesn’t need to rely on module B, it just needs to rely on Interface, and then module A is going to call the function of module B, it just needs to call module B and put it on the Interface of module B.
How to achieve:
The Interface module has a common class called ModuleInterface and it has two methods inside it, a registration function that lets other modules register their instances there and a fetch function that gets the instance of the corresponding module using a key
public class ModuleInterface {
public static let shared = ModuleInterface(a)public var protocols: [String: BaseProtocol] = [:] // Maintain a dictionary
// Register the function
public func registProtocol(by name: String.instance: BaseProtocol) {
self.protocols[name] = instance
}
// Get the instance
public func getProtocol(by name: String) -> BaseProtocol? {
return self.protocols[name]
}
}
Copy the code
The key here is to maintain a dictionary and find the corresponding instance by the corresponding key.
The public Interface of module B is also placed in this Interface module, for example:
extension ModuleInterface: BProtocol {
// b External interface
public func getBModuleValue(b: String.callback: ((Int) - >Void)) -> Int {
if let pro = self.getProtocol(by: "BProtocol") as? BProtocol {
return pro.getBModuleValue(b: b, callback: callback)
} else {
print("no found BProtocol instance")
callback(0)
return 0}}}Copy the code
There are two tricks here
- Using the extension ModuleInterface: BProtocol, so that the method defined in BProtocol can be implemented in the ModuleInterface class, so that other modules can use the ModuleInterface to call, simple entry
- Use self.getProtocol(by: “BProtocol”) as? BProtocol, through the way to get the instance of BProtocol type, you can call the method of B module. And it is run time check, this also solved, without reference to the B module can also compile through.
So each module only needs to add its own instance to the dictionary when initialized. And then when you want to call another module, you just take the instance of that module out of the dictionary and call another module.
If module A wants to use module B’s getBModuleValue method, it simply imports the Interface module and calls it from the ModuleInterface module as follows:
let a = ModuleInterface.shared.getBModuleValue(b: "a call b") { value in
print("==callBModule=result==", value)
}
Copy the code
The whole code implementation is very simple.
The code for pod is as follows:
A module's podSpec definition
s.dependency 'Interface'
A module's podfile definition
use_frameworks!
platform :ios, '9.0'
target 'AModule_Example' do
pod 'AModule', :path = > '../'
pod 'Interface', :path = > '. /.. /Interface'
end
B module podSpec definition
s.dependency 'Interface'
B module podfile definition
use_frameworks!
platform :ios, '9.0'
target 'BModule_Example' do
pod 'BModule', :path = > '../'
pod 'Interface', :path = > '. /.. /Interface'End main projectPodfile definition for Demo
use_frameworks!
platform :ios, '9.0'
target 'Demo' do
pod 'AModule', :path = > '../AModule'
pod 'BModule', :path = > '../BModule'
pod 'Interface', :path = > '../Interface'
end
Copy the code
Specific code to see examples: github.com/yxh265/Modu…