Toward ASP. NET architecture design-Chapter 2: Design & Test & code (previous)

Source: Internet
Author: User

Toward ASP. NET architecture design-Chapter 2: Design & Test & code (previous)

TDD is used as an example for two reasons: 1. TDD indeed presents the design ideas; 2. compared with DDD, TDD is easier to use and the learning curve is not that steep.

I would like to reiterate that this series does not describe TDD, but uses TDD to build design ideas. Even with DDD, it is sometimes used together with TDD.

 

The topics in this article are as follows:

Comparison of development methods

What is design?

Preliminary Design

 

Series of articles:

Toward ASP. NET architecture design-Chapter 1: Design

Toward ASP. NET architecture design-Chapter 2: Design & Test & code (previous)

 

Comparison of development methods

 

We use the following analysis to introduce the content of today:

Think about how we write code:

Use requirements, analyze features, and write functional code.

There is no problem with this method, and we have been using it for many years. For the convenience described later, we call this methodTraditional Process.

 

How to do TDD:

Use requirements, analyze features, write functional test code, and write functional code.

In fact, the two processes are almost the same.

 

First, analyze the two development processes. I personally think: TDD has another process of role conversion: in our traditional process, we have been thinking about problems, analyzing, and then starting to implement it.

In TDD, after analyzing a function, we need to check whether this function is really needed from the customer's perspective (of course we are still simulating the customer, then, start encoding.

 

Next we will look at a group of analysis charts:


This process is definitely biased from obtaining and understanding the requirements to the final implementation. As shown in.


In TDDFunction TestingIn that phase, this deviation is controlled. Even if there is a deviation at the end, it is smaller.

 

 Why do we need to compare the two development methods?

First of all, in general, the traditional process is to first make basic useful things, And TDD first builds a shelf and then is doing things.

In TDD, we go straight to the function: To test the function based on requirements, and then to test the function. Click it. These functions may not be fully used yet, because there is something missing, such as a database, which we may simulate during testing. For example, when implementing a function, if this function requires database operations or network access, we want to see the final effect of the function when writing it in the traditional method, it is often debug, or make visual things out, and the attention is quickly dispersed. If you find that the requirements are not correct, you will come back before, and the cost can be imagined. The TDD method can be used to write test simulation, such as mock and stub. The focus is mainly on the business. This method is like the water wave effect: spreading from the center to the surrounding area.

What is design? 

The most important part of a software system is its core business functions. When designing a system, it must first analyze the functions and confirm that the analysis functions meet the requirements, and then find a solution for implementing the functions.With a solutionAnd then consider the technical options, complexity, scalability, maintainability, feasibility, and so on. Finally, the "design" is created. After the implementation scheme is determined, it is finally implemented ." Design is indeed a mental task.

 

Let's take a look at how to make a better design.

There are too many considerations for design. If there are many, they may be "excessive". If there are few, they may be incomplete.

Next we will use TDD to help us build some design ideas.

Before that, I would like to propose that TDD is not a test,YesDesign. If we thought that TDD was a write test, it would mean that our understanding of TDD is still "shaped.

Preliminary Design

We have said before: TDD is not a test, but more about design ideas. So why is there a good design for writing a test before writing the code? Let's try it out.

 

We know that there are many design principles in object-oriented design, such as s.o.l. I. d. Fully using these principles in the system will lead to a benign development process. Therefore, a better design should be closer to these design principles as much as possible.

 

Let's look at an example:

For exampleUser Order Management SystemThere is a requirement: when placing an order, the customer first needs to check whether there is sufficient balance in his account, then pay, and save all the paid orders. (Of course, this example is very simple. Here we just use a simple example to demonstrate the process of thinking)

 

The demand is now known, and the technical implementation is not very difficult. Just think about it, the shelf is basically coming out:


Traditional Design Methods:

 

Let's take a look at the customer class above. Most of the time, we write it like this (in fact, it is the implementation method of active record. We will talk about enterprise architecture design later ).

 

The following is the definition and implementation of the business method processorder:

 

Public void processorder (order)
{

// 1. Obtain the customer's account balance

// 2. Calculate the total price of all proudct in order

// 3. Compare the balance and total price

// 4. Save order information

}

 

The Code architecture is built up, and the Implementation idea is also available. To ensure that the business is understood correctly, we may need to communicate with customers or project teams and then code them. In the implementation of encoding, the database should be read, and the inserted data should be inserted. What should be done. After the code is written, debug is usually used to debug the code (it is not very cost-effective to write a UI for this function at the beginning). Check whether the code is running as we wish. You should have no opinion on this implementation method.

Well, some requirements have been added during order processing: If the unit price of a product exceeds 1000 in order, please notify the user.

Code changed:

 

Public void processorder (order)
{

// 1. Obtain the customer's account balance

// 2. Calculate the total price of all proudct in order

// 3. If the unit price of porudct exceeds 1000, notify the user

// 4. Compare the balance and total price

// 5. Save order information

}

Then debug, query data, insert data, deubg, and so on. Repeat the previous steps.

I don't know how everyone feels.

 

In the above example, In the first code implementation, we added some operation code of the database to judge the correctness of processorder.

For the second time, we only added some minor changes to the business process, but we still debug the process at the debugging cost and debug the data access code. That is to say, in the second time, the data operation method was not changed, but the process was changed. However, to judge the correctness of the processorder method, we still finished the entire debug process.

 

If you add new requirements to the order processing process again, this method will quickly expand (we may separate the entire method into some small sub-methods), and the debugging cost will increase, in addition, code with good functions, such as data access code, is often debugged repeatedly, and the time spent on debugging is also increasing.

Some may think this is not a problem. The example is very simple. If a project with more complex services has many functions like this, what will happen at the end of the project?

 

Next we will use the TDD design idea to implement it, and then we will compare it ourselves:

First, the requirement analysis is the same as before.

The next step is to confirm the understanding of the requirement (or the same as before ).

Finally, write the test code as needed.

In fact, there are two problems:

1. What parts of the system need to write test code?

2. How to Write test code for this requirement?

 

1. What parts of the system need to write test code?

I have seen some projects developed using TDD: almost every method has corresponding test code, and the test result is displayed through debug when the test code is written, it is similar to implementing functional code and then debugging.

In fact, testing has a coverage problem. Coverage is: the percentage of functional code with test code in all functions in the system. For example, if the system has 100 functions and 30 functions write test code, the coverage rate is 30%.

Of course, 100% of the coverage rate is good, but it is neither realistic nor necessary.In general, you need to write test code for the core business process of the system, and then write some test code for the areas you think may have problems, to test if the introduction of changes, this part of the function is good. The coverage rate is generally70-80%It's reasonable, but it depends on the situation.

2. How to Write test code for this requirement?

Test code can be written, but writing good test code is not that easy. First, when writing the test code, you have to look at the user's perspective to see whether the function is correct, regardless of how the internal logic is implemented-only the results, not the process, design the test code with this idea. An inappropriate analogy: The test code is like a parent who looks at Cheng chengfeng, and sends smart children to school for training, maybe the school asks a famous teacher to teach the course, whether to study through competitions or in other ways. The parent will not care about it. In the end, if the child becomes talented, it means that your school is competent. Otherwise, the school won't work.

 

We started to write the test code. We started to focus only on the business process.

(If there is no above class diagram, we will re-design it, because the class diagram between them is used to describe the traditional design method. Forget the class diagram above)

Our test code may be written as follows:

 

Public void test_orderprocecss_is_executed_successfully ()
{

Customer customer = new customer ();

Order order = New Order ();

//.....

// Add some products to order

//...

Customer. processorder (order );

}

In this case, the compilation will certainly report an error: because we do not have these classes in the system. Then we add the corresponding code, which is compiled.

We designed the most direct customer class and tried not to write additional code:


Another problem is:

The above test code does not seem to reflect any results. How can we test it?

These problems occur when you start writing tests. Now we have to consider our previous example of "sending parents to school. Here, if the system order processing is successful, it will be told: OK, it is successful, otherwise it will be said to fail.

The test code is changed to the following:

 

Public void test_orderprocecss_is_executed_successfully ()
{

Customer customer = new customer ();

Order order = New Order ();

//.....

// Add some products to order

//...

Bool issuucess = Customer. processorder (order );

Assert. isequal (issuucess, true );

}

OK. This is the basic test code. (Of course there are some shortcomings. We will gradually improve the process of thinking later)

 

Next we will make the test code pass.

Our focus is on business processes, regardless of how data is obtained and where it is obtained, so as to avoid distracting attention.

The processorder method is implemented as follows:

The process is as follows:

 

Public void processorder (order)
{

// 1. Obtain the customer's account balance

// 2. Calculate the total price of all proudct in order

// 3. Compare the balance and total price

// 4. Save order information

}

Implemented pseudo code:

 

Public void processorder (order)
{

// 1. Obtain the customer's account balance
Decimal despoit = get the balance information from a place, no matter where you get it, just get it.
// 2. Calculate the total price of all proudct in order // 3. Compare the balance and total price
// 4. Save order information
Xxx. Save (order); save order, no matter how it is saved, just save it.

}

It may be a bit strange to see the above Code. Because processorder is a business process, it should only focus on how the process is processed. If you want data, find a place to take it, save the data, and find something to save, no matter how it is queried or saved. Let's review the previous "how schools teach children ".

 

Note that: Our current focus is on the correctness of business processes. Where data comes from is not important.

Now we just want to run the business process. We have designed all the data used for testing. Even if the data comes from the database and comes with the data, it must be stored in the memory, why should we start writing so much data access code now? Instead, we should directly use the data in the memory to allow the process to run smoothly and then slowly Replace the data access code.

 

Well, since the data is determined to be taken from the memory, to put it bluntly, it is hard code for a few pieces of data. If we put the data retrieval method in the customer, it is just like the traditional design. In fact, there is a problem: At this time, we put the data access code in it and the streaming passed. Then we replaced the hard code with the real database operation code, the process is complete. If a new processing process is added to processorder, we add the code and run the test. If the test fails, the business process fails, or Data Access Code failure? Do I need to debug it? If debug is required, what is the purpose of the tested code? Do not test it at the beginning. debug it directly. There are two reasons why the test code fails.

 

Therefore, there is a very important principle: there can be only one reason for failure in a test method. Otherwise, debug analysis is required for each test run, which causes the failure.

Therefore, an important design principle should be used:

S -- single responsibility principle (SRP)

This is what we often say"Single Responsibility Principle". It is easy to understand: each object has only one factor that changes it. That is to say, each object only pays attention to one or more functions, do not mix a lot of different functions in a class.

However, the above class design strictly violates the SRP principle. Because of the two functions above: saving business class information and taking charge of persistent data.

If you need to add or modify some data access methods, this class must be constantly modified. Similarly, changes to business processes also change the classes where the data access code is stored, the changed points should be separated.

Customerrepository is used to persist the data of the customer service class. In this way, the change is separated by the SRP principle.

In this case, the processorder method runs the test again after a new processing flow is added. If the test fails, the Process Code is faulty. In addition, customerrepository hides the data source, which is almost unchanged.

In fact, in our traditional design method, the "Desire" for "single responsibility" is not very obvious, because if there is a problem with the change process, debug should look at it; when TDD was added to the test code, the design that put the Business Process Code and data access together made the test code feel a little confused: is it a process issue or something else? Therefore, the "Desire" for "single responsibility" is a little stronger. In this way, at least a little improvement can be made during the design, which means a little "drive a good design. What do you think?

In fact, the "single responsibility" is not only used in the design class, but also has reference value in the design class method. It is impossible to design n of a method. Finally, we need to write something about TDD:

In fact, the test above is not well written, because the test is successful and the test fails. We cannot change the test code to replace the data every time. It is better to design two testing methods directly, as shown below:

Public void test_orderprocecss _ executed_successfully_with_validatedata ()

Public void test_orderprocecss _ executed_failed_with_invalidatedata ()

Do not access external resources such as databases and web services in the unit test code. For example, in the customerrepository above, when we use it to participate in the unit test, we directly put the data hard code. Unit Tests are often run. If external resources are used and testing fails due to network problems, it is easy to confuse people: it is unclear that a function fails, or another reason.

Let's talk about the specifics later!

 

I want to try my best to explain the process of thinking in plain words, so it seems so embarrassing! I don't know how everyone feels! Hope you can give feedback!

Author: Xiaoyang, Yan yangtian
Source: http://yanyangtian.cnblogs.com/


 


 





 






Related Article

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.