Introduction to OOP

Source: Internet
Author: User

What is data?

Programmers are dealing with data every day. But can we answer this question clearly: What is data? It is no exaggeration to say that this seemingly simple question is crucial in understanding OOP ideas. Let's first look at the essence of data from an integer example: integer...,-, 1, 2,... what is the essence? First, our intuitive understanding of the data comes from its symbol indicating "0", "1", "2", etc, however, it is easy to realize that it is meaningless to regard number 1 as a symbol "1". The meaning of a symbol cannot be separated from its environment and exists independently. The environment here is an integer operation. We must evaluate its nature from the operation. 1 as an integer has more meaning than the symbol "1" (Note: The symbol here is not a string in the computer, but a mark ). In addition to visible and intuitive symbols, the data contains related operations and operation rules. They are organically related to the whole. Let's give this whole a name:Type). A mathematical language defines a symbolic set and an arithmetic rule set. A symbolic set includes an Operator and an Operator, which is recorded as follows:Type = (SymbolSet, RuleSet). For integer type integer, SymbolSet = {..., "-1", "0", "1", "2 ,",...} other {"=", "<", ">", "+", "-", "*", "/", "%", "^ "}, ruleSet = Integer Operation Rule Set (addition combination law, exchange law, addition and subtraction inverse operation, multiplication and addition relationship, etc ).


In order to distinguish from non-typed symbols and emphasize the data type, we call data a typeObject)For example, 1 is an integer object. The object here is not an example of the OOP language class, but a mathematical concept.


Type immutability

An interesting phenomenon is that in type definition, the selection of a symbolic set is not that important. If we do not use Arabic numerals, but use Roman numerals to represent integers, the nature of integers does not change. FamousChurch Numberals)Use the lambda function that maps any other function f to its n-function combination to represent the natural number n:

0Extends λ f. λ x. x

1Extends λ f. λ x. f x

2Extends λ f. λ x. f (f x)

3Extends λ f. λ x. f (f x ))

...

NExtends λ f. λ x. fNX

Addition FunctionPlus(M,N) =M+NUsing the EquationsF(M+N)(X) =FM(FN(X)).

PlusExtends λ m. λ n. λ f. λ x. m f (n f x)

Multiplication functionTimes(M,N) =M*NUsing the EquationsF(M*N) = (FM)N.

MultExtends λ m. λ n. λ f. n (m f)

Exponential functionsExp(M,N) =MNGiven directly by the definition of the number of chunch.

ExpComment λ m. λ n. n m


It seems difficult to use a function to define a natural number. In fact, as long as you understand the nature of data, it is not difficult to understand that a function is also data. Even Function notation is closer to the essence of data than Arabic numerals, because function symbols are regular and contain computation, which reflects the organic connection between data and computation.


The above example is to illustrate that the selection of a symbolic set is not so important. When different symbolic sets are used, the relationship between symbols remains unchanged. In this case, the change is the essence of the type, we call it the type specification orType Invariant). For example, first-in-first-out (FILO) is the immutability of stack type. It is a constraint for the stack object push and pop operations. The essence of stack is not how to implement push/pop, but whether the relationship between the pressure stack and the stack maintains FILO. FILO is not easy to formally express like the Four arithmetic operations of the integer type, but is equivalent to the following formal representation:

{Push (S,X);V← Pop (S)} Is equivalent {VBytesX}

That is, for two consecutive operations, "1. Press x into the stack S; 2. Play the stack and assign it to V." The effect is equivalent to "Assign x to V ". This formal expression is equivalent to the normative expression of FILO.


Encapsulation and data abstraction

The stack example introduces another topic. If a stack is implemented in C language, we usually define a structure Stack to store data, and implement the push and pop function operation data respectively to implement the pressure stack and the elastic Stack. Our question is: how should we write unit test cases for this stack? One idea is to write test cases for push and pop respectively:


Test Solution 1:

/* C Language */

Void test_push (){

Stack * pStack = create_stack (); // create a struct stack

Push (pStack, 1 );

ASSERT_EQUAL (1, pStack-> items [0]);/* Check status */

Push (pStack, 2 );

ASSERT_EQUAL (2, pStack-> items [1]);/* Check status */

}

Void test_pop (){

Stack * pStack = create_stack ();/* Create a struct stack */

PStack-> size = 2;

PStack-> items [0] = 1;

PStack-> items [1] = 2;/* data preparation */

 

Int item2 = pop (pStack ));

ASSERT_EQUAL (2, item2 );

Int item2 = pop (pStack ));

ASSERT_EQUAL (2, item2 );

}

In contrast, another idea is to combine push and pop to test FILO:

Test Solution 2:

/* C Language */

Void test_FILO (){

Stack * pStack = create_stack ();

Push (pStack, 1 );

Push (pStack, 2 );
Int item2 = pop (pStack ));
ASSERT_EQUAL (2, item2);/* Check FILO */
Int item1 = pop (pStack );

ASSERT_EQUAL (1, item2);/* Check FILO */

}

Although this is a C language, the difference between process thinking and OO thinking is fully reflected. If you write test cases similar to solution 1, you are thinking about the process; otherwise, if you write solution 2, although the C language is used, but you already have some OO ideas! Procedure and ObjectProgramming Paradigm)Two different programming ideas are embodied. The process is mainly process abstraction. the problem to be solved is abstracted into a process, and then decomposed into several sub-processes for separation and governance. The bricks of a procedural Program are processes. Therefore, the test solution 1 with a single function as the basic unit reflects the process thinking. Objects are mainly data abstraction, which maps the problems to be solved to the type definition. objects are the bricks of the program. Test Solution 2 noticed that the push and pop functions are cohesive under the immutability of the stack type, so we say it reflects the OO thinking. Just as if we see integer 1, we naturally think that it can participate in addition and subtraction operations, and addition and subtraction operations are inverse operations. When we see stack objects, we should think that we can push and pop operations on them, the relationship between push and pop is FILO. It can be seen that the OO thinking is not reflected in the use of the OOP language, whether there is a defined class, but whether there is a type consciousness.


Type is a mathematical concept that emphasizes semantics. Class (Struct) is the syntax mechanism provided by OOP for defining types.In terms of semantics, a class is definedAbstract Data Type)This process is calledData Abstraction action)In syntax, a class defines external interfaces and hides internal implementations. This process is calledEncapsulation). Note: Class is usually used to defineReference Type), Struct is usually used to defineValue Type)The differences between the two are not described in detail here. The "Class" in this article includes the Class and Struct.


To define the stack type, we can use C ++ to write the Stack class declaration:

// C ++

// Stack. h

Class Stack {

Public:

Stack ();

~ Stack ();

Public:

Push (int I );

Int pop ();

Private:

Std: vector <int> items;

}

Stack. h is a declaration of the Stack class. It defines the syntax features of the Stack class external interface. The user will directly deal with Stack. h without seeing the implementation of Stack. cpp. Therefore, class declarations and header files are also an abstract mechanism. However, apart from defining type-related operations in syntax, the type semantics must also be met. We can use a unit test case to represent the customer to express the expectations of the Stack class to meet FILO:

// C ++

Void test_FILO (){

Stack stack;


Push (stack, 1 );

Push (stack, 2 );



Int item2 = stack. pop ();
ASSERT_EQUAL (2, item2);/* Check FILO */
Int item1 = stack. pop ();

ASSERT_EQUAL (1, item2);/* Check FILO */

}

Combined with the Stack class declaration and unit test, the stack type data is abstracted from syntax and semantics.As for the specific implementation, we can have a variety of different implementations, as long as the syntax conforms to the Stack class declaration, the semantics conforms to the FILO. Strictly speaking, the class declaration mechanism of c ++ header files is not perfect. The problem with header files is that, although it is intended for users and users do not need to know about private content, class declarations require private content, which is equivalent to "deactivating ". For example, if you get the Stack. h above and see the private std: vector <int> items, you will know that the Stack is implemented based on vector. For example:

// C ++

// Stack. cpp

Class Stack {

Public:

Stack (){}

~ Stack (){}

Public:

Push (int I) {items. push_back (I );}

Int pop (

Int I = items. back ();

Items. pop_back ();

Return I;

);

Private:

Std: vector <int> items;


}

 

C ++Pimp usageOne of its functions is to solve the problem of exposing internal implementations of class declarations, so that class declarations can block things that users should not and do not need to see:

// C ++

// Stack. h

Class Stack {

Public:

Stack ();

~ Stack ();

Public:

Push (int I );

Int pop ();

Private:

Class StackImpl; // StackImpl class declaration

StackImpl * pStackImpl;

}
The process of data abstraction includes two parts: the interface syntax and semantic specification of the class, and the specific implementation of the class. As a result, the category has two sides: the face-to-face is relatively fixed for users, and the face-to-reality is relatively easy to change. Variable overcast SurfaceLocality)Inside the class, stable Yang is safely used everywhere. The design of the positive plane belongs to the scope of software design, and the implementation of the negative plane belongs to the scope of algorithms and data structures. This change and change reflectInformation Hiding)Principle, but the principle of information hiding is a general principle. All abstractions belong to Information Hiding. Special attention must be paid to the type integrity immutability for data abstraction.

TDD

The preceding class declaration and test cases are implemented in combination with the popularTest-driven development method (TDD). To a certain extent, TDD promotes programmers to think about behaviors in positive ways and naturally obtain testability and high code coverage. In general, TDD can lead to a good design, but I have also seen (mistaken) a worse case with TDD. Let's look at when TDD makes the design worse. For example, for the above stack requirements, some people use TDD small-step iteration to write the case:

// C ++

Void test_push (){

Stack stack;

Stack. push (1 );

}

At this time, the programmer found that he could not verify whether the push method was successful.Dependency Injection)Decouples the vector from the stack and checks the vector status. Therefore, the test driver becomes:

// C ++

Void test_push (){

Std: vector <int> items;

Stack stack (items); // constructor dependency Injection

Stack. push (1 );

ASSERT_EQUAL (1, items [0]); // check the status

}

Dependency injection and TDD are all used in popular software ideas, but where does it seem to have seen such code? That's right. Isn't it essentially the same as the test case for each function in C language? Haha! If we say that the previous friend who used C language to write the FILO test in combination with push and pop is "wise and wise", we can only give this brother a "wise and wise. A design that uses dependency injection, decoupling, and other "advanced" software ideas is essentially process-oriented. We didn't say process orientation is not good, but I'm afraid this friend who liked the idea of advanced software didn't realize that he was writing a process-oriented program ?! It can be seen that the key to design is not whether we are using a certain language, or even whether you are using something that sounds fashionable, but whether you have a deep understanding of the nature of programming.


"High Cohesion and low coupling" are two aspects of software. ABSTRACT Data Types first reflect a high cohesion, such as: push and pop belong to FILOFunction Cohesion). Coupling refers to the relationship between objects. UML mainly distinguishes between dependency, association, aggregation, and combination, generally, only the first three relationships are suitable for low coupling through dependency injection (refer to my previous article ). At present, the development community seems to overemphasize "low coupling" and ignore "high cohesion", leading to many "decoupling", which is a big misunderstanding of OOP. In the final analysis, only by grasping the essence of things and achieving the high cohesion of high cohesion, the low coupling and low coupling can be regarded as high-quality software design.


Summary

This article is a thought guide for beginners of OOP and a summary of my own learning stages. OOP and program design are profound and require constant learning, thinking, and exploration. I warmly welcome my friends to criticize and correct the mistakes and shortcomings in this article. I hope to make progress together with you!


References

Zheng Hui, colon classroom-programming paradigm and OOP Thoughts
Babara Liskov, Data processing action and Hierarchy

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.