All of the iOS code we’ve written so far follows the OOP programming paradigm, using objects to copy and express our understanding of the world. Adhering to the five SOLID principles when designing our classes makes our code more extensible and maintainable. The O in SOLID stands for the Open/closed principle. This article will not only discuss the Open and closed in class design, but also look at the Open and closed in code from a broader perspective.

preface

As code workers, we can’t just write code that works, but focus on improving our posture all the time. To be specific, it is to strengthen the study of “internal work mind method”, and gradually improve the abstraction and design ability of writing code.

Programmers are a branch of science and technology education, and we have always been based on strict logical deduction ability. We can easily find the logic incoherence, not rigorous, and not strict in the thinking of liberal arts students. We are good at explaining logic and process with exquisite keywords like if, else, for, switch and so on. Using code to express the process looks really cool, very scientific, very true, but in the eyes of mathematicians, most of our programmers write code is actually “full of bugs”, and “rigorous” is hardly close to the word, looks no better than liberal arts students how much. What’s the problem? The posture level is not enough.

Open vs Closed

Let’s start with the Open/closed principle as a starting point to build a preliminary impression of the Open and closed code. Wikipedia defines it as follows:

In object-oriented programming, the open/closed principle states “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

This principle requires that we design units of functionality that are open to extension and closed to code modification. I don’t know what you think of this abstract description, but when Peak first saw it, his mind felt like a cloud. How could a horse run without eating grass?

This mysterious description, reflected in the code, but some daily language skills, we can understand and realize the openness and closure of the code from multiple perspectives.

Inheritance, a simple inheritance relationship can reflect the Open /closed principle. If we design a parent class that has a clear and complete function definition at the beginning of design, and swear to God that we will never modify a line of code of the parent class, then we can say that the parent class has been closed. What if you want to expand the functionality? Create a new subclass that inherits from the parent class and add the new functionality we need to make it open. In a nutshell, the parent class is closed to code modification, while the subclass is open to extension. In a real project, how many people can bear to not modify the parent class?

Polymorphism can also reflect the Open/Closed principle. When designing functional units, we only define the interface, but do not specify the details of specific implementation. When a class or module is delivered and we continue to make a vow never to change the definition of the interface, the interface is closed. But later we may need to modify the implementation details and extend the functionality, so we replace another class that implements the interface, and this new class implementation is open to code changes. In short, interfaces are closed to code change and implementations are open to code change. That’s why we need to make a lot of use of Protocol when we write iOS code.

In this view, the definition of open and closed is still very clear, and they can reasonably coexist if they are aimed at different objects. But why should we be both closed and open? Because closed things are static, stable, secure, and you can’t bug without writing a line of code, right? However, every project we do is in a state of change, and every new feature is to meet the changing market demand, so open is inevitable. What should we do? Let open and Closed coexist, keep the stable part unchanged, expand on the closed code base, and open new code.

Algebraic data types

Having talked about familiar inheritance and polymorphism, let’s move into slightly stranger territory: Algebraic data types.

Algebraic data Types is a type definition in the purely functional programming language Haskell. This is a seemingly simple technical concept that is extremely confusing to beginners. It’s hard to understand because it’s mostly used to define data models, and it’s not at all the same as data types like ints and floats that we use to write business.

Algebraic data types can be simply defined as a set of data types, where data types are the data types in our traditional sense, such as bool, int, double, etc. Algebraic Data Types Provide specific Algebraic operations to perform logic on each data type in a collection of data types. The algebraic operations are usually of two kinds: sum and product. Pretty abstract, isn’t it? What’s the point? Let’s look at the code in iOS.

For example, the BOOL type we use everyday:

BOOL isValid = true; 
isValid = false;Copy the code

The value of isValid is either true or false, so there are two possibilities for the value of isValid, namely the sum of true and false, so the BOOL type can be understood as a sum type.

To see a CGPoint:

struct CGPoint {    

   CGFloat x;    CGFloat y; };Copy the code

X and Y exist in the type CGPoint at the same time. It is not a choice between the two types, but a relationship similar to the combination of simultaneous existence. We call the data type CGPoint composed of two sub-types product type.

As you may have noticed, sum type and product type are operations on the elements of a data type set to assemble possible combinations. OOP data types (such as our self-defined class) emphasize the encapsulation of property and function, while Algebraic data Types have a completely different perspective, focusing on the combination of data types. When we use Algebraic data types recursively to describe data, we open the door to a new world.

Sum Type and Product Type are Algebraic data types. What is the use of a data type defined according to this rule? There are many benefits, one of which is relevant to the topic of this article. Algebraic data types have an important feature: Algebraic Datatypes are used for all types of Algebraic datatypes, and cannot be modified once the datatypes have been defined. Algebraic Data Types are closed once they are defined. Algebraic data types are Algebraic. Algebraic data types are Algebraic.

For example, if isValid contains true and false, half-true is not allowed, and all isValid operations must use both true and false possibilities.

Closed and Exhaustive features of Algebraic Data Types can make code more stable. Of course, this feature requires language support. Objective C does not have relevant features, but we can use their ideas for reference in code design.

When we write business, we often need to design various Model classes. Facebook opened source a framework for managing and generating Models, called Remodel, in 2016. One of the powerful and comprehensive libraries is the generation of models that conform to Algebraic data types. For example, the following code describes a message model with multiple types:

@interface MessageContent : NSObject <NSCopying, NSCoding>



+ (instancetype)imageWithPhoto:(Photo *)photo; + (instancetype)stickerWithStickerId:(NSInteger)stickerId; + (instancetype)textWithBody:(NSString *)body; - (void)matchImage:(MessageContentImageMatchHandler)imageMatchHandler sticker:(MessageContentStickerMatchHandler)stickerMatchHandler text:(MessageContentTextMatchHandler)textMatchHandler;



@endCopy the code

MessageContent has three possible types, image, sticker, and text. The Match method provided by MessageContent deals with all possible scenarios in an exhaustive manner, and for the users of MessageContent, no possibility is missed, forcing the model users to consider all of them.

The advantage of this approach is that the code, once generated, is extremely stable and cannot be modified. The disadvantages are also obvious. Once the business requires us to add a new type, such as the voice message MessageContent for Voice, it will be difficult to start, because once the change must change the match method signature, to add a new type processing in an exhaustive way, the code change must involve a wide range of.

So you see, whether the design is closed or open is actually a tradeoff between change and constant according to the business scenario. The purpose of introducing Algebraic data Types here is to explain that the Closed and Exhaustive design methods make our code more reliable and stable.

Optional in Swift

When I first started learning Swift, I don’t know if you were curious about the introduction of optional. There are a lot of scenarios where optional is used. There are a lot of documents about how to use optional in different syntax. How is it different than when we used Objective C to determine if it’s nil?

Let’s start with the following function:

- (User*)getLuckyUser { 

   //perform some calculation...    return _user; }Copy the code

This very common code doesn’t take into account a situation where _user is nil. You might say that the function returns nil, and that’s up to the caller to decide. Of course, if you return nil, in Objective C runtime, it’s safe to send a message to a nil object, which just means that it doesn’t crash, but it’s possible that the logic that was supposed to be executed doesn’t continue, and from that point of view, a nil object is not safe for business. And we defer the effects of nil cases until run time.

It makes more sense to consider cases like nil at compile time. That’s where optional comes in, so if we define the return value as optional, then the user of optional has to take into account scenarios where the value doesn’t exist, and if we miss scenarios where the value is nil, the compiler will report an error, so not only will it not crash, but it’s safe for the business logic.

The optional type is very similar to the sum type in Algebraic data types (Algebraic data Types, Algebraic Data types, Algebraic Data types, Algebraic Data types). When we use Algebraic data types to describe data, the language itself forces us to make exhaustive checking to consider all possibilities of data. This is another strong piece of evidence that Swift is more secure than Objective C, as Swift has absorbed many of the best features of functional programming languages.

In summary, Swift forces us to consider the possibilities of data when we write a business using optional, so that the logic of the written function is complete and comprehensive.

conclusion

There are many other examples of open and closed design ideas, such as the Final keyword in Java and the Visitor Pattern in design mode. Personally, I prefer to write this kind of random fantasy articles, imagining the connection between different technical concepts in the design idea, summarizing and consolidating. Ok, say a bunch of abstract concepts AD nag, read here have not given up the friends hard, for your patience, cheers!

Welcome to follow the public account: MrPeakTech