iOS writing high-quality code coupling processing

Source: Internet
Author: User

original 2016-12-26 mrpeak mrpeak Grocery Store

Coupling is a topic that every programmer has to face and is easily overlooked, and how to deal with coupling is related to the quality of our final code. Today, peak June and everyone talk about coupling this basic topic, together to smooth out the iOS code to deal with the various ways of coupling and differences.

Simplifying Scenarios

The topic of coupling can be large and small, but the principle is interlinked. For the sake of discussion, we will first abstract and simplify the scene and discuss only the coupling between the two classes.

Suppose we have a class person who needs to drink water, and according to the responsibilities, we need another class of cups to complete the water action, the code is as follows:

// Person.h @interface person:nsobject-(void) drink; @end // Cup.h @interface cup:nsobject-(ID) providewater; @end

Obviously, there is a coupling between the person and the cup to complete the drink, and we'll look at what are the coupling methods under objective C, and the effect of the different coupling modes on the later code quality changes.

Way one:. M reference

This method imports the Cup.h directly into the. m file and generates a temporary cup object to invoke the method in the Cup. The code is as follows:

#import "Person.h"#import "Cup.h"@implementation Person- (void) Drink {Cup* C = [CupNew]; IDWater =[C Providewater]; [Self sip:water];}- (void) Sip: (ID) Water {//SIP Water}@end

This should be a lot of students will choose the practice, to use the function of a class, import the class, and then call the method, the function to complete the submission of test one go.

There's nothing wrong with this way of looking at first. However, there is a disadvantage: the person and cup coupling is buried in the Person.m file method implementation, and. m files are generally the hardest hit areas of business logic code, when the PERSON.M code volume expands, if the person class by another engineer to maintain, the new classmate can not from the Person.h See the person class and which classes have interaction, even in the person.m of drink's statement there is no clue, to clarify the words, can only be person.m file read from beginning to end, the impact on team efficiency is conceivable.

Way two:. h property

Since the reference directly in. m will cause the coupling to be unclear, we can put the coupled part in the property with the following code:

//Person.h@interfacePerson:nsobject@property (nonatomic, strong) Cup*Cup;- (void) drink;@end//person.m@implementation Person- (void) Drink {IDWater =[Self.cup Providewater]; [Self sip:water];}- (void) Sip: (ID) water{//SIP Water}@end

In this way, we only need to glance at Person.h to understand which classes the person class has relied on, much clearer than the direct reference in. M.

I wonder if you have ever wondered why there are. h files in Objective C, why not a file representing a class like Java,swift? There are pros and cons to using. h files.

The greatest significance of the. h file is to isolate the declaration and implementation . The declaration is to tell the outside I support what features, implementation is to support these functions behind the code logic. When we read the. h file of a class, its main function is to reveal two messages:

    • What external elements do I (person Class) depend on

    • What interfaces I (Person Class) provide for external invocation

So the. h file should be the key to our code coupling, and when we hesitate to declare the property of a class in the. h file, consider whether the property must be exposed to the outside. Once exposed to the. h file, it increases the chance of dependency and coupling. Sometimes review code, just see if the. h file is clear, you can probably guess the level of this class of designers.

When we make the Cup class as the property statement of person, it shows that there is a necessary dependency between person and cup, we put this kind of dependency into the header file, and it has a clear effect. This is a lot clearer than the way one, but there is another problem, after the cup is exposed, the external elements can be arbitrarily modified, when the internal execution of the drink, the other thread may have empty the cup, affecting the normal business process.

Way three:. H ReadOnly Property

In mode two, the person class is dependent on the cup and also assumes the risk of the cup being externally modified at any time. Of course, the intuitive approach is to use the Cup class as the property of ReadOnly, while providing an external setter:

// Person.h @interface  readonly) cup*  Cup; -(void) Setpersoncup: (cup*) Cup; -(void) drink; @end

Some students may ask, what is the difference between this and the above approach, there is not the same interface to read and write? The biggest difference is that the entrance to the inspection and disturbance is increased.

When I debug, often need to check that a propery is exactly who modified, set a breakpoint in the setter to debug a lot of convenience. We can also use the caller mechanism of Xcode to see if the current setter is called by those external classes, and it is helpful to analyze the association between classes and classes.

The setter method in PERSON.M also provides an entrance to our expansion capabilities, such as the need to add multi-threaded sync lock to the setter, and when other methods in person.m use the cup, the setter must wait for completion to execute. Another example is that we can implement the copy on write mechanism in the setter:

// person.m-(void) Setpersoncup: (cup*) Cup {    cup* anothercup = [cup copy];     = Anothercup;}

This allows the person class to avoid sharing the same cup with the external class and to eliminate the health problems of using the same cup;)

In short, the individual setter method allows us to have more control over the code, but also for the subsequent maintenance of your code for students to bring convenience, self-interest people.

Mode four: Init injection

The use of the property with setter although looks good, but the setter method can be arbitrary external class at any time with the engraved call, for PERSON.M in the method of using Cup, how much some is not relieved, in case of use by someone else to change it?

In order to avoid being arbitrarily modified, we can use the Init injection method, Objective C in the designated initializer is to be born for this:

// Person.h @interface person:nsobject-(Instancetype) Initwithcup: (cup*) Cup; -(void) drink; @end

Remove the property, put the cup's settings into the Init method, so that the person class provides only one chance to set Cup,init, the external class has no other chance to modify the cup.

This is the most used, but also the more recommended way. Only when the object is created, to establish a relationship with other objects, the variability is reduced to a certain extent. Is there any downside to this approach?

Setting the cup in init mode eliminates the influence of external factors, but if the Cup object is held internally, the internal function call can still be coupled to the Cup class through various poses, such as:

//person.m@interfacePerson () @property (nonatomic, strong) Cup*Mycup;@end@implementation Person-(Instancetype) Initwithcup: (cup*) Cup { self=[Super Init]; if(self) {Self.mycup=Cup; }      returnSelf ;}- (void) Drinkwater {IDWater =[Self.mycup Providewater]; [Self sip:water];}- (void) Drinkmilk {IDMilk =[Self.mycup Providemilk]; [Self sip:milk];}@end

The method inside the person can generate the coupling through all the external interfaces of the cup, at which point the coupling between the two classes is mainly interpreted by the Cup.h header file. If the Cup class design is reasonable, the header file structure is clear, this is not really too bad scene. Is there any other way?

Mode five: Parameter injection

In the way property is held, the possibility of coupling exists throughout the life cycle of the person object, because the property is globally visible to. m files. Another way we can make the coupling happen inside a single method is parameter injection:

// Person.h @interface person:nsobject-(void) Drink: (cup*) Cup; @end // person.m-(void) Drink: (cup*) Cup {        id water = [Cup providewater];    [Self sip:water];}

The advantage of this approach is that the coupling of person and cup only occurs inside the drink function, and once the function call ends, the dependency is ended between person and cup. In terms of time and space, this approach is less risky than holding the property.

If there is more than one cup of dependence in person, such as drinkwater,drinkmilk,drinkcoffee and so on, but rather than the property intuitive convenience.

Method Six: Single case reference

The pros and cons of the single case have a lot of excellent technical article analysis, peak June only to emphasize one point, is also peacetime review code and debug found the most problem reason: A single case of state sharing .

In the above example, we can make a single case of cup, the code is as follows:

// person.m-(void) drink {        id water = [[Cup sharedinstance] providewater];    [Self sip:water];}

The coupling generated in this way is not only as covert as the way one, but is the most likely to lead to code demotion, and with the iteration of the version, we are likely to get the following class correlation graph:


All objects depend on the state of the same object, and all objects have read and write access to the state of the object, and the final result is probably a patch fix bug, Gourd Dipper float.

The use of a similar scenario is common, such as we hold a user's information in a singleton, after the user log out, forget to clear the previous user's information will lead to strange bugs, and once a single case scattered in all corners of the project, it is very difficult to deal with each.

Way VII: Inheritance

Inheritance is a strong coupling relationship, there are a lot of inheritance (inheritance) and combination (compoisition) between the pros and cons of the article, here do not repeat. Inheritance does make it easy to build clear object models at an early stage, reuse and polymorphism look good too, the problem is that the strong coupling relationship is prone to divergence in understanding, such as what objects can be established as parent-child relationships, and which subclasses can be put into the parent class for other subclasses. These problems can become more complex when multiple layers are inherited. So the peak is proposing to use as little as possible the inheritance relationship to describe the object, unless it is a clear and no objection to the parent-child relationship.

I'm not going to force a wave. The definition of a parent class is an example, such as what Objectwithcup.

Mode eight: Runtime dependent

Using runtime to deal with coupling is a unique way of objective C, and the coupling is so low that it can even be felt that there is no coupling, such as:

// person.m-(void) Drink: (ID) obj{       id water = nil;     = nsselectorfromstring (@ "providewater");         if ([obj Respondstoselector:sel]) {        = [obj Performselector:sel];    }         if (water) {      [self sip:water];    }}

You do not need to import the cup header files, and you do not need to know which methods the cup supports. The problem with this approach is that the coupling is too low to allow developers to perceive the existence of coupling and to perceive relationships between classes. If one day someone put Providewater into Getwater,drink method if not synchronized to, Xcode compile without prompting you, runtime will not crash, but the business process did not go down normally.

This is why we do not recommend using OBJECTIVE-C Runtime's black magic to do business, but in the absence of side-effects of the scene to complete some data acquisition operations, such as the use of AOP to log logs.

Mode nine: protocol dependent

This is not an independent coupling, protocol can be combined with these various coupling methods to further reduce the coupling, but also in the complex class relationship design recommended way, such as we can define such a protocol:

@protocol Liquidcontainer <nsobject>-(ID) providewater; -(ID) providecoffee; @end // Person.h @interface person:nsobject-(void) Drink: (ID<LiquidContainer>) container; @end

In the above-mentioned way, whether the property is held or parameter injected, can use protocol to reduce dependence, the advantage of protocol is that he only prescribes the declaration of the method, does not limit the specific class to achieve it, for later maintenance left more space and possibility.

More complex scenarios

These are some of the common class coupling methods that describe the coupling between the two Class A and a B. From the above description, we can roughly perceive the depth of the coupling caused by the two classes using different ways , which is plainly the frequency of calling functions and accessing states . Understanding the depth of this coupling can help us roughly quantify the coupling between two objects to analyze the coupling of a module or an architectural approach in more complex scenarios.

In more complex scenarios, such as a,b,c three classes can be used in a similar way to analyze, a,b,c can be the following relationship:
When analyzing the coupling relationship between three classes or more classes, it is also first disassembled into a number of two class analysis, such as the left we analyze ab,bc,ac three groups of coupling, and then to perceive ABC as a whole coupling degree. Obviously, the right side of the way looks better than the left side, because only the AB and BC need to be analyzed. When we use the design pattern to refactor the code, we can also analyze it in a similar way, choosing the least coupling, the pattern that best fits our business scenario.

Our principle is: The method called between class and class, the less dependent state, the better, in Objective C this language environment, the writing classification is clear, the interface concise header file is very important.

Benign coupling

The previous analysis is trying to quantify and perceive the depth of the coupling, but not every method call is risky, and some coupling can be called benign.

If our code is highly abstracted, all the code can be categorized into two categories:data and action. The property in a class is data, and the function in class is action, as mentioned in a functional article I wrote earlier, what really makes our code dangerous is the change in state, that is, changing data. If a function is a pure function, neither depends on the external state nor modifies the external state, then the function is safe no matter how many times it is called. If two classes, such as the person and cup above, call each other a pure function, then the coupling between the two can be regarded as benign, and will not cause the state of the program to maintain confusion, but will make the code refactoring difficult, after all, the deeper the coupling, refactoring changes the more code.

So when we do design, we should try to make the coupling between the different elements benign, which involves the maintenance of the state of the problem, the first look at two different design methods:
The red circle in the figure represents the state held by each class or function unit. According to the design method above, each unit handles its own state change, and these states are dependent on each other, the deeper the coupling, the more difficult it is to develop debugging and refactoring, the worse the code will degrade. If the state changes are all concentrated together in the way below, it is much easier to maintain, which is why many apps have the design of the model layer, and the changes to the app state (various model) are processed independently as a layer, The upper level (business layer) is just the shell that acts as the presentation and interaction of the model layer. This design technique can be applied to the processing of an app architecture, small to a small function module design.

Concluding remarks

It summarizes some of the coupling methods we use to analyze how different codes are written in order to influence our final coupling. Finally, it is worth mentioning that some of the above coupling is not the absolute merits of the points, different business scenarios may be selected in different ways, for example, some scenarios do need to hold property, some scenarios are more appropriate, the key is that we can understand the different ways for our code later maintenance impact, This article may be more abstract in some places, many of which are personal sentiment and summary, or inappropriate, please read the selective absorption after reading, hoping to be able to write code to deal with the coupling of people to bring some help.

iOS writing high-quality code coupling processing

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.