The design pattern is a solution that solves its elegant solutions after a specific issue has been summarized after countless experience. However, if you want to truly make the design pattern maximize, just know what the design pattern is, and how it is achieved, because you can't make you real understanding for design patterns, you can't The correct and appropriate design pattern in your own design. This article tries to view the design mode from another angle (design mode intent, motivation), through this new idea, design mode will become very close to your design process, and can guide, simplify your design, will eventually Export an excellent solution.
1 Introduction
In the development activities of the project, some design is very good in the project, but with the progress of the project, it is found to be modified or expanded to the existing code, resulting in such main reasons: new features The needs of demand and further understanding the system. At this time, we tend to find this work more difficult, even if you can complete it. At this point, a must be done is to reconstruct the existing code, which makes our next work relatively easy by reconstruction. Reconstruction is to improve its internal structure without changing the external behavior of the software system code. The goal of reconstruction is to make the code structure more reasonable, flexible, and adapt to new demands, new changes. Design patterns for a particular problem to give a beautiful solution will often become a refactoring goal, and once we can identify design patterns that can solve our problem, we will greatly simplify our work, because we can reuse others have already done jobs. But in our original design and ultimately, the transition between our design patterns is not smooth, but there is a gap. Such a result is: Even if we know a lot of design patterns, facing our practical problems, we have no effective way to judge which design model applies to our system, we should apply it. The reason for causing the above problem is often due to the result of the solution given by the design mode, and the intention of the design mode, and the motivation it produces is ignored. However, it is the intention of the design model, the motivation has prompted people to give a solution for solving a type of problem, the motivation of design patterns, intention to reflect the formation of this model, so it is more close to our practical problem, thus will be effective Guide our reconstruction process. This article will be displayed by an example. In this paper, the example is simplified, so that this is to highlight the substance of the problem and will make our ideas clearer. The idea itself is the most important, most fundamental, simplified example does not reduce the idea of the idea that we show.
2, problem description
A perfect software system must have a corresponding handling of errors. Only in this way can make the system enough, I am going to display the ideas I use as an example in a software system to show the ideas, methods I use. In a distributed network management system, an operation is often not successful, often because there is such a reason for failure, at this time, we should process the corresponding process according to the cause of failure, so that the impact of the error is within the smallest range It is best to recover without affecting the normal operation of the system, it is important, that is, do not forget the manager of the notification system while processing the error, because only managers have the ability to further further Analysis, so that the root cause of the error is found, it is fundamentally solved. Below I will start from the wrong advertisement manager, start our journey. Assuming an operation of accessing a database in a distributed environment, it is possible to fail because of communication or the cause of the database itself. At this time we want to notify the manager's incorrect by the user interface. Simplified code examples are as follows:
/ * No Error * / class ErrorConstant {public static final int ERROR_DBACCESS = 100; public static final int ERROR_COMMUNICATION = 101;} / * omitted other functions of the user interface * / class GUISys {public void announceError (int errCode) {Switch (Errcode) {CASE ERRORCONSTANT.ERROR_DBACCONSTANT.ERROR_DBACCESS: / * Notice Manager Database Access Errors * / Break; Case ErrorConstant.Error_Communication: / * Notial Manager's communication error occurred * / BREAK;}}} start, this paragraph The code work is very good and can complete the functions we need. However, this code lacks corresponding elasticity, it is difficult to adapt to changes in demand.
3, problem analysis
Familiar with object-oriented readers will soon find the above code is a typical structured method. The structured method is to organize the structure of the program with specific functions, and its package is only 1 level, that is, only For a particular function package (function). This makes a structured method difficult to adapt to changes in demand, and object-oriented methods are in this point superior to structured methods. In the object-oriented domain, it is an object to form a program structure, an object has its own responsibilities, complete the system's functionality through the interaction between objects, which makes it packaged at least 2, that is, encapsulates to complete your own responsibilities Methods and data. Alternatively, the method of object also supports higher levels of packages, such as description by common concept behaviors of different specific objects, we can reach 3 level package - abstract classes (in Java is interface). The higher the level of the package, the higher the abstract level, which makes it designed, the higher the code, the more easily adapt to the change. Consider the code in the previous section, if you find a new error during the development of the system, such as: User authentication error, how should we make our system to increase the need for this feature? ? A relatively simple, direct approach is to add a CASE statement used to handle this error. Yes, this method can indeed work, but this is to pay for it. First, as the system is further developed, there may be more error types, then it will result in lengthy for the processing part of the error, which is not conducive to maintenance. Second, it is also the most fundamental point, and the code that has been able to work is easy to introduce the error, and in many cases, the error is incorrectly introduced, and it is difficult to locate this type of error. Some surveys show that we are not used in the development process, and most of the time is debugging and discovered errors. In the object-oriented field, there is a very famous principle: OCP (Open-Closed Princi), its core meaning: a good design should be able to accommodate the increase in new functional requirements, but the increase in way is not by modifying Module (class), but is done by adding new modules (classes). If a design can follow the OCP, you can effectively avoid the above problems. If a design can meet the principle of OCP, we require us to function as the core when designing design. The key to achieving the OCP is abstraction, abstraction characterizes a fixed behavior, but there are many different specific implementation methods for this behavior. With abstract, we can use a fixed abstract concept to replace which easy changes in many specific concepts, and make the modules that depend on which easy-to-change concepts, depending on this fixed abstract concept, so The result is: The increase in new demand will only cause an increase in the specific concept without affecting other modules of abstracts dependent on the concrete concept. On the level implemented, the abstract body is described by an abstract class, and in Java is an interface. For a more detailed description of OCP, see References [2]. Since you know the nature of the problem and the corresponding solution, you will improve our code structure. 4, preliminary program
Let's re-examine the code and see how it is abstract. In the error handling, you need to handle different types of errors, each particular error has some information specific to yourself, but they are consistent on the concept level, such as: can be obtained from the interior through a specific method interface. Error message, each error has its own processing method. This allows a preliminary scheme to define an abstract error base class, which defines some methods that can be conceptually suitable for all different specific errors in this base class, each particular error can have its own different Realization of these methods. The code example is, for example, Interface ErrorBase {public string getInfo ();} Class DBAccesSserror Implements ErrorBase {public void handle () {/ * processes the processing of database access errors * /} public string getInfo () {/ * Constructs Information Back to Database Access Errors * /}} Class CommunicationError Implements ErrorBase {Public Void Handle () {/ * Performing About Communication Errors * /} Public String GetInfo () {/ * Construction Returns Information about Communication Errors * /}}
In this way, we can construct an actual error object at an error, and reference it with ErrorBase. Then, give the error handling module. At this time, the error handling module only knows a type ERRORBASE, without having to know each specific error type, so you can use a unified way to handle errors. The code example is, for example:
Class Guisys {public void AnnounceError (ERRORBASE Error) {/ * Handling error * / error.handle ();}}
It can be seen that the increase in new error types only needs to add a specific error class, which does not have any impact on the error handling section. It looks perfect, it also meets the principle of OCP, but further analysis will find that there is a problem in this program, and we will perform a detailed description in the next section.
5, further analysis
The last section gives a solution, which is perfect for only guisys, but the situation is often not the case. The front also mentioned that for the occurrence of the income, in addition to the user to inform the system, other processing, such as trying to recover, such as logs, etc. It can be seen that these processing methods are very different from the error notice to the user, and there is no way to unify all different processing only with a Handle method. However, if we add different processing methods in ErrorBase, in a specific error class, the corresponding implementation of its own needs, it seems to be a good solution. The code example is, for example:
interface ErrorBase {public void guiHandle (); public void logHandle ();} class DBAccessError implements ErrorBase {public void guiHandle () {/ * notification database access the user interface error handling * /} public void logHandle () {/ * notification log System database access error handling * /}} Class CommunicationsError Implements ErrorBase {public void guihandle () {/ * Notifys the user interface communication error handling * /} public void loghandle () {/ * Notification Log System communication error handling * / }}} Class Guisys {public void AnnounceError (ErrorBase Error) {Error.GuiHandle ();}} class logsys {public void AnnounceError (ErrorBase Error) {Error.logHandle ();}} The reader may have noticed that this practice is actually true It is not very compliant with OCP, although it limit the change in ERRORBASE hierarchical architecture, but increases new processing methods, or changed the existing ErrorBase class. In fact, this design method also violates another famous object-oriented design principle: SRP (SINGLE RESPONSIBILITY Princi). The core meaning of this principle is: A class should have only one responsibility. With regard to the meaning of responsibilities, there is a famous definition of object-oriented master Robert.c martin: the root of a class is to guide the changes of this class. If a class has more than one duty, then there will be multiple different reasons It is actually coupled to multiple duties to each other, which will reduce the cohesiveness of this class. The responsibility of the wrong class is to save and related error status, and provide methods to get these states. Different processing methods are also placed in the error class in the design, thus increasing the duties of the wrong class, even if there is no relationship with the error class itself, there is no change in error handling, increase, modification, will result in the modification of the error class. This design method will bring unexpected problems when demand changes. So can it be stripped from the wrong processing method? If the reader is more familiar with the design mode (the familiarity here is the intention, motivation, instead of how to implement a specific design mode), it should be faint that a better design is about to appear.
6, design mode surface
Let us re-describe the problem: We already have a class hierarchy about the wrong, now we need to allow us to increase our new processing methods for this level without changing this class hierarchy. It sounds familiar, good, this is the description of the intent of the Visitor design mode. Through analysis of this mode motivation, we can easily know that if you want to use Visitor mode, you need to define two class hierarchies: a class level of the element corresponding to the received operation (that is our error class), and the other corresponds to the definition pair The visitor of the elements (that is, our different processing methods for errors). In this way, we convert the problem perspective, that is, transform the problem of different error handling methods to require different error handling classes, this result is that we can add new error handling methods by adding new modules (classes). Instead of increasing new error handling methods (so, it is necessary to modify the existing class). Once this is, the following work is relatively simple, because Visitor mode has built a design context for us. At this time we can pay attention to the implementation of the Visitor mode to guide us below the specific implementation. The UML map and code examples of the final program structure are given below, and the method of errors belonging to the error itself is ignored, and each particular error handling method interacts through these methods and specific error class objects to complete their respective processing. Features. The final design of the program structure chart final code example
interface ErrorBase {public void handle (ErrorHandler handler);} class DBError implements ErrorBase {public void handle (ErrorHandler handler) {handler.handle (this);}} class CommError implements ErrorBase {public void handle (ErrorHandler handler) {handler.handle (this);}} interface ErrorHandler {public void handle (DBrror dbError); public void handle (commError commError);} class GUISys implements ErrorHandler {public void announceError (ErrorBase error) {error.handle (this);} public void handle (DBERROR DBERROR) {/ * Notifies the user interface to process the processing of database errors * /} public void handle (Commerror CommeRROR) {/ * Notify the user interface for processing related to communication errors * /}} class logsys IMPLEMENTS ERRORHANDLER {public void AnnounceError (ErrorBase Error) {Error.Handle (this);} public void handle (dberror dberror) {/ * Notification Log System Performing Database Error Processing * /} Public Void Handle (Commer ROR CommeRROR) {/ * Notification Log System Performing Processing about Communication Errors * /}} 7, conclusion
The design pattern is not just a solution about a specific problem. Its intent and its motivation are often more important, because once we understand the intention of design patterns, the motivation, it is easy during the design process. Discover that our own design patterns are therefore greatly simplified, and you can get a more ideal design. In addition, in the process of learning design mode, you should pay more attention to the things behind design patterns, namely some of the excellent guidelines for specific design patterns, which have detailed discussion in the first chapter of reference [1]. There are basically two points: discovery change, package change priority use combination, rather than inheritance If you pay attention to learning from these aspects, understanding design patterns, you will get more useful knowledge than a single specific design model itself, and Even in the case where there is no ready mode available, we can also design a good system.