ViewModel Practice: Efficiently and simply organize your code

Source: Internet
Author: User

Reprinted from: http://www.cocoachina.com/ios/20150714/12447.html

Text/A Bird

Objective

Unconsciously, the author also has been the code for more than a year. With the rapid rise in the number of code, how efficient, simple organization of code, often caused by the author's thinking. As a methodology and practitioner (the definition is the author's own nonsense), I always hope to find some simple and effective ways to solve the problem, and thus began a practical experience of building code.

This time to share is your own work flow with some understanding of the MVVM framework after a long-term practice of the MVVM structure. Perhaps there are some areas of improper, I hope you can communicate with each other.

Foreplay

ViewModel This concept is based on the MVVM structure, the full name should be called Model-view-viewmodel, structurally speaking, should be model-viewmodel-viewcontroller-view. Simply put, it is on the basis of the MVC structure that the data-related functions in the viewcontroller are stripped out and form a hierarchical hierarchy.

For a detailed definition of ViewModel, refer to this MVVM introduction.

In addition, in the workflow, the author to a certain extent, I refer to the code construction of BDD, although there is no real sense to build the test code according to behavior, but its writing process and BDD do have a similar point. For BDD, you can refer to this behavior-driven test.

The Demo for this article has been sent to Github: Portal ~

All right, let's get started.

ViewModel and Viewcontroller

Base class

Well, in this case, you need to use the Classic mode of OOP-inheritance.

We're not going to build the ViewModel function too heavy, so it just needs a factory method that points to having its own viewcontroller pointer, with an assignment viewcontroller. Just like this code:

BCBaseViewModel.h @interface bcbaseviewmodel:nsobject @property (nonatomic,weak,readonly) Uiviewcontroller * Viewcontroller; + (Bcbaseviewmodel *) Modelwithviewcontroller: (Uiviewcontroller *) Viewcontroller; @end

ViewModel only needs a viewcontroller pointer of weak type to point to its own viewcontroller, while ViewModel is held by Viewcontroller using strong pointers to circumvent circular references.

That would be enough.

Principal and Agent

In order to make the relationship between ViewModel and Viewcontroller more clear, but also in order to be able to batch production ViewModel, the next definition is ViewModel and Viewcontroller structure features.

After analyzing the reasons and main functions of the ViewModel division hierarchy, we can summarize the following characteristics:

    • ViewModel and Viewcontroller are one by one corresponding

    • The functionality that ViewModel implements is stripped out of the viewcontroller.

    • ViewModel is a subordinate object of Viewcontroller

According to the above characteristics, the most easily thought of inter-class relationship should be the agent/delegate relationship, a glance on the relationship said that the complex may be called to scold, but the next discussion, above more or less will play a decisive role.

For example, although the agent and delegate are determined, but who is the agent, who is the principal? In other words, who is the party of the agreement, and who is the realization of it?

The author here gives two basis to confirm.

    1. The Protocol method is a passive call method, that is, a reverse call. Based on this, the implementation of the Protocol should be both the responder of the event, the event-driven forward call, and then trigger the reverse call.

    2. The implementation of the Protocol is a way of passing, and abstract. In reverse, the parties to the agreement need to implement more difficult abstractions or more specific methods. This basis can also be understood from a different level, that is, the implementation of the agreement should be more replaceable.

The first basis of relative doubt, after all, Viewcontroller is the view of the holders and managers, but also the view and ViewModel mutual influence of the only channel. Having ViewModel as the responder to the View event drives the Uiviewcontroller, which is somewhat unstructured.

The second is the result of practice, in the actual development, from the outside, the view of the change frequency is often greater than the data. Therefore, the probability of reconstructing viewcontroller is greater than the probability of reconstructing ViewModel. However, this inductive conclusion can not be a word, but will advise you in the actual development process, should be aimed at these development requirements for the structure of more flexible adjustment and optimization.

In this practice, the code will be built with ViewModel as the party to the agreement.

Make the protocol a little lighter

In OC, there is a series of @protocol-related grammars specifically designed to declare all the functions associated with implementing the Protocol. But considering that the specific ViewModel and Viewcontroller are different, if we declare an agreement for each group of ViewModel and Viewcontroller, and are handed to each other for implementation and invocation, A surge in code volume is basically a necessity.

In order to make the entire protocol structure lighter, there is no @protocol-related syntax to implement, but the following code is used:

typedef Nsuinteger Bcviewcontrollercallbackaction; @interface Uiviewcontroller (ViewModel)-(void) Callbackaction: (bcviewcontrollercallbackaction) Action info: (ID) info ; @end

This piece of code does a few things:

    1. By using classification, the ViewModel related callback method declarations are extended for Uiviewcontroller. The function is similar to the parent class declaration abstract interface, and the subclass to implement.

    2. Interfaces support the use of parameters, and specific classes no longer develop protocol methods, but only protocol parameters.

    3. By declaring the classification in the base class of ViewModel, you can ensure that the uiviewcontroller that are visible to the ViewModel implement the Protocol method, thus eliminating the need to write @protocol paragraphs.

In the specific ViewModel and Viewcontroller subclasses, you only need to design the callback parameters according to the specific requirements, and construct a corresponding enumeration.

The structure of the entire protocol is lightweight, mainly because the protocol content changes frequently. Using enumerations instead of protocol, you can reduce the scope of changes, and the code is small and easy to customize.

I have also tried the two-way abstract method definition, that is, ViewModel also do some abstract methods, so that the two sides only according to the agreement of the base class work. But in practice, the ViewModel method is not easy to abstract, because its public methods often directly reflect the viewcontroller data needs. If you force an abstract method, instead of building a concrete class, the result is inductive confusion, resulting in the worst outcome is to abandon the compliance protocol, the entire code will become difficult to maintain.

Demand as a behavior

The most common development flow in the development process is the demand-driven development flow. Plainly, is to throw you a picture, sometimes good luck and interactive prototype God horse (bad luck is someone else's home App =), and then to your wayward East a hammer West a wooden club write and draw.

At this point, it is advisable to plan the development process appropriately. Here are some of the main considerations:

    • Development level and order;

    • Only care for as little as possible within a unit of time;

    • Easy to build and debug;

    • Reasonable omission of repetitive work.

In fact, the simple point is to make the whole work flow of rules and order to ensure that development is well-reasoned and controllable. In addition, it can effectively avoid the frequency and severity of the inverse error.

Here, I don't want to share my simple workflow with my face.

The whole process is not complicated, in fact, the first Viewcontroller interface, where the need for data, in the ViewModel to declare a method, and then pretend to call. The code of the masturbate is probably this way:

Typedef ns_enum (bcviewcontrolleraction,bctopviewcallbackaction) {     bctopviewcallbackactionreloadtable = 1 < < 0,     bctopviewcallbackactionreloadresult = 1 << 1};  @interface  bctopviewmodel  : BCBaseViewModel -  (nsstring *) ledstring; -  (Nsuinteger) Operationcount;  -  (nsstring *) Operationtextatindex: (Nsuinteger) index; -  (void) undo; -  ( void) clear;  @end   @interface  BCTopViewController  () @property   (Nonatomic,strong)   bctopviewmodel *model; @property   (nonatomic,weak)  iboutlet uitableview *operationtable ; @property   (nonatomic,weak)  IBOutlet UILabel *result; @end    @implementation   bctopviewcontroller -  (void) viewdidload{    [super viewdidload];     self.operationtable.tablefooterview = uiview.new;    }  #pragma  mark - action -  ( ibaction) Undo: (Uibutton *) Sender{    [self.model undo];}  -  (Ibaction) Clear: (uibutton *) sender{    [self.model clear];}   #pragma  mark - call back -  (void) Callbackaction: (bcviewcontrolleraction) action{    if  (action & bctopviewcallbackactionreloadtable)  {         [self.operationTable reloadData];     }    if  (Action & bctopviewcallbackactionreloadresult)  {         self.result.text = self.model.LEDString;     }}  #pragma  mark - tableView datasource & delegate -  ( Nsinteger) TableView: (uitableview *) Tableview nuMberofrowsinsection: (Nsinteger) Section{    return self.model.operationcount;}  -  (uitableviewcell *) TableView: (uitableview *) Tableview cellforrowatindexpath: ( nsindexpath *) indexpath{    uitableviewcell *cell = [tableview  dequeuereusablecellwithidentifier:@ "Cell"  forIndexPath:indexPath];     cell.textlabel.text = [self.model operationtextatindex:indexpath.row];     Return cell;}   @end

This makes use of a Runtime trick that Nil can respond to any message.

So, although we only declared the method, and did not implement, the above code is ready to run. In other words, you can run it at any time to debug the interface without worrying about the ViewModel implementation.

The relative trouble is the test callback method, the author's own advice is to write a good callback method, the corresponding ViewModel in the Viewcontroller after the positive call to call their own callback, if you encounter a possible network request or need to delay the processing of callbacks, you can also consider writing a Dispatch_after test macro to test the callback.

Generally speaking, the development of the view interface layer is always WYSIWYG, so the test standard is the page requirement itself. When all the needs of the naked eye are realized, our interface writing is over. Of course, at this point the code is still fragile, because we only do the forward implementation, has not done the boundary use case testing, so do not know in the abnormal situation, whether there will be something weird.

Fortunately, we have successfully isolated the data-related parts of Viewcontroller. In future tests, any data-related bugs found, we can all pat the chest and say, it certainly has nothing to do with Viewcontroller.

In addition, as I said, the requirement itself is the test standard of the page. That is, when you implement the requirements, your view layer has passed the test. Yes, I'm going to start to apply TDD thinking. We've taken the demand as a test case, and one by one passes.

And as we develop the viewcontroller, we have also declared all public methods for ViewModel and made calls in the corresponding locations. The point of BDD is It ... When...should's behavior asserts that at this point in the environment, It is Viewmodel,when is the Viewcontroller in each call, and should, corresponds to ViewModel all the data interface derived from the changes.

In other words, we may not be able to see all the changes from the interface, but we have built a testable environment before the ViewModel implementation. If time is sufficient, the first thing at this point should be to write a strong enough test code for each public method to avoid data errors, based on the specific calling environment.

By the way, a few words on the romantic field. In the construction of the program, interface-oriented is better than the implementation-oriented, because in any system, compared to the generation of information, the transmission of information determines whether the system itself is strong. While writing code, the abstract function method is materialized, and then the data is gradually abstracted, experience a similar shuttle-type process, can be more perfect fit "high cohesion, low coupling" goal.

Fat Model

If the ViewModel in practice, the above content has been explained almost. However, given the fact that the author has a full Demo, it explains the design of several other places.

The first is about the design of the fat Model. About the concept of fat-thin Model I also recently from this IOS application architecture on the view layer of the organization and invocation scenarios see. Before that, it was just a question of intuition and friends that the model and model should be differentiated.

The Model's fat-thin is based on business relevance. So, I sometimes call the fat model as the business layer model to differentiate the thin model. In the sample code, Calculatorbrain should be considered a relatively standard business layer Model.

If you encounter a requirement that a single ViewModel (or Controller in MVC) cannot solve, you need a whole business sinking and a relatively independent Model to solve the problem. The upper layer only holds the Open interface of the model, in order to facilitate the business layer Model, with obvious business traces, plainly speaking, is not easy to reuse.

However, the author's own development view is that weak business-related module reusability should be strong, that is, the function should be as far as possible. The strong business-related modules should have better reconfiguration and replacement performance, that is, as much as possible in the function of cohesion. Say simple, such as this Demo is no longer a calculator, and need to become a counter or something, only need to refactor calculatorbrain this class. (Of course, this is just based on the assumption that the interface does not change the requirements of the underlying data to be unthinkable ... )

On the other hand, in the entire MVVM framework, each individual ViewModel can also be treated as a pipe. In the whole business chain to do a two-way abstraction, so that the whole chain of business to replace the various parts of the increase, the author is inclined to interpret it as, through the design of the middle layer, balanced the upper and lower levels of complexity.

More Lightweight Viewcontroller

Objccn.io the first article of the first installment is the lighter-weight View Controllers. It has been mentioned in the article that by moving the implementation of the various protocol to the Viewcontroller, to thin the body for viewcontroller.

I have also been one of the practitioners of this proposal, and even once thought that this is the main function of ViewModel. But as the development time stretched, the author had to start to look at the problem again.

First, this approach creates a lot of extra interfaces. We still use UITableView for example. Suppose we let ViewModel implement the Uitableviewdelegate and Uitableviewdatasource protocol. At this point, what if another control in Viewcontroller wants to respond to TableView's scrolling position? Since ViewModel is the delegate of TableView, we need to declare an additional public method for Viewcontroller, which is called by the ViewModel in the callback method.

It is not difficult to find that the Delegate protocol of the Basic view control involves the response of the view itself, as long as it involves interaction with different control elements under the interface, it is inevitable that viewcontroller participation is required.

I have also tried to implement Uitableviewdelegate in the Viewcontroller, and Uitableviewdatasource to ViewModel way. The egg-ache thing happened in the dynamic altitude Cell implementation, we on the one hand in ViewModel to Tableview:cellforrowatindexpath input data, on the one hand, but also for TableView: Heightforrowatindexpath: The Open Interface provides the same data for calculating the height.

The author concludes with the reason that the View layer and the Viewcontroller layer itself are holding and being held dependent, so any class that implements the protocol callback as an in-class instance of Viewcontroller, is actually called at the cross-layer, so it is destined to be at the expense of an additional interface , in other words, the cohesion of the viewcontroller has become worse.

On the other hand, the reason is about testing. The reason we say viewcontroller is difficult to test is because in most cases it does not have a few decent public methods, and a majority of the methods in the private method are parameter callbacks. If we implement these protocol in another class, it will not actually improve their testability. A more effective approach would be to isolate the protocol implementation from the data interface, allowing the implementation party to populate the data with the interface rather than itself.

In the Demo, Topviewmodel opens the Operationcount and Operationtextatindex for the contents of the cell: such a data interface. It is much easier to construct a test environment for such a data interface than to construct a test environment for this method of Tableview:cellforrowatindexpath. From the side, such an interface is more appropriate for testing coverage.

Based on the above two reasons, in the subsequent development, the author began to be more and more protocol back to the Viewcontroller. And, due to the existence of ViewModel, the author prefers to build Viewcontroller into a class that is independent and responsible for the layout and logic of the interface, allowing a class to do less, but doing better.

Postscript

The relevant Demo of this paper, the realization of the function is not complicated, even some poor people. See the author is responsible for the imagination of the poor, in the practice of demonstration-based mentality, make a reference on it.

The author prides himself as a methodology and practitioner, and shares the view that "the method of building code is more valuable than code". Writing one or two amazing code may be luck, mastering the way to build the code itself is the battle. As much as possible to make their own every code is justified, rather than arbitrary, but also feel more responsible, at least write up a confession.

The above summary insight is limited, many places may have the omission, hoped to be able to communicate with you crossing, if can point out the omission even wrong viewpoint, that is not very grateful.

Besides, it's not embarrassing to say something. As of the author finished this blog post, although the "design pattern" of the relevant concepts have a variety of innuendo and query, but still did not learn the relevant concepts. Ashamed to say, sometimes they spend a great effort to understand, think clearly of the answer, suddenly found a certain book, an article has already a few words speak plainly, in fact, quite frustrated. Many times, even to the knowledge of the unknown resistance, used to comfort themselves very good, this is specifically stated that there is no systematic study of the reasons for it.

However, the development of a long road, in fact, we all know, we just climbed to the shoulders of the Giants to move brick workers only. Look back at their feet, every brick is enough to make their own ashamed, self-deception or anything, is only impetuous above, embarrassing.

So I write in the middle of this article, has purchased the "design mode: Reusable Object-oriented Software Foundation" a book, hoping to learn some of the code of the system to build skills bar (later also good bragging = =).

ViewModel Practice: Efficiently and simply organize your code

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.