Record some of my knowledge about software design . Before we start , Let's start with two concepts The goal is
and The way
( There may be a little bit of rhetoric here , But mainly to distinguish between subjective and objective some subtle differences ).
1 Goals and approaches
When we're doing something , There will always be a certain purpose : For example, three meals a day , It's to give the body the energy it needs . So how to implement these three meals , There will be a variety of ways . For example, you can choose to eat carbon water food 、 vegetables 、 meat 、 Milk or eggs and so on ; You can also choose to inject some of the glucose or protein you need intravenously . All in all , Just be able to replenish energy for your body .
1.1 The goal is
So in the small example above , our Purpose
It's to give the body energy , Used to maintain normal life activities . Of course, it can also be said that it is our The goal is
, however The goal is
Focus on the process , The purpose is to emphasize the result .
1.2 The way
As can be seen from the above example, there are many ways to achieve the above mentioned Purpose
. Each of them is a way to achieve Purpose
Of The way
, Of course, in order to replenish the balanced energy , It's usually a combination of several different foods , I call this a means or a method . methods
and Method
With a certain degree of subjectivity ; and The way
It's a way of describing objective alternatives .
2 The purpose of software
Before we start talking about software design, ask yourself a basic question : Why do we need software ?
The author thinks that it exists to solve the related problems in a certain field in reality . It's like the original computer was used to calculate the trajectory of a missile . Common in life QQ And wechat is to meet people's social communication needs , Taobao Jingdong, etc. is to meet the needs of people to buy and buy .
therefore , The purpose of software is that it can solve problems in some fields , That's the only reason it exists .
For example, in the movie Matrix , Programs that are no longer in use have only one end , That's to delete .
3 The goal of software design
If you had software in the beginning , In fact, it doesn't matter whether you want software design or not . But the problem is that software doesn't come out of thin air , Not from 0 To 1 There is no intermediate process to get the desired software directly . In software from 0 To 1 The process of , It's the scope of software design ( So here I use the concept of the goal of software design ). Because the purpose of software is that it can solve problems in some fields , So first of all, the minimum requirement for software is that it can be used , Can be used to solve problems . For example, a mathematical addition, subtraction, multiplication and division calculator , The minimum requirement is that you can calculate the result, right . So what is the goal of software design ? I think it is to control this from 0 To 1 The process of , To avoid getting out of control ( Once out of control, you may not even meet the minimum requirements of software ).
《 Domain-driven design : How to deal with the complexity of software core 》 That's what the subtitle of a Book means . It focuses on how to deal with the complexity of software itself in an object-oriented way , To avoid getting out of control .
So the author's cognition of the goal of software design is : To avoid software out of control . Why the goal, not the goal ? Because software design exists in the whole life cycle of software , It's an ongoing process , Until the day the software is no longer in use ; Not just in the beginning , The follow-up remains unchanged .
4 The root cause of the loss of control
The goal of software design is To avoid software out of control . So what is the cause of Out of control ? The business you're facing is too complex ? The legacy code of the project is too bad ? The level of team members is uneven ? The time limit is too tight to make design plan ? Maybe , These are more or less facts that already exist .
- Is the complexity of the business the reason it's out of control ? Think about it The purpose of software What is it? ? Solve problems in some areas , So can we make the complexity of the business disappear or decrease ? The answer is yes , Can't ! There are people here who want to say you fart ... You dare say we can't reduce the complexity of our business , Hit you oh . If you kill me, the complexity won't decrease ,,, Complexity is an objective attribute of business itself , It will not be changed by human will , Unless you stop doing it . It's like you're going to buy a mobile phone on Taobao , You are in Beijing , The seller is in Guangzhou , No matter what kind of express delivery you use , The time spent on the physical distance from Guangzhou to Beijing cannot be eliminated . You said you were in a hurry , Good. , The seller gives you the choice of air transportation , Soon you get the goods . You said air transportation doesn't reduce the delivery time , Isn't it the same as reducing complexity ? It's not , Because complexity means No matter what kind of express delivery you use , The time spent on the physical distance from Guangzhou to Beijing cannot be eliminated , It's a process that you can't eliminate . But it's weird, right ? Yes , It looks strange , Obviously, the time for me to receive the goods has been shortened , How does complexity not change ? So here's another concept : The impact of the way business interacts . The impact is very big , But it's often overlooked by us , For example, you choose to buy the goods from the seller in Beijing , Is it possible that the time has been greatly shortened ? It's also true in business , The complexity of the business itself , And the impact of the way we interact when we transform our business into software , The complexity of the business itself cannot be reduced or eliminated , But the latter interaction mode can be controlled , It's also part of software design , So in fact, we choose air transportation to change this part . It's like you're a B/S Application software of , Your users see Web page . Behind this Web The process of the page from the server to the user's browser and the process of the browser rendering the page can't be eliminated in any case , But browsers can cache it , The next time you open this page , It can eliminate the interaction process mentioned above .
- Is the legacy code of the project too rotten to be out of control ? It's not , It's a manifestation of being out of control .
- Is the uneven level of team members the reason for out of control ? Neither , Although this is an objective fact , But it's not appropriate for you to put the blame on your teammates , Maybe that's what your teammates think of you .
- Is it out of control that you have no time to do design planning because of the tight schedule ? Of course not , This is an excuse ... It's like you're getting up late today , Would you choose to go out naked and naked ?
In addition to some of the above facts , Of course, there are other factors , It doesn't seem like the culprit for the runaway . So what exactly caused the loss of control ??? Think about it , What is the usual scenario when we feel that the project is out of control ?
- There's a known bug, When you change it, you find that there are too many things involved , Pull one hair and move the whole body , You don't dare to do it . You think the code is out of control ...
- There's an unknown one bug, You've been looking for a long time and can't find , The code is too messy . You feel a sense of powerlessness ...
- Here's a new feature , You find that you're going to change here and there , But I don't know if it will destroy the existing function , I don't know if the new function can really work. You feel like you can't control the code ...
- There are other situations , All in all, you feel like you can't control the actual behavior of the code , You don't know what kind of results your code will produce , Like Schrodinger's code ...
So there's another scene , When you start a new project , Everything is new , There is no historical debt burden , How do you feel at this time ? Full of confidence. It must be , At this point, you don't feel like you're going to lose control of the code that comes down , Because you don't have a single line of code ...
So what caused it to get out of control ? The existing inability to maintain (bug、 The new features are maintenance ) Out of control caused by code for , And it's also the result of being out of control . So why do you Unable to maintain These codes , Because there is a deviation between what it really does and what you understand , You think it's out of control . This is really out of control , Whether the code is rotten is not the point , As long as you can maintain , None of this is a problem .
The code will only be executed according to the behavior you write , Instead of acting according to what you think .
So how to avoid getting out of control ? Write maintainable code . Beat you to death , Explain that such a long time to suppress such a nonsense , Who doesn't know to write maintainable code ...
I can only say don't worry , Keep looking down ...
5 The goal is - Maintainability
Since our goal is to avoid getting out of control , The way to avoid getting out of control is Write maintainable code . Then I'll put Maintainability As the ultimate goal of software design , And not one of them . Also known as Meta principle , That is to say, the respective programming principles we are currently exposed to 、 Suggestions, best practices, etc. can be passed through Maintainability Deduce and refine , And you can't go against it .
Make a comparison , Just as the constitution is the basis of all other laws , If any law goes against the Constitution , So it's invalid .
So according to Maintainability It can be deduced that 3 A core principle : Comprehensibility 、 ...Testability and Isolation .
5.1 Comprehensibility
This principle seems to be very subjective , But it's not .
For example, you just wrote a piece of code , You find it easy to understand , He doesn't look easy to understand ; Or he wrote code , He seems easy to understand , But you can't understand his thinking all at once , And then you find it hard to understand . If this happens , Then it's all incomprehensible . Now you have to say : You're going to shoot both sides with one stick . Yes , It is so . Think back to what our goal is ? Maintainability ! Maintenance here is not just about maintaining your code , It's about people crossing each other ; You've added a feature , I'm in charge of other things , Then it's up to your teammates to take care of it ; Or you take over to maintain someone else's code .
So we need an objective Comprehensibility . So what is objectivity ? It's impossible to measure ! It's not complicated , When you read a piece of code , Do you need extra thinking , The extra mental context is maintained to understand the intent of the code , if necessary , So it's incomprehensible , At least it's not easy to understand . To put it more simply, this code should let you understand its intention without thinking about it . For example, here's a little example , Functions are completely equivalent , But the difference is very subtle .
// 1
if(userList.isNotEmpty()){
}
// 2
if(userList.isEmpty() == false){
}
// 3
if(!userList.isEmpty()){
}
// 4
if(userList.length() != 0){
}
What do you think of comprehensibility ? The answer is yes ?1 > 2 > 3 > 4
.
- 1 Is it that you don't have to think at all , Just read it and you'll know what it means ?
- 2 There is one
==fasle
The process of , It requires you to think simply . - 3 It's close to 2, But compared to 2 It's worse , Because the reverse sign is in front of , But the decisive value is behind , And your reading order is from left to right , So you need a ratio 2 A little more complicated thinking process .
- The first three can be seen at a glance empty perhaps Non empty Context of , however 4 It's even worse ,4 The length is not equal to 0, Logically, it's actually the same as Non empty It is equivalent. , But you need to do this mapping in your brain length !=0 Equate to Non empty , The abstraction level of this is obviously one level lower .
I don't know if I can appreciate the subtle difference . So do you think these understandings are objective or subjective ?
5.2 ...Testability
Intelligibility ensures that you can quickly understand the intent of existing code , But its real behavior ? Is it consistent with what you think behavior is ? I said above :“ The code will only be executed according to the behavior you write , Instead of acting according to what you think ”.
So how to make sure that your real behavior is consistent with what you think you're doing ? That's it test . Code what you think of as behavior , To verify that when your business code is executed, it will get the desired output according to the input you give . With the help of automation CI, You can run all existing tests every time you change the code , Then you can at least get 3 A little profit :
- Code really does what you think it does .
- Make sure your changes don't break existing code behavior .
- Force your code to be reasonably decomposed and abstracted , Otherwise, it's hard to write effective tests .
Of course, you may have written the test wrong ,,, The probability is much smaller . What's more, suppose you really write the wrong test , Time is long. , This mistake becomes feature. Why? ? Maybe the consumer of your code has dealt with it according to its actual behavior , At this time, you rashly put this bug Repair the , As a result, consumers can't work normally . At this time, the wrong test actually becomes a kind of contract test of the consumer side . Make sure you don't correct it ,,,
such as C# In the class library of
DateTime
, There's a lot of weird behavior in dealing with time zones , At this time, Microsoft can't fix it , We have to add a new oneDateTimeOffset
, The two coexist , Slowly move past .
5.3 Isolation
So now you can quickly understand the existing code , It also ensures that your new code doesn't break existing functionality , Also make sure that your code behavior is what you think it is . Is it possible to merge the code happily and release it online ? Yes , Almost . however , There are always exceptions , We can not all hope that we can strictly implement the above two points . There's always an alternative, right ?
Isolation is one such alternative , The intention is to isolate your code behavior , Even if it's just rotting into non maintainable code , As long as it doesn't affect other modules , So it's controllable . It's like a 10000 ton ship , The bottom compartment is always independent , If one of them gets into the water, it doesn't affect the others , So as to avoid the whole out of control .
6 The way
Remember the beginning of the article The goal is and The way The concept of it. , Aforementioned 3 One principle is our goal , So what are the ways to achieve this goal ?
6.1 name
There was a saying , There are two big problems in computer science : Naming and caching failures . The importance of a good name is needless to say ? In addition, I have another experience : If you find it difficult to name , So take a look at your design from the beginning , Maybe you're going in the wrong direction . I think once there is a problem of naming difficulties , That's definitely something wrong with your design . Maybe you have too much responsibility for the method , You can't describe it clearly with a simple name , Maybe the meaning of your field is not clear , So you can't exactly describe it in a simple word .
The goal is | effect | explain |
---|---|---|
Comprehensibility | ++ | Increase readability . |
...Testability | nothing | No influence . |
Isolation | nothing | No influence . |
6.2 Single responsibility
Almost everyone understands the importance of a single responsibility , But it's easy to ignore it . For example, the following small example :
// 1
public String sum(
final Collection<BigDecimal> bigDecimalCollection
) {
final BigDecimal sumResult = bigDecimalCollection
.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add);
final DecimalFormat format = new DecimalFormat("#,##0.00");
return format.format(sumResult);
}
// 2
public BigDecimal sum(
final Collection<BigDecimal> bigDecimalCollection
) {
return bigDecimalCollection
.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
1 There's a little bit of responsibility for ?
The goal is | effect | explain |
---|---|---|
Comprehensibility | ++ | One concern makes code intelligible greatly . |
...Testability | ++ | It also makes testing easier to implement . |
Isolation | ++ | Single, single , Isn't that isolation ? |
6.3 Data model matching business
The meaning of data model matching is to let your code truly express the actual business intention , And this intention has to be implemented at the data level , Not at the code level . In short, let your data reflect your business , It's not your code that reflects your business . It's a little twisted , What the hell do you mean ? Let me give you a little example : Calculation of individual income tax
// 1
(empployee.salary - 3500) * taxRate;
// 2 employee.exemption = 3500
(empployee.salary - employee.exemption) * taxRate;
Which do you think is more suitable ?1 It's that the business is embodied in the code , Now 2019 Years. , The personal tax exemption has been raised to 5000, What would you do ? Change the code ,3500 Change to 5000 No, it's over . Yes , Done. , So what about historical data ? Someone wants to compare the difference between the old version and the old one , How to calculate ? Can't , You were forced to write two versions ,2019 A version of the code a year ago ,2019 A version later , And then the chaos started .
So what's the fundamental problem ? Because of 3500 This number doesn't look impressive , But it's part of the business itself , The result is put into the code . This is the typical data model mismatch business . This kind of detail is sometimes hard to detect at first , But once it's discovered, it may be hard to recover , Code can be changed at will , But what about the existing historical data ? The above example is good to say , You can go through the historical data and fill it in . But most of the time, the data is not recorded at first , The follow-up can't be mended in any way , Cause your code to be bound to death , No more new features can be added .
I quite agree with Linus torvalds A word of :“ Bad programmers care about code . Good programmers are concerned with data structures and their relationships .”[1].Git The data structure is very stable , Its underlying layer is actually a content addressed file system , On top of such an underlying data structure , For more than a decade Git Added n Multiple functions and commands , But it's consistently compatible ( Do you use Git Earlier versions of the initialization operation a repo, The latest version is still a perfect match ).
The goal is | effect | explain |
---|---|---|
Comprehensibility | ++ | The matching model can express the real business intention , There is no intermediate conversion link , It allows you to understand the code without additional mental burden . |
...Testability | + | Make the test more intuitive description of the real business behavior . |
Isolation | + | Reasonable model partition can effectively reduce unnecessary dependence , To be relatively independent . |
6.4 The level of abstraction
It takes a few steps to put an elephant in the refrigerator ?
- Open the refrigerator door .
- Put the elephant in .
- Close the refrigerator door .
It's that simple , All three things are at an abstract level . So refine it a little bit , It takes a few steps to open the refrigerator door ? And there are no elephants now , I'm going to get one from the zoo first , What do I do ? Are these details and the above three steps at an abstract level ? Definitely not ! But we usually do things like this a lot of the time , For example, business code is mixed with how to splice SQL Code for statement . When you read this kind of code, it's very messy , Why do you feel confused ? Because it covers different levels of abstraction code together , That's when you're thinking about how to put an elephant in your front foot , I suddenly found out that the next thing was how I could get an elephant out of the zoo . Remember the above a judgment is not empty a small piece of code ?
// 1
if(userList.isNotEmpty()){
}
// 4
if(userList.length() != 0){
}
4 It's just like that , Although it's very subtle , But it's just a little bit of different levels of abstraction mixed together , You're messing up your code , Make the comprehensibility drop sharply .
The goal is | effect | explain |
---|---|---|
Comprehensibility | ++ | Avoid distracting yourself from unnecessary details when reading code . |
...Testability | ++ | For example, I can finish the first one with an elephant's plush toy 2 Go ahead ? This greatly simplifies the focus of testing and writing . |
Isolation | ++ | It's blocking out some of the underlying details . |
6.5 Okam razor
What the hell is this ? How come the razors come out , Do you think the hairline is not high enough ? It's not , This is a simple line principle , Also known as “ If not necessary , Do not add entities. ”. That is, if there are two ways to do the same thing , Choose the simpler one with fewer assumptions .
The goal is | effect | explain |
---|---|---|
Comprehensibility | + | Choosing a simpler one helps to understand . |
...Testability | nothing | No influence . |
Isolation | nothing | No influence . |
7 Some misunderstandings
When I see this, I guess some people can't help criticizing me :
- Reusability ?GoF23 All design patterns emphasize building reusable software , Where is reusability ? You ate it .
- Reliability ? Robustness ?
- High availability ?
Wait , Just like the various indicators listed in the soft engineering course , Or various patterns and architectures and so on . It's not that these things don't matter , Or I don't approve of these things , I agree , And understand their importance . But there's one thing that needs to be made clear , What are our goals ? Which are our approaches ?
7.1 Reusability is just a phenomenon
Is reusability our goal ? My answer is : no , Our goal is software maintainability ! Then you say reuse will increase maintainability , Not really , Improper reuse can reduce maintainability , This is a double-edged sword , I borrow a word from my brother :“ The more general, the more useless ”. So you say it's not a goal but also a way ! So my answer is : It's not the way , Your approach may be unconstitutional , Do you think it's suitable ? It's not the goal , It's not the way , So what is it ? answer : It's just a phenomenon , If you implement the above 5 Some of the ways , You will find that your code can be reused naturally .
7.2 Design patterns come from defects
First, let's take a look at what design patterns are : “ It's a set that's been used over and over again 、 Most people know that 、 Catalogued 、 Summary of code design experience . Design patterns are used for reusable code 、 Make code easier to understand 、 Ensure code reliability 、 Reusability of program .” That is to say, it's empirical code with proven best practices . So here comes the question , When best practices are needed ?, When you get confused about the tools you use , When I don't know what to do with it , You need to learn from other people's better solutions to complete your work . This solution , It's design patterns . Now think about it ,GoF23 The design pattern is to make up for the defects of what ?OO ah , The subtitle is “ The foundation of reusable object-oriented software ”.
Of course, design patterns are not OO Something exclusive to , Those that have been named best practices , Can be called design patterns .
7.3 OOP Is not the goal
A lot of times when we talk about code , It's uncomfortable looking at the code , They put on a hat to each other when they didn't agree , Your code is not at all OO! It doesn't have to be ,OO To solve some problems , But it doesn't solve all the problems , There are so many static Class or method of , it OO Do you ?OO It's just a way to solve our problems , It's not the only way , Never use a tool as an end .
7.4 DDD There are more problems to be solved than problems to be solved
DDD Since its birth, it has faced a lot of controversy .DDD The starting point is very good ( How to deal with the complexity of software core ).DDD Is based on OO, stay OO A lot of concepts have been extended on it , I hope to make the most of OO The advantages of . But there are too many extended concepts , And a thousand people, thousands of faces , Everyone's understanding is different , And it can be said that there are all kinds of different ways , That makes it It's very difficult to reach an understanding consensus in the team . It also leads to various difficulties in implementation , Even if a part of it falls in the beginning , as time goes on , It will become more and more difficult to continue , It seems that the focus is all over the place DDD The idea of ? And the focus on business becomes second-class citizens , It's a disaster , At this point, the comprehensibility of the code is very fragile . So according to the Occam razor principle , Shaving it is the best choice .
8 summary
The above is the author's thinking process about software design : The author thinks that its goal is to avoid the software out of control and the relevant measures , And some of the common concepts . If there is something wrong , Welcome to discuss .
9 quote
This article was first published in :https://linianhui.github.io/talk/objective-and-approach-of-software-design/
git actually has a simple design, with stable and reasonably well-documented data structures. In fact, I'm a huge proponent of designing your code around the data, rather than the other way around, and I think it's one of the reasons git has been fairly successful […] I will, in fact, claim that the difference between a bad programmer and a good one is whether he considers his code or his data structures more important. Bad programmers worry about the code. Good programmers worry about data structures and their relationships. Message to Git mailing list ︎