Introduction:To ensure the stability and success of the architecture, useCodeIt is a practical method to verify the architecture. The core of code verification is testing, especially unit testing. The basic operating principle of testing is testing first. It is a very important practice in Agile Methods and an important guarantee for reconstruction and stability of core models.
Code verification in the object-oriented system
Code verification is a way to ensure excellent architecture design, and also a way to avoid the emergence of ivory tower architecture design. In the previous article, we mentioned in stability that architecture design will eventually be embodied in the form of code. Therefore, it is most effective to use formal code to verify the architecture.
Because it is code verification, it is inseparable from coding, and the code is always closely related to the specific language and compiling environment. Here we mainly discuss the object-oriented language, the Java language used in the sample code. There are many advantages of using object-oriented languages for Architecture Design:
- First, object-oriented language is a better structured language. Compared with non-object-oriented languages, it can better implement encapsulation, reduce coupling, and allow designers to think at an abstract level. These factors provide conditions for excellent architecture design.
- Second, the object-oriented language allows designers to focus only on the Framework Code, rather than the specific implementation code. Of course, this does not mean that non-object-oriented languages cannot do this, but the performance of object-oriented languages is better.
- Finally, object-oriented languages can be well reused. This means that designers can use their original knowledge and original software systems to solve new problems.
In addition, more benefits can be obtained by using the Java language. Java is an interface-oriented language. We know that the Java language itself does not support multi-integration, and all Java classes
All are inherited from the object class. In this way, it is difficult to change an inheritance system once it is determined. To achieve the flexibility of Multi-inheritance, Java introduces the interface mechanism, using interfaces and using Abstraction
There is no difference in the class. A specific class can implement multiple interfaces, and the client can use it by declaring the interface type, as shown below:
List employees = new vctor ();
If you need to replace vctor with a mirror list, you do not need to modify any other Code except the above Code. In addition to implementation, the vctor class
In addition to the List interface, cloneable, collection,
Randomaccess and serializable. This shows that in addition to the List interface, we can also access the Vector class through the interfaces listed above. Therefore, the interface can be inherited
It can be a complementary means of class inheritance and play a very flexible role. At the same time, the complexity of Multi-inheritance is avoided. However, only empty methods can be defined in the interface, which is a defect of the interface. Therefore, in actual programming
And abstract classes are usually used together. We can see the collection interface in the Java. util package of Java and
Abstractcollection abstract class is an example in this regard. You can inherit from the abstractcollection abstract class (or a subclass of it), so that you can
To use the default code in abstractcollection. Because abstractcollection implements the collection interface, your class also implements
Current Collection interface; if you do not need to use the code in abstractcollection, you can write a class by yourself to implement the collection interface.
(This is unlikely to happen in this example, because the reusability of the tool class has been designed very well ). There are many similar examples in Java. Java language design is not the focus of our discussion,
For more in-depth discussions, see special books. We will not introduce them too much here.
It took some time to discuss some simple preparations for Object-Oriented Design and interface-oriented design. This knowledge will become the basis for code verification.
Interfaces and architecture
The interface here is not the interface concept in Java. It is a generalized interface, which is embodied in the public method or interface method of the class in Java. In the com system
Or there are similar but not identical performance in the J2EE system. For a system architecture, the most important thing is to define these interfaces. These interfaces are used to associate system classes.
Provides services for users and connects external systems (such as databases and legacy systems) through interfaces ). Therefore, to verify the architecture, we need to convert it into interface verification requirements.
The basic idea of Interface Verification is to ensure the interface testability. To ensure that interfaces are testable, you must first analyze the responsibilities of classes and classes. There are several principles to improve the interface testability.
1. encapsulation principles
The Implementation Details of the interface should be encapsulated inside the class. For a class user, he only needs to know the public method released by the class, rather than the implementation details. In this way, you can write the corresponding test code according to the common method of the class. As long as the test code is satisfied, the class design is successful. For the architecture, the testability of the class is the foundation, but it is not enough to ensure it.
2. Minimum Responsibility Principle
The question of how many functions a class (Interface) needs to implement is a constant debate. However, the functions implemented by a class should be as compact as possible. A class only processes closely related functions, and a method is more
Only one thing should be done. In this way, the test code of the class is also relatively concentrated, ensuring the testability of the class. Recall the example we discussed in the layered mode. The implementation class provides different
Interface, which is also a manifestation of the Minimum principle.
3. Minimum interface Principle
You must be cautious when releasing the method to users. Generally, release methods should be as few as possible. Since published methods may be frequently used by customers, if there is a design problem or
Improvements to the design will affect existing methods. Therefore, these effects must be minimized. On the other hand, some lightweight common methods should be combined into a single method. This can reduce the number of users
The degree of coupling with the system. You can use the appearance mode or the business delegation mode. For more information, see hierarchical mode. A small number of interfaces can reduce the test workload and make the test
The trial work is more concentrated.
4. Minimum Coupling Principle
The minimum coupling principle is that the interactions between classes and other classes should be minimized. If a class has a coupling relationship with a large number of classes, a new class can be introduced to weaken the coupling. In the design module
Medium, both the mediation mode and the appearance mode are such applications. For testing, especially unit testing, the ideal situation is that the testing class is a pure class and has no relationship with other classes. But in reality
This type is rare, so what we can do is to minimize the coupling between the test class and other classes. In this way, the test code is relatively simple, and the class has little impact on the test code when it is modified.
5. Layered Principle
The layering principle is the improvement of the encapsulation principle. A system usually has various responsibilities, such as the code that is responsible for dealing with databases and the code that is used for dealing with users. Divide the code by function
For different layers, you can encapsulate different parts of the software architecture. The class testability guarantee should be developed into the architecture testability guarantee. You need to use the hierarchy principle for the system and
Do not write the test code. For more information about layering, see layering mode.
If the architecture you designed cannot meet the above principles, you can reconstruct the architecture to improve it. For details about refactoring, refer to the refactoring book Martin Fowler and Joshua.
The reconstruction of kerievsky to the pattern book.
What is the significance of a verifiable architecture if we look into it? This is the test-driven and automated testing concept mentioned in the next section.
The concept of test-driven may be unfamiliar to everyone. The same concept in RUP is test-first
In XP, test-first
Programming ). In fact, in our daily work, we are already unknowingly working on the test-driven part, but increasing the test-driven height is attributed to agile methods. Test drive
The basic idea is to consider (or write) the test code before designing (or coding). In this way, the test is not just a test, but a design (or code). Martin
Fowler is called "specification by example"
In the field of agile testing. One way is to fully express the requirement as a test code. In this way, the demand of software designers is no longer how to write requirements to capture users' needs, but how to write tests.
To capture users' needs. This has obvious advantages. The most critical code in software design is the failure to meet the requirements found in the test work. There are many reasons for this, but the results
It is terrible, and it will lead to a lot of rework. The requirements are organized into the form of test code. The final code can meet the requirements as long as it can be tested. Of course, this is certainly a prerequisite, that is, testing
The trial code must be able to describe the requirements completely and accurately. It is not easy to achieve this. We can imagine that there is basically no code or even no design drawing when we analyze the user's needs.
At this time, it is difficult to write the test code. This requires the designer to write the test code, and the overall architecture of the system has become very simple. Therefore, although this technology has bright prospects
Far from mature.
Although we cannot fully use the above technologies, it is entirely possible to borrow the ideas.
First, the idea of replacing requirements with test code is good because the test code is unambiguous and can describe the requirements very accurately (because the code level is the finest level ), and closely integrated with the architecture. Because
Therefore, from the demand analysis stage, we should try our best to maintain the testability of the requirement documents. One possible method is to use CRC technology. CRC technology can help designers analyze the key existing in requirements
Class, and find out the relationship between class responsibilities and classes. Similar technologies are also available in RUP. The Business Entity represents some entity classes in the field and defines the responsibilities and relationships of the business entity.
Testing. No matter which method, the idea is to use analysis technology to identify and refine key factors in the business field.
Secondly, the test driver believes that testing is more than just testing. More importantly, testing has become a contract. Guides design and testing. In this regard, Bertrand
Meyer has long proposed the concept of design by contract. From the smallest unit of software design, this contract actually defines the interface between the producer of the class and the consumer of the class.
Finally, if all relevant personnel in the software development team can understand the architecture test code, it will be helpful for the design, implementation, and improvement of the architecture. Here is a test engineer's role
. In general, we think that the main responsibility of testers is to identify errors. The problem is that testers spend a lot of time identifying errors that developers should not make. For the newest
Testing is undoubtedly a very important part, but if the daily work of testers is flooded with errors that can be avoided, the quality and cost of the software will be lacking. One
The testing staff of the show should focus on the availability of the software, including whether the requirements are met, whether the specifications are met, whether the design is defective, and whether the performance is good enough. Besides discovering defects (note that we use
Is a defect, not an error), The tester should also find out the cause of the defect and give a correct opinion.
Therefore, it is better to require developers to perform code-level tests on the software. Therefore, it is an effective way to improve the software quality to give the architecture test code and require that the Code pass the test. Learn more
After testing-driven thinking, let's answer the question at the end of the previous section. The biggest benefit of verifiable architectures is the ability to build an ever-improving architecture through automated testing. In the refactoring mode, we understand
To ensure the testability of the architecture, and establish a testing network for it (discussed in the next section ), this is the basic guarantee for the smooth restructuring of the architecture. We know that the basic meaning of refactoring is not
Make adjustments to the internal structure on the premise that the code or architecture's external behavior is affected. However, once the code is adjusted, it is difficult to ensure the immutability of its external behavior. Therefore, the test-driven approach is used to implement
Automated testing is the equivalent of external behaviors of the architecture. No matter how the architecture evolves, as long as the test passes, the external behaviors of the architecture remain unchanged.
As in the previous article, the interface concept is still a generalized interface. We hope that the architecture can maintain the stability of external behavior during restructuring. But it is not easy to do this. Released interfaces must be stable
Designers must have rich design experience and field experience. One of the meanings of the minimum interface principles mentioned above is that the more published interfaces, the more troubles it will bring in the future. Therefore, we are designing
In architecture and design, we should start with designing their interfaces, rather than thinking about the specific implementation. This is a big difference between object-oriented thinking and process-oriented thinking.
Here, we need to review the methods mentioned in the stabilization model to find immutating factors from changes. The methods described in the stabilization mode also apply to this mode. Only when the interface is stable can the test script be
Stable enough for testing automation. Encapsulating the changing factors is the main idea to keep the test script stable. The changing factors and the degree of encapsulation vary depending on the environment. For a project
Generally, the database is fixed, so the data access code can meet the changing needs as long as it can be concentrated at a fixed position. However, for a product, data access needs to be encapsulated as Data Access
The question layer (or the or ing layer) can dynamically replace connections for different database designs.
The last concept in this chapter is the concept of testing the network. If software development is carried out in strict accordance with the test-first approach. When the software is complete, a network composed of a large number of test scripts will be generated. Is
What is test network? Test scripts package the software, and the changes in any part of the software will be immediately reflected by the test network. This is like spider web, which can quickly and effectively change the requirements and design.
The scripts of the test network are mainly composed of unit tests. Therefore, apart from codingProgramIn addition, you also need to weave and repair this network. The purpose of knitting is to write the test code before writing the code,
The repair means that the test script needs to be modified synchronously when the interface changes due to software changes. The extra work seems to increase the workload of developers. But in our daily practice,
We found that, on the contrary, the development speed may decrease as a result of building a testing network, but in the middle of the development process, the cost saved by the change of the test network software will soon be able to offset the initial
Investment. In addition, with the familiarity and recognition of the testing priority method, the cost of building a testing network will continue to decrease, and the advantage will become more and more obvious:
- It is easy to detect code with errors, removing the worries of developers and enabling them to continuously develop new functions. In addition, it is also the basis for daily code creation.
- It saves a lot of time for testers and enables testers to focus on more efficient areas.
In addition, there is an additional cost for creating a testing network. If the development team is not familiar with the object-oriented language, changes to the testing network due to unstable interfaces will increase the construction cost.
From the above discussion, we can see that the architecture and code are inseparable, and the architecture cannot be called a good architecture without code. This is determined by the purpose of the architecture. The final goal of the architecture is to become executable
lines of code, while the architecture provides structural guidance for the code. Therefore, it is an effective way to verify the architecture with code. It is not easy to implement this practice. We need to consider the code-level
architecture-related knowledge (although the knowledge we discuss is limited to object-oriented language, but similar ideas can also be found in other languages) and used to serve the architectural design.