Thank you for correcting a wrong description in the article.
Look good, but don’t stop liking.
Zero, directories,
The full word: 3718 | are expected to read: 14 minutes
Click to expand the catalog
- One, the introduction
- 2. Module definition
- Three, modular design principles
- 3.1 Interface-Oriented
- 3.2 Data Driven
- 3.3 Module Isolation
- Fourth, modular framework design
- 4.1 the data source
- 4.1.1 Data source protocol
- 4.1.2 Module component Management
- 4.1.3 Data flow direction
- 4.2 Module Components
- 4.2.1 Component Protocols
- 4.2.2 Module component data model
- 4.3 Channel Proxy object protocol
- 4.4 Object Communication
- 4.5 interaction diagrams
- 4.6 Architecture Overview
- 4.1 the data source
- Five, the summary
One, the introduction
In order to meet the requirements of the content typesetting of the channel page dynamically configured by the operation students and the one-time development of the product and the reuse of various channels, a framework should be developed to meet the following two points:
- Flexible content layout: the contents and order of a channel page, or even a new channel page, can be directly configured by the operation students in the BACKGROUND of CMS
- Module global reuse: a host of content
The module
After development, it can be configured in all channel pages
Benefits of page modularization:
-
It is convenient for students to directly create a new interface or dynamically adjust the interface (navigation bar, footer, content elements, etc.) in CMS background online. Shorten the content launch cycle
-
As our different business codes are componentized, they are isolated from each other. Business components developed by different lines of business are difficult to reuse (data model, naming, method definition, etc.). And not all business components can be encapsulated as generic base components and sunk into the base component library. By developing this framework, you can simultaneously create a uniform canonical library of business components
2. Module definition
The content mentioned above is the module we see on each channel page. Different modules have their own unique product functions and operational purposes.
Take lvmama home page channel as an example, as shown below:
The area circled by each box is an independent module. Such as:
- Banner module (product recommendation, activity promotion, advertising, etc.)
- Channel entry and subject list module (user diversion orientation)
- Travel headlines module (recommended by popular travel notes)
- .
In addition, each module can contain a single or multiple different module components:
Three, modular design principles
In addition to considering SOLID(six principles), the framework will be designed around the following three points.
3.1 Interface-Oriented
By defining interface (that is, protocol) abstractions and classes or things that are of concern to the specification framework. Low coupling between framework and module.
For example, for a framework, it doesn’t care what structure the configuration data is or how it is retrieved, it only cares about how many modules there are, how big each module is in the container, and where.
You can do this by defining a data source protocol that specifies the behavior you must follow to act as a framework data source object. It doesn’t matter what the exact type of the data source object is, as long as you follow the protocol you can play a role in the framework.
An interface is just like a contract. A prepared contract cannot be easily modified. If the original terms are hastily modified, it is bound to affect all the people who follow the contract. Therefore, the contract formulation stage is particularly important, can not be ambitious and can not be short-sighted, set everyone in accordance with the contract.
Of course, interface orientation and object orientation are not in conflict, but complement each other and are not discussed here.
3.2 Data Driven
Data determines and drives the presentation and response of content.
-
Data determines the content to be displayed, that is, the one-to-one correspondence between data and content:
According to the relevant data provided by the data source, the framework decides the type of components to be created for each module, the display size and layout position of module components, etc.
-
Data drives content change. Focus on data, not events.
For example, for any event that happens to a module in the framework, there are only two outcomes:
- The numbers have changed since the event
- Data does not change after the event
So the framework doesn’t care what happens, it just “looks” at whether the data it cares about has changed, right
In addition, event-driven events often correspond to one response operation, a one-to-one relationship. While data-driven can be a 1 versus N relationship, it can be multiple events that modify the same data.
3.3 Module Isolation
Modules are isolated from each other. Modules are independent and autonomous, and their related affairs are handled by themselves.
Modules can be developed separately and registered in the configuration. The module can use MVX, VIPER and other Structual Design patterns.
In addition, in module co-development, framework and module should be properly isolated. Follow the principle of dependency inversion. Module (high level) dependency should not directly rely on framework (low level) development, framework only know we abstract module interface, module only know framework interface, both sides follow the interface to implement.
Generally speaking, the functional development of the framework and the development of modules are two parallel lines, unless the interface is modified, otherwise the implementation of both sides will not affect the other side.
Currently this implementation is a traditional architecture (4.6 Architecture Diagram), that is, the development of modules is directly dependent on the implementation of the framework (concrete base classes), and the framework does not abstract out the interfaces exposed to the upper layer. Following the dependency inversion principle, when a module (high level) accesses the lower level (framework), it can only touch an object (UIViewController
*) that follows the framework interface, not a specific XXXClass class.
It’s still a matter of trade-offs, selective opening and closing. If the dependency inversion principle is fully followed, then the upper level cannot inherit directly from the lower level implementation, including what is not allowed to change, which is to use the common implementation, which is left to the upper level to extend, and so on.
Fourth, modular framework design
Taking the iOS platform as an example, the specific design of the whole framework is described. Regardless of Android and iOS platform system coding style habits and specific implementation of the existence of differences, the overall idea is similar.
4.1 the data source
A channel page consists of several modules, each of which contains one or more different components. The framework creates and places module components based on information provided by the data source.
4.1.1 Data source protocol
-
Module data source protocol: mainly provides the framework with the component information contained in a module, the related layout information, and the content of the component fill data, etc
typedef NSObject<LVTSectionDataSource> LVTSectionData; @protocol LVTSectionDataSource <NSObject> - (LVTemplateClass)headerClass; - (LVTemplateClass)cellClassAtIndex:(NSUInteger)index; -... - (BOOL)hidden; - (NSUInteger)numOfItems; - (UIEdgeInsets)sectionInset; - (CGFloat)itemSpace; - (CGFloat)lineSpace; - (CGSize)itemSizeAtIndex:(NSUInteger)index withContainerSize:(CGSize)size; -... - (nullable LVTItemModel *)itemModelAtIndex:(NSUInteger)index; - (void)requestSectionCustomData; @end Copy the code
-
Channel page data source protocol: mainly provides the framework with the total number of modules owned by the whole channel, as well as local data sources of each module
@protocol LVTPageDataSource <NSObject> - (NSUInteger)numberOfSections; - (LVTSectionData *)sectionDataAt:(NSUInteger)section; - (void)fetchPageDataWithCompletedBlock:(void(^) (NSError _Nullable *error))completedBlk; @end Copy the code
4.1.2 Module component Management
For any component in the module, there is a corresponding identity ID. We maintain the identity and component mapping through a configuration file. In joint development, you only need to modify the configuration file every time you develop a new component.
The structure of the JSON configuration is as follows:
// part of the example {"header": {
"header1": "LVTXXXHeader", // value is the name of the concrete class"header2": "LVTXXXHeader". },"cell": {
"cell1": "LVTXXXCell". },... }Copy the code
We get a concrete class name from the ID, and use reflection to get the class object for the framework to create component instances.
On iOS we use a ClassMapper to maintain the correspondence, as shown below.
The above class name is just a better representation of Mapper’s responsibilities. The actual class objects returned by ClassMapper are decoupled using generics, and no component headers are introduced in ClassMapper.
4.1.3 Data flow direction
From the raw data to each module component rendered to the screen, the data flow is shown below:
The elements above represent: LVTPageDataSource: complies with the channel data source protocol. LVTSectionData: complies with the module data source protocol. ClassMapper: manages the mapping between LVTCellXXX and LVTHeaderXXXCopy the code
4.2 Module Components
Components are fundamental elements for reuse in modular frameworks.
4.2.1 Component Protocols
Module components are divided into reusable and non-reusable categories, corresponding to the following protocols respectively:
-
Reuse component protocol: Provides the reuse Id used by the component to reuse queues, the size of elements used for layout, and so on
typedef Class<LVTReuseItemProtocol> LVTemplateClass; typedef UICollectionViewCell<LVTReuseItemProtocol> LVTemplateCell; typedef UICollectionReusableView<LVTReuseItemProtocol> LVTemplateReuseView; typedef WKWebView<LVTReuseItemProtocol> LVTemplateWebView; @protocol LVTReuseItemProtocol <NSObject> + (NSString *)tIdentifier; + (CGSize)itemSizeWithModel:(LVTItemModel *)model andContainerSize:(CGSize)size; - (void)configItemWithModel:(LVTItemModel *)model; - (void)setEventCenter:(id<LVTEventCenterProtocol>)center; - (void)setCacheUtil:(LVTCacheUtil *)util; - (void)itemPrepareForReuse; @end Copy the code
-
Non-reusable floating component protocol: provides view height, floating location information, etc
typedef Class<LVTFloatViewProtocol> LVTFloatViewClass; @protocol LVTFloatViewProtocol <NSObject> + (CGFloat)topInSection; + (CGFloat)viewHeight; - (void)configItemWithModel:(LVTItemModel *)model; -...@end Copy the code
Common methods such as data populating can be abstracted to another protocol and then inherited
4.2.2 Module component data model
The types of data models used to populate module components vary, and the framework is not tied to a specific model. The properties of the protocol specification data model are sufficient.
Data model protocol:
typedef NSObject<LVTItemModelProtocol> LVTItemModel;
@protocol LVTItemModelProtocol <NSObject>
@property (nonatomic.assign) BOOL isFolded;
@property (nonatomic.assign) CGSize itemSize;
@property (nonatomic.assign) CGSizefoldedItemSize; .@end
Copy the code
The LVTItemModel we saw in the previous protocol represents the data model that follows this protocol
4.3 Channel Proxy object protocol
The position of the modules mentioned above in the framework is adjustable. Fixed content in a channel, such as navigation bars, container headers, footers, etc., is handled by a channel’s proxy object.
In addition to the fixed content management, there are some business functions unrelated to the framework, such as point location acquisition, site switching and other functions, will also be implemented in the proxy object, but not reflected in the protocol.
The specific agency agreement is as follows:
@protocol LVTPageDelegate <NSObject>
@property (nonatomic.weak) LVTEventCenter *eventCenter;
@property (nonatomic.weak) LVTLayoutQuery *layoutQuery;
@property (nonatomic.readonly) CGFloat containerViewTopInset;
@property (nonatomic.readonly) BOOL hidesBottomBarWhenPushed;
@property (nonatomic.readonly) BOOL showsLoadingIndicator;
@property (nonatomic.strong) MJRefreshHeader *header;
@property (nonatomic.strong) MJRefreshFooter *footer; . - (void)setupPageUI;
- (void)configPageWithModel:(id)model; -...@end
Copy the code
The management of proxy types is consistent with module management, sharing configuration files, and obtaining proxy objects also through ClassMapper.
Also, strictly speaking, it would be more appropriate to name the delegate Strategy, and its use is reflected in the policy pattern. Different agents have different implementations, and a channel may dynamically switch proxy objects when running.
For example, after a drop-down refresh, the delivered agent ID is changed, that is, the corresponding class object is changed, and a new policy object is created to replace it, resulting in a different UI or behavior.
4.4 Object Communication
There is a need for communication between modules and between modules and frameworks. For example, some module components need to know about the lifecycle events that exist in the framework in order to act accordingly.
Common communication modes between objects are:
- Command mode or target-action
- Proxy mode or Callback
- Observer model
Considering that the communication between modules can be one-to-many, and the previous two are one-to-one communication, we choose to implement a shared object containing all cross-module events based on ReactiveCocoa or RxJava library and follow the observer mode for centralized management. Hereinafter referred to as the event center.
In particular, the set of events associated with the communication requirement module is added to the shared object of the event center in the form of empty methods (method implementation is empty, but not abstract class). Each module optionally subscribes to events on the shared object according to its own needs.
The module communication mode is to directly call the added event method in the shared event center, as follows:
In the 4.2 Module Components section, you can see that both protocols define methods for setting the event center for the framework to assign a value to for the component to access.
4.5 interaction diagrams
The interaction between the core elements of the framework is as follows:
The figure above does not include specific interaction details, so fill in two chronographs:
-
Sequence diagram displayed on the top screen of a channel (ignoring local caching, etc.):
Note: The module’s data source (SectionData) gets the specific class object from ClassMapper, which is visible to the data source protocol
-
A sequence diagram of a frame event (such as a switch interface, slide, etc.) passed to the subscriber through the event center:
Event delivery is a synchronous operation, which thread invokes which thread is triggered, and the order in which subscribers receive events is determined by the order in which they subscribe
4.6 Architecture Overview
The schematic diagram above shows the layer where the elements mentioned above are located after the channel page is modularized.
level | “Personality” named | instructions | Include modular elements |
---|---|---|---|
4 | Top of tower (Summoner’s Canyon) | Externally, it is a “product “. Internally, it is a” carrier “. | There is no |
3 | Pure Business Layer (outer tower) | A layer that is closely related to a specific business, such as a specific interface | Modular common controller VC, follow PageDataSource data source class |
2 | Modular “module” layer (middle tower) | A virtual layer, just to make it more intuitive. It’s actually the pure business layer up there. | Includes all classes that follow the PageDelegate protocol, module component protocol, and module DataSource protocol. It is a large collection of uniform norms |
1 | Business Function Layer (inner tower) | A layer that is still relevant to the business. But it is a common business function | Modularized defined interface, following the VC base class of the interface, “component” base class, ClassMapper, EventCenter, other auxiliary component development classes, etc |
0 | Basic Function Layer (Crystal) | A layer that is not business related can be used for another project and is open source | Common base components, etc |
Five, the summary
The above is the donkey mother channel page modularization of the general idea, the idea is not complex, the main details are various, will not be launched.
No matter what kind of implementation scheme, on the premise of flexibly meeting business needs, and at the same time ensuring the technology expansion, the future will continue to “beat strange upgrade “, is a better solution.
Original author: Fu Xiang
The original address: mp.weixin.qq.com/s/J5YhTk5gy…
For non-commercial reprint, please indicate the author and the address of the original text above.