Over the past year, I've written a lot of articles in the "Pursue Code quality" column. These articles show you a number of tools and techniques to improve the quality of your code. I've shown you how to apply code metrics to monitor the quality of your code base, how to use test frameworks like TestNG, fit, and Selenium to verify the functionality of your application, and how to use Xmlunit and strutstestcase Extension Frameworks (and some powerful help tools such as Cargo and DbUnit) to extend the scope of the test framework.
While code metrics and developer testing are important to ensure code quality throughout the development process (as I have often said, testing is done in a timely and regular manner), they are basically only responsive to code quality. You determine and quantify the quality of your code by testing and measuring the code, but the code itself is already written. No matter how hard you try, you will be stuck with the original design.
Of course, different methods of the design of the software system will have good and bad, mixed. One of the key factors of excellent design is to maintain the maintainability of the system. Poorly designed and executable systems may be easy to write, but supporting them is a real challenge. These systems are often fragile, meaning that modifications to one area of the system will affect other seemingly unrelated areas, so it is difficult and time-consuming to refactor them. Adding a developer test to a code library can provide us with a plan for our work, but its progress is still a tough and slow process.
We can refactor to improve the code that has been written, but it's often expensive to make changes after the code is finished. And would it be easier and easier to write the code to perfection in the first place? This month, I'll introduce a very proactive technique to ensure the quality and maintainability of your software system. The dependency inversion principle is proven to be necessary to write high quality code that can be maintained and testable. The basic idea of the dependency inversion principle is that the object should depend on abstraction rather than implementation.
is dependency inversion rather than dependency injection
The dependency inversion principle has no direct relationship with dependency injection. Dependency injection, also known as control reversal (inversion of CONTROL,IOC), links object dependencies at run time, rather than at compile time, using frameworks such as Spring. Although dependency inversion and dependency injection do not need to be used at the same time, they are complementary: two techniques strive to use abstraction rather than implementation.
Too tightly coupled.
You may have heard at least the term coupling (coupling) used in object-oriented programming. Coupling is the interrelationship between components (or objects) in an application. Loosely coupled applications are more modular than tightly coupled applications. Components in loosely coupled applications rely on a variety of interfaces and abstract classes, while tightly coupled systems rely on a variety of specific classes for their components. In loosely coupled systems, the components are interconnected by using abstractions rather than implementations.
The tightly coupled problem can be easily understood if there are illustrations. For example, the GUI of the software system in Figure 1 is coupled to its database:
Figure 1. A tightly coupled system
A GUI's reliance on an implementation, rather than an abstraction, can impose constraints on the system. The GUI cannot be executed without the database being started and running. The design does not seem to be very bad from a functional point of view-after all, we've been writing apps and there's nothing wrong with it-but the tests are different.
The ' fragile ' system
The system in Figure 1 makes isolation programming particularly difficult, which is necessary for testing and maintaining the system in all its aspects. You will need an active database with the correct lookup data to test the GUI, and a functioning GUI to test the data access logic. You can use Testng-abbot (now named FEST) to test the front end, but this still doesn't tell you anything about database functionality.
Listing 1 shows this bad coupling. A specific button on the GUI defines a actionlistener that communicates directly with the underlying database through a getorderstatus call.
Listing 1. Define the ActionListener as a button in the GUI
findWidgetButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
String value = widgetValue.getText();
if (value == null || value.equals("")) {
dLabel.setText("Please enter a valid widgetID");
} else {
dLabel.setText(getOrderStatus(value));
}
} catch (Exception ex) {
dLabel.setText("Widget doesn't exist in system");
}
}
//more code
});