Understandable: Why can we understand hundreds of thousands of lines of code while reading hundreds of thousands of novels?
-- Objects are principally about people and their mental models, not polymorphism, coupling and cohesion
Difficult to understand code is a critical problem in the software industry. many methods and methodologies are committed to solving this problem, whether subjective or objective. there are many reasons for difficulties in understanding. One of the reasons we discuss today is that business processes are broken down in code.
The extended question of this reason is: how to organize business processes in the code? The debate on this issue never stops:
- Transaction script vs. Domain Model
- Competition between Anemia Model and congestion Model
- Discussion on service waste storage
The reason for the debate is the essence: oo is longer than the structure, rather than capturing behavior. when dividing the world into multiple objects, oo also disperses behavior. To understand an interaction, I need to jump between different methods of different objects. discontinuous space. sometimes callback and Asynchronization are used, and the time is not continuous.
Try
Let's try to get out of the scope of the software and look for ideas in a wider range. For example, why are there complicated relationships and plots between novels and movies that we can easily understand? Is it related to the mental model when people understand things? If we can find the mental model that humans understand, and then write the code that conforms to the mental model, will it be more understandable? Following this idea, we have obtained a tentative solution: breaking the world into data, context, and interaction, or DCI for short.
Let's try to deduce it from the beginning.
The first question: When we miss the beginning and start watching a movie in the middle, a person on the screen is doing something. What should we do and ask?
- Who is he?
- What does he do?
- What is he doing?
This is the mental model that we understand in a movie script or novel: the character, role identity, and then scene after scene. for example, the leeching team in the movie <leeching space inception> is as follows:
- The extractor)
- The dream ect)
- The point man (scout)
- The forger)
- The tourist (traveler)
- The Chemist (Pharmacist)
The most critical step for leeching is to go back to the previous layer or reality at the right time. The film is called kick. So where does kick () Put this operation? Everyone can kick. at this time, we will think of a role called dreamer. Kick should be a dreamer operation, and anyone can assume dreamer in a specific scenario.
Class dreamer {
Void kick ();
}
Data
Let's take a look at an example that is a little closer to software development: transfer money.
Assume that the domain model of the savings account is a class called savingaccount, which encapsulates attributes such as account balance. we have at least two options for how to use it to support transfers, such as withdrawals and deposits: we only use it to encapsulate simple addition and subtraction operations, or is the entire transfer process encapsulated? That is, in the following code, decrease and withdraw must be selected.
Class savingaccount
{
Private amount balance;
Void decrease (amount ){...}
Void withdraw (amount ){...}
}
From the perspective of the business scope, the required knowledge and dependencies:
- The decrease operation only involves amount. The required knowledge is nothing more than addition and subtraction in mathematics.
- Withdraw does not only subtract the amount of balance, but also involves transaction semantics, user interaction, recovery, error processing, system logs, and business rules, such as the amount of withdrawal.The savingaccount class cannot complete all operations.
A savings account is a relatively stable business concept, so which of the simple decrease and complex withdraw can better match the stability of the savingaccount?
- Decrease is very stable. It involves only mathematical addition and subtraction operations.
- However, withdraw is more likely to change. Changes in infrastructure error handling or rules such as withdrawal quotas will lead to changes in withdrawals.
The data model is relatively stable, so here we choose to use savingaccount to express the data model. There is only a simple data operation method such as decease.
Class savingaccount
{
Private amount balance;
Void decrease (amount) {balance-= amount ;}
Void increase (amount) {balance + = amount ;}
}
Role + Interaction
So the question is, where is the real transfer operation transfer? The answer to DCI is explicit modeling,Interaction
Interaction naturally involves role. In fact, a role is defined by a specific interaction. if you do not want to teach, the title of teacher is meaningless. if you do not talk to the customer, the role Ba does not make any sense. in other words, as long as you are doing business analysis and demand analysis, you are BA at this moment.
So what role does transfer involve? Source account and destination account.
Transfer (sourceaccount source, destinationaccount destination, amount)
{
Source. decrease (amount );
Destination. Increase (amount );
...
}
Context
The last question: who should specify who plays what role? The answer to DCI is context.
Class transfercontext {
Void transfer (savingaccount source, savingaccount destination, amount)
{
VaR sourceaccount = cast <sourceaccount> (source );
VaR destinationaccount = cast <destinationaccount> (destination );
Transfer (sourceaccount, destinationaccount, amount); // new transferinteraction (XXX). Transfer ();
}
}
DCI
- Data: what the system is. (Static, structure)
- Role + Interaction: what the system does. (dynamic, behavior)
- Context: Mapping the data to role, trigger the interaction. (The Director)
Inference 1, split! Remove the behavior.
What? OO isn't it necessary to encapsulate data and behavior? Let's take a look at Gu. originally, oo said that data and behavior should be encapsulated. the problem solved is the hidden bugs caused by the inability to fully control data access, and the difficulties in understanding the missing concepts. however, this does not mean to encapsulate all the data and behavior without discrimination, and encapsulate the behavior involving a piece of data. in fact, we have made too many packages without careful analysis. It is time to split the data and inappropriate behavior,The principle of splitting is stability and application scenarios.
Inference 2: class methods should only operate on their own data, and method parameters should only be basic types or their own member types.
When you find that the objects of two classes interact and put the interaction on either side will violate the above principles, define an interactive class so that all three classes meet the above principles.