This chapter is important to introduce yourself but not that is so traditional "paradigm" (Pattern) programming method.
During the evolution of the object-oriented programming, the most important step is the "Design Pattern". It is defined as a "milestone" in the "Design Patterns" written by Gamma, Helm and Johnson (the book is published by Addison-Wesley in 1995). That book lists 23 different ways to solve this problem. In this chapter, we are ready to reveal the basic concepts of design paradigm accompanying several examples. This may arouse your desire to read the "Design Pattern" book. In fact, the book has now become a reference book for almost all OOP programmers.
1: But warning everyone: The example in the book is written with C .
The following part of this chapter contains an example of displaying the design evolutionary process, first of all, the original program, gradually developed and improved, slowly become a more logical, more appropriate design. The program (simulated garbage sorting) has been evolving, which can make this evolution as a prototype of its own design - first propose an appropriate program for a specific issue, and gradually improve, making it a solution to the problem The most flexible solution.
16.1 paradigm concept
At the beginning, the paradigm can be imagined into a particularly smart and self-adaptation, which can solve a particular type of problem. That is, it is similar to some people who need to fully understand a problem. After understanding the aspects of the problem, finally propose a set of most universal, flexible solutions. The specific problem may be seen before and resolved. However, the previous program may not be the most complete, everyone will see how it is specifically expressed in a paradise.
Although we call it "design paradigm", they are actually not limited to the design area. When thinking about "paradigm", it should be separated from traditional sense, design, and implementation. Instead, "paradigm" is a complete set of thoughts in a program, so it may sometimes appear in the analysis phase or the advanced design phase. This is very interesting because the paradigm has the form of direct implementation in the form of code, so it may not be desirable to reveal it before low design or specific implementation (and in fact, unless you really enter those stages, otherwise you don't know yourself. A paradigm to solve the problem).
The basic concept of paradigm can also be seen as the basic concept of programming: add a new layer of abstraction! As long as we abstract something, it is equivalent to isolating a specific detail. Moreover, this is the most striking motive that "will remain in the absence of changes." Another reason why this is that once some part of the discovery may change due to such or such reasons, we generally want to prevent those changes from other changes in the code. This can not only reduce the maintenance cost of code, but also more convenient for us to understand (the result is also reduced overhead).
In order to design powerful and easy-to-maintain applications, usually the most difficult part is to find something I call "lead changes". This means that it is necessary to find the most important thing that causes system changes, or changes an angle to find the highest cost, the biggest cost of overhead. Once the "lead change" is found, you can set a focus for yourself, focus on it.
So the final goal of the design paradigm is to isolate the contents of the code in the code. If you observe from this perspective, you will find that this book has actually used some design paradigms. For example, inheritance can imagine a design paradigm (similar to a compiler). It has the same interface (ie, the constant thing) object, which allows us to express the differences in behavior (ie, something changing). Synthesis can also imagine a paradigm because it allows us to modify-dynamically or static-used to implement the objects of the class, so modify the mode of operation. In the "Design Patterns" book, everyone can also see another paradigm: "Inheritor" (ie Iterator, Java 1.0 and 1.1 are not responsible for the enumeration, ie "enumeration"; Java1.2 The collection is changed back to the name of the "inheritor"). When we traverse it in the collection, when choosing different elements one by one, the successor can effectively hide the implementation details of the collection. With a heir, a generic code can be written so that all elements in a sequence take some kind of operation while do not have to care about how this sequence is built. In this way, our general code can be accompanied by any collection of a hemerler.
16.1.1 list
Perhaps the simplest design paradigm is "Singleton), which provides one of the objects (and only one) instance. The list has been applied in the Java library, but the following example looks more directly:
909-910 page program
The key to creating a list is to prevent customer programmers from creating an object in any way outside of us. All builders must be set to private, and at least create a builder to prevent the compiler from being automatically synchronized with a default builder (it will be self-smartly created into "Friendly" --Friendly, Not private).
At this point, you should decide how to create your own object. Here, we chose the way static creation. But you can also choose to wait for a customer programmer to create a request, then dynamically created according to their requirements. No matter which case, the object should be saved as "private" attribute. We provide access to access by public methods. Here, getHandle () generates a handle to Singleton. The remaining interface (GetValue () and setValue ()) belong to a normal class interface.
Java also allows an object to be created by cloning (clone). In this example, the class is set to Final to disable cloning. Since Singleton is inherited directly from Object, the Clone () method keeps the protected attribute, which cannot be used (forcibly using the compile period). However, if we are inherited from a class structure, the structure has already contained a clone () method, making it a public property, and implements Cloneable, then in order to prohibit clone, you need to overload clone (), and throw a ClonyNotsupportedException ( Cloning violations are not supported, as in Chapter 12. Also overload clone () and simply returns this. That will cause certain confusion because the customer programmer may mistakenly think that the object has not been cloned, still manipulating the original one.
Note We are not limited to creating an object. This technology can also be used to create a limited object pool. But in that case, you may need to solve the sharing problem of the inner object. If you really encounter this problem, you can design a set of schemes to realize registration and revoking registration of shared objects. 16.1.2 Paradigm classification
"Design Patterns" discusses 23 different paradigms and is based on three standards (all standards involve aspects that may change). These three standards are:
(1) Create: Object creation mode. This usually involves the isolation of the object creation details, so it does not have to rely on the specific type of object, so it does not have to change the code when adding an object type.
(2) Structure: Design objects to meet specific project restrictions. This involves the connection of objects and other objects to ensure that changes within the system do not affect these connections.
(3) Behavior: Objects for manipulating a specific type of action in the program. This requires that we will be able to take the operation package, such as explaining a language, implementing a request, traversing in a sequence (like it is in the inner) or implemented an algorithm. This chapter provides examples of "Observer" and "Visitor" paradigm.
"Design Patterns" uses a section for all these 23 models, and there is a lot of examples, but most of them are written in C , and a few use SMALLTalk (if you have seen this book, you know this actual Not a big problem, because it is easy to translate the basic concept from two languages to Java). Now this book does not intend to repeat all paradigms introduced by "Design Patterns", because it is an independent book, you should read it separately. Instead, this chapter is only ready to give some examples, let everyone have a rough impression on the paradigm and understand where their importance is.
16.2 Observer paradigm
Observer paradigm is solved is a quite common problem: Since a change in certain objects, a set of objects need to be updated, then how do you solve it? In SmallTalk's MVC (Model - View-Controller) "Model - View" section, or in almost equivalent "document-view structure", you can see this problem. Now we have some data ("documentation") and multiple views, assuming as a picture (plot) and a text view. If data is changed, two views must know that they are updated, and that is the "observer" to be responsible. This is a very common problem, and its solution has included a standard Java.util library.
In Java, there are two types of objects to implement the observation paradigm. The OBSERVABLE class is used to track all individuals who wish to receive notifications when a change occurs. Regardless of whether "status" changes. If someone says "Well, everyone must check themselves, and may be updated", then the OBSERVABLE class performs this task - calls the NotifyObservers () method for each "person" in the list. The NotifyObservers () method belongs to a part of the base class Observable.
In the observation paradigm, there is actually possible to change: observe the number of objects and the way of updating. That is, the observer paradigm allows us to modify these two aspects without interfering with other code surrounding it.
The following example is similar to the ColorBoxes example of Chapter 14. Boxes (Boxes) are placed in a screen grid, each initializing a random color. In addition, each box is "Implement" interface, and the OBSERVABLE object is registered. If you click on a box, all other boxes will receive a notification indicating that a change has occurred. This is because the OBSERVABLE object automatically calls the Update () method of each OBServer object. In this method, the box will check if the box in the point is close to yourself. If the answer is affirmative, then modify its color, keep the coordination of the box in the point. 912-914 page program
If it is an online help document for the Observable, it may detect that you can manage updates with an original OBSERVABLE object. But this statement is not established; you can try it yourself - in the boxobserver, create an OBSERVABLE object, replace the boxobservable object, see what will happen. In fact, what doesn't happen. To really produce, you must inherit from Observable and call setChanged () in a place of derivative class code. This method needs to set "Changed" flag, which means that when we call notifyObservers (), all observer is actually notified. In the above example, setChanged () is simply called in NotifyObservers (), and you can decide when to call setChanged () based on any criteria that meet the actual situation.
BoxobServer contains a single OBSERVABLE object called Notifier. When you create an Ocbox object, it will contact the Notifier. In Ocbox, as long as you click the mouse, the call to the notifyobservers () method will issue a call, and the object in the point is passed as a parameter into it, so that the message (with their Update () method) all boxes Can you know who is in a point, and it is necessary to change it accordingly. With the combination of code in notifyobservers () and Update (), we can cope with some very complex situations.
In the notifyobservers () method, the surface seems to be notified on the surface must be fixed during the compilation period. However, just carefully study the above code, you will find that the only thing in BoxobServer or Ocbox needs to pay attention to whether to use the boxobservable is when you create an OBSERVABLE object - starting from the beginning, all things will use the basic OBSERVABLE interface. This means that if you want to change the notification, you can inherit other Observable classes and exchange them during run.
16.3 Simulated Garbage Recycling Station
The essence of this problem is to throw the garbage into a single trash, in fact, it is unsociated. But in the future, some special information must be recovered to correctly classify the garbage. In the initial solution, RTTI plays a key role (see Chapter 11).
This is not a common design because it adds a new limit. It is this limit to make problems very interesting - it is more like those that we have encountered in our work. This additional limit is: When garbage arrives at the garbage collection station, they are all mixed together. The program must set a model for the classification of garbage. This is where RTTI's role is placed: we have a lot of unknown rubbish, and the program will correctly determine the types they belong. 915-917 page program
The first place to pay attention to is a package statement:
Package c16.recyclea;
This means that in the source catalog adopted by this book, this file will be placed in the Recyclea subdirectories brought from C16 (on behalf of Chapter 16). Chapter 17 of the dispenser is responsible for placing it into the correct subdirectory. The reason why this is because this chapter will rewrite this particular example; each version of it will place it in your own "package" and avoid conflicts of class names.
Several VECTOR objects are created for accommodating the TRASH handle. Of course, the Vector actually accommodated Object (object), so they can eventually accommodate anything. The reason why they want to accommodate trash (or other things derived from trash), the only reason is that we need to avoid anything other than trash. If you really put some "wrong" things into the vector, then you will not get an error or warning prompt in the compile period - you can only know that you have made mistakes through an violation through the runtime.
After the TRASH handle is added, they will lose their specific identification information, which will only become a simple Object handle (traceable). However, due to the presence of versatility, when we call dynamic binding methods through the Enumeration Sorter, once the result Object has stimulated back to TRASH, the correct behavior will still occur. SUMVALUE () also operates each object in the Vector with an EnuMeration.
The surface is held, first trace the TRASH back to a collection of hosted basic type handles, and then back to retroactively stamp, which seems to be a very stupid approach. Why not just put the garbage into the appropriate container at the beginning? (In fact, this is the key to "recycling" a fog). In this program, we can easily replace this approach, but in some cases, the structure and flexibility of the system can be greatly benefited from discontinuation.
The program has met the original intention of the design: it works fine! As long as this is a one-time program, it will look very well. However, the truly useful program should be able to solve the problem at any time. So you must ask yourself a question: "If the situation changes, can it work?" For example, the thick panel is now a very valuable recyclable item, so how to integrate it into the system Especially when the program is very complicated? Since the type in front of the Switch statement checks that the encoding may spread throughout the program, all of the codes must be found each time you join a new type. If you accidentally miss one, the compiler cannot provide any valuable help in addition to the existence of an error.
RTTI is improperly used here that "each type is tested." If special treatments are required due to the type of subset, only the subset is found, so the situation will become better. However, if each type is found in a Switch statement, it is likely to miss a key point to make the final code difficult to maintain. In the next section, everyone will learn how to improve this procedure and make it more flexible. This is a very meaningful example in the programming. 16.4 Improvement Design
The organization of all scenarios in the "Design Patterns" is expanded around "what changes will change when procedure". This may be the most important issue for any design. If you construct your own system according to the answer to this question, you can get two aspects: the system is not only more easily maintained (and cheaper), but also produces some objects that can be reused, so that other related systems are also It becomes less cheap. This is the advantage of object-oriented programming, but this advantage is not automatically reflected. It requires a comprehensive and in-depth understanding of our problems that need to be solved. In this section, we are ready to show you how to do this during the step-by-step improvement of the system.
For this recycling system, the answer to "What will change" is very common: more types will join the system. Therefore, the design of the design is to simplify this type of addition as possible. In the recycling program, we are ready to encapsulate all places involving specific type information. In this way, if there is no other reason), all changes are carried out locally for those packages. This way of processing also makes the part of the code are particularly refreshing.
16.4.1 "Make more objects"
This leads out a regular criterion for object-oriented programming, I was earliest to I heard that there were also more than complicated, and more objects were produced. Although it sounds a bit, it is simple, but it is really a very useful criterion I know (everyone will notice "Make more objects" often equivalent to "add another hierarchy"). Under normal circumstances, if you find that a place flooded with a lot of complicated code, you need to consider what kind of cockroach can make it refreshing. In this way, the system will often get a better structure and make the program more flexible.
First consider the place for the TRASH object for the first time, this is a Switch statement in main ():
919-920 Program
These code obviously "too complex", and one of the places that must be changed when new types are added. If you often join a new type, then a better solution is to create an independent approach to get all the required information, and create a handle, pointing to an object of the correct type - has traced a trash object. In "Design Patterns", it is roughly called "creation paradigm". The special paradigm to be applied here is a variant of the Factory method. Here, the Factory method belongs to a Static member of TRASH. But a more common case is: it belongs to an overloaded method in the derived class.
The basic principle of the Factory method is that we will pass the basic information required to create an object to it, then return and wait for the handle (the shape to the basic type) as the return value. From this point, you can treat an object by multi-faceted way. Therefore, we don't have to know what is the exact type of the object created. In fact, the Factory method will hide yourself, we can't see it. This prevents accidental misuse. If you want to use objects without versatility, you must use RTTI and specified shapes.
However, there is still a small problem, especially in the basic class, using more complex methods (not the type shown here), and overloaded (overwriting) in the derivative class. What should I do if the information requested in the derived class requires more or different parameters? "Creating more objects" solves this problem. In order to implement the Factory method, the TRASH class uses a new method called Factory. In order to hide the creation data, we use a new class named INFO to create all information required to create an appropriate trash object. Here is an easy implementation of INFO: 920-921 Page Program
The unique task of Info object is to accommodate information for the factory () method. Now, if there is a special case, Factory () requires more or different information to create a type of trash object, then there is no need to change Factory (). By adding new data and builders, we can modify the INFO class, or use subclasses to process more typically object-oriented forms.
The factory () method for this simple example is as follows:
921 page
Here, the exact type of the object is easy to judge. But we can imagine some more complex situations, and Factory () will use a complex algorithm. Anyway, the key now is that it has been hidden to a place, and we know to go to that place when we add new types.
The creation of new objects in main () is now very simple and refreshing:
921 page program
We created an Info object here to incorporate data into factory (); the latter creates a TRASH object in the inner stack and returns the handle added to the Vector BIN. Of course, if you change the number and type of the parameters, you still need to modify this statement. However, if the creation of Info objects is automatic, it can also avoid the trouble. For example, a Vector of the parameter can be passed to the builder of the INFO object (or directly into a factory () call). This requires analysis and inspection of parameters (self-variables) during operation, but it does provide very high degree of flexibility.
Everyone can see that Factory is responsible for solving the "lead change" problem: If new types (changing) have been added to the system, the only code that needs to be modified is inside the Factory, so Factory will isolate the impact of the change. .
16.4.2 A paradigm for prototype creation
One problem with the above design is still a central place, where all types of objects must be known: inside the factory () method. If you often add a new type to your system, the Factory () method is modified for each new type. If you really feel distressed to this problem, you can try again in a step, all information related to the type - including its creation process - all in-that represents the type of class. In this way, each time you add a type, the only thing you need to do is inherited from a class.
To move information created by the type into a specific type of trash, you must use the prototype paradigm (from "Design Patterns" book). The most basic idea here is that we have a master object sequence, which makes one for each type of interest. Objects in this sequence can only be used for the creation of new objects, and the operations employed are similar to the CLONE () mechanism within the Java root OBJECT. In this case, we named the cloning method tclone (). When you are ready to create a new object, you have to collect some form of information in advance and build the type of object we want. Then traverse in the master sequence, compare the information in the hand with any appropriate information in the original object in the main control sequence. If you find a match you need, you clone it. With this solution, we don't have to implant any creation information in hard coding. Each object knows how to reveal the appropriate information and how to clone itself. Therefore, when a new type is added to the system, the factory () method does not require any changes.
To solve the problem of the creation of prototypes, a method is to add a large number of methods to support the creation of new objects. However, in Java 1.1, if you have a handle pointing to the Class object, it has provided support for creating new objects. With the "Reflection" of Java 1.1 (already introduced in Chapter 11), even if we only point to a handle to the Class object, a builder can also be called normally. This is undoubtedly a perfect solution for prototype problems.
The prototype list will be indirectly represented by a handle list to all the Class objects you want to create. In addition, if the prototype processing fails, the factory () method will consider that it is tried to load it because a specific Class object is not in the list. By dynamically loading prototypes in this way, the TRASH class does not need to know what type they have to manipulate. Therefore, there is no need to make any form of modifications when we add new types. Thus, we can use it in the remaining parts of this chapter.
923-925 page program
Basic trash classes and SUMVALUE () are still like usual. The remaining part of this class supports the prototype paradigm. You will first see two internal classes (set to Static Attributes, making it only for internal classes that are only for code organization purposes), which describes the possible violations. The following follows is a vector trashType, which is used to accommodate the Class handle.
In trash.Factory (), the String of the Info object ID (another version of the INFO class, the different previously discussed) The String contains the type name of the TRASH to be created. This String will compare the Class name in the list. If there is a match, it is the object to be created. Of course, there are many ways to determine the objects we want to create. This method is to be used because information read from one file can be converted into an object.
After discovering the TRASH (garbage) species you want to create, the next "reflex" method is big. The getConstructor () method requires its own parameters - an array consisting of a Class handle. This array represents different parameters and is arranged in their correct order so that we look for builders. Here, the array is created with a syntax dynamically created with the array of Java 1.1:
NEW class [] {double.class}
This code assumes that all trash types have a builder that requires a double value (note that Double.Class is different from double.class). If you consider a more flexible solution, getConstructors () can also be called to return an array of available builders.
From getConstructors (), it is a handle to a constructor object (the object is part of java.lang.Reflect). We use the method newinstance () to dynamically call the builder. This method needs to obtain an Object array containing the actual parameters. This array is also created by syntax of Java 1.1: New Object [] {new double (info.data)}
In this case, the Double must be placed inside the package (container) class to truly be part of this object array. By calling newInstance (), Double will be extracted, but everyone may feel slightly confused - the parameters may be Double, or Double, but must be delivered with Double when calling. Fortunately, this problem exists only in the middle of the basic data type.
After understanding the specific process, create a new object and only provide a Class handle for it, things become very simple. For the current situation, Return in the internal cycle will never be executed, and we will exit at the end. Here, the program dynamically loads the Class object and adds it to the TrashTypes list to try to correct this problem. If you still can't find a really problematic place, the loading is successful, then repeat the Factory method, re-trial.
As everyone will see, the biggest advantage of this design is no need to change the code. No matter what circumstances, it can be used normally (assuming that all trash subclasses contain a builder to get a single Double parameter).
TRASH subclass
In order to adapt to the prototype mechanism, the only requirement for each new child class for TRASH is to include a builder, indicating that it gets a Double parameter. The "reflection" mechanism of Java 1.1 can be responsible for all remaining work.
Here are different types of trash, each type has their own files, but all belong to a part of the trash package (the same, in order to facilitate reuse in this chapter):
926-927 page program
Here is a new TRASH type:
927 page program
It can be seen that these classes have no special places except for the builder.
2. Analyze TRASH from external files
Information related to the trash object will be read from an external file. For each aspect of trash, all the necessary information is listed - each line represents a fixed format of "Garbage (Waste Name: Value". E.g:
928 program
Note that when a given class name, the class path must be included, otherwise the class cannot be found.
To parse it, each line of content is read, and a string method indexof () is used to create an index of ":". First use the string method SubString () to remove the type name of the garbage, then use a static method Double.Valueof () to achieve the corresponding value and convert to a Double value. The TRIM () method is used to delete a string two overhead spaces.
The Trash parser is placed in a separate file because this chapter will continue to use it. As follows:
929-930 page program
In Recyclea.java, we hold the trash object with a vector. However, other collection types can also be considered. To do this, the first version of Fillbin () will get the handle pointing to a Fillable. The latter is an interface for supporting a method called AddTrash ():
930 page
All things that support this interface can be used with Fillbin. Of course, the Vector does not implement Fillage, so it can't work. Since the vector will be applied in most examples, the best practice is to add another overloaded Fillbin () method to make it as a parameter as a vector. With an Adapter class, this vector can use: 930 Page Program as a Fillage Object
It can be seen that the only task of this class is to connect Fillable's addTrash () to the product () method of the vector. Using this class, the overloaded Fillbin () method can be used with a vector use in Parsetrash.java:
930 page program
This solution is suitable for any frequently used collection class. In addition, the set class can also provide its own adapter class and implement Fillable (see later, in DynaTrash.java).
3. Repeat application of prototyping mechanism
Now, everyone can see the use of prototype technology, revised Recyclea.java version:
931 page
All trash objects - and Parsetrash and support are now become part of a package called C16.Trash, so they can be simply imported.
Whether to open a data file containing trash description information or parse the file, all the operations involved have been encapsulated into the Static (static) method Parsetrash.Fillbin (). So it is no longer a focus on our design process. In this section, you often see what type of new class, Parsetrash.Fillbin () will continue to work, which will not change, which is undoubtedly an excellent design.
Remove the creation of the object, this program does have a strict "localization" that has changed the changes required for the new type to join the system. However, in the process of using RTTI, there is a serious problem, which has been explicitly exposed. The program surface is working very well, but it is always detected that there is a new type of waste that cannot be "cardboard" - even if there is a cardboard type in the list! This happens, it is entirely due to the use of RTTI. RTTI will only look for those we tell it. RTTI is incorrect herein is "Each type in the system" has been tested instead of testing only one type or a type subset. As you will see later, you can use other ways to use a versatile feature when you test each type. However, if you use RTTI in this form, it is easy to add a new type in your own system, it will be easily forgotten to make an appropriate change in the program, so that it is difficult to discover after it is bury. Therefore, in this case, it is necessary to avoid using RTTI, which is not just for the surface look - also to produce a more easily maintained code.
16.5 Abstract Application
Going to this step, then consider the remaining part of the design. Where is the class? Since the way to classify the garbage is very indecent and too exposed, why not isolate the process, hide it into a class? This is the famous "if you have an indecent thing, you should at least localize it to a class" rule. It looks like this:
932 pages
Now, as long as a new type of trash is added, the initialization of the TraSrter object must be changed. You can imagine that the trashsorter class looks like this:
Class trashsorter extends vector {
Void Sort (TRASH T) {/ * ... * /}
}
That is, TRASHSORTER is a VECTOR (series) consisting of a series of handles, and the handle points to the Vector of the TRASH handle; using addelement (), you can install new trashsORTER, as shown below:
TRASHSORTER TS = New trashsorter ();
Ts.addelement (new vector ());
But now, sort () has become a problem. How to coding with static mode How to deal with a new type of joined fact? To solve this problem, you must delete the type information from sort () so that everything you need to do is to call a general method and take care of all the details involving type processing. This is of course another way to describe a dynamic binding method. So sort () will be simply traversed in the sequence and call a dynamic binding method for each vector. Since the task of this method is to collect the garbage flakes of interest, it is called GRAB (trash). The structure now turns into the following:
933 pages
Among them, trashsORTER needs to call each grab () method; then the type of time is received according to the current VECTOR, it will get a different result. That is, the Vector must pay attention to the type of itself. The traditional way to solve this problem is to create a foundation "trash bin" class and inherit a new derivative class for each different type that hosted. If Java has a parameterized type mechanism, it may be the most direct way. But for this mechanism, we should be constructed for us, we should not make trouble, manual coding, and later "observation" ways provide a better encoding method.
OOP design a basic criterion is "The use of data members is used as a state of behavior." For the Vector of receiving Paper, as well as the VECTOR that accommodates Glass (Glass), everyone may think that the Grab () method used for them will certainly produce different behaviors. But how is it depends entirely on the type, not something else. It can be explained to a different state, and since the Java has a class representation type (Class), it can be used to determine which type of TBIN is to accommodate what type of trash.
Builder used for TBIN requires us to deliver one of its own Class. Doing this can tell VECTOR what type it hopes to accommodate. Subsequently, the grab () method uses Class Bintype and RTTI to check if the TRASH object we passed to it corresponds to the type it wishes to collect.
The complete solution is listed below. The number (such as * 1 *) set to the comment is convenient for the instructions listed later.
934-935 page program
(1) TBINLIST accommodates a series of TBIN handles, so Sort () can inherit by TBIN when looking up with the trash object we passed to its trash object.
(2) Sortbin () allows us to pass a complete TBIN in, and it will traverse it in TBIN and select each trash and classify it into a specific TBIN. Please note the versatility of these code: When the new type is joined, it does not require any changes. As long as the new type is added (or other events), a large number of code does not need to change, it indicates that we are designed to be an easy-to-extend system.
(3) How easy it can now be added to add a new type. For support, just change a few lines of code. If it is necessary, it can even further improve the design so that more code remains "fixed".
(4) A method call makes the content of the BIN to the corresponding, specific types of garbage.
16.6 more dispatched
The above design option is definitely satisfactory. The new type of system in the system involves adding or modifying different classes, but there is no need to make a wide range of modifications to the code within the system. In addition, RTTI is not used improperly as it is in Recyclea.java. However, we are still possible to go deep into steps, see RTTI at the most "pure" perspective, consider how to completely eliminate it in the garbage classification system. To achieve this goal, we must first recognize that all activities with special associations with different types - such as detecting a specific type of garbage, and put it into the appropriate garbage bin - these activities should Control through versions and dynamic binding.
The previous examples are first sorted by type, and a series of elements belonging to a particular type will be operated. Now you need to operate a specific type, please stop thinking about it. In fact, versatile (dynamic binding method call) The entire purpose is to help us manage information with specially associated with different types. In this case, why do you want to check the type?
The answer is that everyone may not be a truth: Java only performs a single dispatch. That is, if an object is performed for multiple types of unknown objects, Java will only be a call dynamic binding mechanism for those types. This certainly does not solve the problem, so in the end, it has to be manually judged to effectively generate its own dynamic binding behavior.
In order to solve this defect, we need to use the "multiple dispatch" mechanism, which means that a configuration is needed to make a single method call to generate multiple dynamic method calls, thereby correctly judgeting a variety of types during one processing. To achieve this requirement, you need to operate multiple types of structures: one type structure is required every dispatch. The following example will operate two structures: a series of existing Trash series and a series of trash bins - different garbage or waste will be placed in these cartridges. The second hierarchical structure is not absolutely obvious. In this case, we need to create it to perform multiple dispatches (because this example only involves two dispatched, "double dispatch").
16.6.1 Realization Double Dispatch
Remember that the velocity can only be called by method calls, so if you want to make the double dispatch correctly, two method calls must be performed: in each structure, it is used to determine the type. In the TRASH structure, use a new method to call AddTobin (), which uses the parameters that are configured by Typebin. That way will traverse in arrays, trying to add themselves to the appropriate garbage cartridge, which is where double dispatched.
937 page
The newly established hierarchical structure is Typebin, which contains one of its own methods named (), and has also been applied. But pay attention to a new feature: add () has been "overload" processing, accepts different garbage types as parameters. Therefore, a key point of the double satisfaction mechanism is that it also involves overload.
The re-design of the program also brings a problem: the current basic class trash must contain an AddTobin () method. To solve this problem, a most direct approach is to copy all code and modify the basic class. However, if there is no control of the source code, there is another way to consider: put the addtobin () method into an interface, keep trash constant, and inherit new, special types Aluminum, Paper, Glass, and Cardboard . We are ready to take a way.
Most classes used in this design must be set to public (public) properties, so they are placed in their own classes. The interface code is listed below:
938 page
In Aluminum, Paper, Glass, and Cardboard, each specific subtype is implemented, you will implement the addTobin () method of interface Typebinmember, but the code "seems" used in each case is exactly the same: 938-940 page program
The code in each addtobin () calls add () for each Typebin object in the array. But please pay attention to the parameters: this. For each subclass of trash, the type of this is different, so you can't think that the code "complete" is the same - although it can be considered just after adding a parameterized type mechanism in Java. This is the first part of the double dispatch, because once it enters the inside, you can know how Aluminum, Paper, or other garbage type. This information is passed through this information during the call to the Add (). The compiler analyzes the call to add () correct overload versions. However, since TB [i] generates a handle to the basic type Typebin, it will eventually call a different method - what method depends on the type of Typebin currently selected. That is the second dispatch.
Here is the basic class of Typebin:
940 Program
It can be seen that the overloaded add () method will return false. If the method is not overloaded in the derived class, it will always return false, and the caller (currently addtobin ()) will think that the current trash object has not successfully added a collection, so you will continue to find the correct collection.
In each subclass of Typebin, only one overload method will be overload - depending on what is the trash type of the ready to create. For example, CardBoardBin will overload Add (DDCardboard). The overload method will add a garbage object to its collection and return true. All the add () methods remaining in CardBoardBin continue to return false because they have not been overloaded. In fact, if the parameterization type mechanism is used here, the automatic creation of Java code is much easier (using C "template", we don't have to worry about subclass code, or put the addtobin () method into trash Java has still been improved in this regard).
Since this example, the type of garbage has been customized and placed in a different directory, so you need to operate with a different spam file. Here is an exemplary ddtrash.dat:
941-942 page program
The remaining parts of the program are listed below:
942-943 page program
Among them, trashbinset encapsulates a variety of different types of Typebin, as well as a sortintobins () method. All double dispatch events will happen in that method. It can be seen that once the structure is set, it is very simple to classify into a variety of typebin. In addition, the efficiency of two dynamic methods can be higher than other sorting methods.
Note that the convenience of this system is mainly reflected in main (), but also notes that any particular type information is completely independent in main (). All other methods that communicate with the trash base type interface will not be subject to changing interferences that occur in the trash class.
Adding a new type of change to make changes are completely isolated: We will inherit the new type of trash, then inherit a new typebin (this is just a copy, you can simply edit), and finally add a new type to join Trashbinset set initiation process.
16.7 Accessor paradigm
Next, let us think about how to apply a design paradigm with completely different goals to the garbage classification system.
For this paradigm, we no longer care optimize when adding new trash. In fact, this paradigm makes the addition of new TRASH more complicated. Assuming that we have a basic class structure, it is fixed; it may come from another developer or company, we have no right to make any modifications to that structure. However, we hope to add new versatile methods in that structure. This means we generally have to add some things in the interface of the basic class. Therefore, the difficulties we are currently facing is that the method needs to add a method to the basic class, and on the other hand, the basic class cannot be changed. How to solve this problem? "Visitor" paradigm allows us to extend the basic type of interface, the method is to create a separate class structure of the Visitor, which is virtualized to the operations taken in the future. The basic type of task is to simply "reception" accessor and then call the dynamic binding method of the accessor. It looks like this:
945 pages
Now, if V is a Visitable handle pointing Aluminum (aluminum products), then the following code:
Pricevisitor PV = new pricevisitor ();
v.accept (pv);
There will be two multi-portable methods to call: The first Aluminum version of Accept () will be selected; the second is when accept ()-dynamically calls a specific version of Visit () with the basic class Visitor handle V.
This configuration means that the new function can be added to the system in the form of a new subclass of Visitor, and there is no need to contact the TRASH structure. This is the most important advantage of the "Accessor" paradigm: a new velocity function can be added to a class structure, and it is not necessary to change the structure - as long as the Accept () method is installed. Note that this advantage is useful here, but it is not necessarily our preferred option in any case. So at the beginning, it is necessary to judge that this is not what you need.
Note now nothing: Accessor scenarios prevents classification from the master trash sequence to individual type sequences. So we can stay in a single-primary control sequence in a single-handed sequence, just pass that sequence through that sequence, you can reach the desired goal. Although this does not seem to be the original meaning of the visitor paradigm, it does let us achieve a goal that I hope to achieve (avoid using RTTI).
The double student dispatch in the accessor paradigm is responsible for judging TRASH and Visitor types. In the following example, you can see two implementations of Visitor: Pricevisitor is used to judge the total and price, while Weightvisitor is used to track weight.
It can be seen that all of this is implemented with a recycling program, and the improved version is implemented. And like Doubledispatch.java, the TRASH class is kept isolated and created a new interface to add an accept () method:
946 page
ALUMINUM, Paper, Glass, and Cardboard subtypes have implemented accept () methods:
946-947 page program
Because the Visitor's basic class does not require something, it can create it into an interface:
947-948 page program
The remaining part of the program will create a specific Visitor type and send them through a list of TRASH objects:
948-951 page program
Note that the shape of Main () has changed again. There is now only one garbage (TRASH) cartridge. Two Visitor objects are received within each element in the sequence, they will complete their own work. Visitor tracks their own internal data to calculate the total weight and price.
Preferably, when the things are removed from the sequence, in addition to inevitably, there is no time to verify the type of rules. If the parameterization type is implemented in Java, even that modeling can be avoided. One way to distinguish these two programs in comparison with the previously introduced programs are: Only one overload method in which each subclass is created, i.e. add (). Here, each overloaded Visit () method must be overloaded in each subclass of Visitor.
What is more combinations?
There are still many other code, the TRASH structure and the Visitor structure have obvious "Coupling" relationship. However, there is also a high degree of cohesiveness inside the class set represented: only one thing (trash describes garbage or waste, and Visitor description what action is taken on garbage). As a set of excellent design, this is undoubtedly a good start. Of course, it is only possible to experience it when we add a new Visitor type when we add a new Visitor type. But when adding new types of trash, it seems to be a person's hindrance.
The lowness of classes and classes is undoubtedly an important design goal. But as long as you don't care, you may hinder us to get a better design. From the surface, some classes inevitably have some "intimate" relationships. This relationship is usually paired, and it can be called "couple" (COUplet), such as a collection and hemerler. The front trash-visitor is similar to such a "couplet".
16.8 RTTI really harmful?
The various design programs in this chapter have worked hard to avoid using RTTI, which may give you the impression of "RTTI harmful" (still remember poor goto, because people are impressive, there is no put in Java) . But the actual situation is not absolute. To put it correctly, it should be that RTTI is improperly used. We want to avoid the use of RTTI because its error can cause damage to the scalability. The goal proposed beforehand is that it can freely add new types to the system, while ensuring that the surrounding code is as small as possible. Since RTTI is often abused (let it find every type in the system), the code is extended. When you add a new type, you must find all the code to use RTTI. Even if you only miss one, you can't get any help from the compiler.
However, RTTI itself does not automatically generate non-spreading code. Let's take another look at the garbage collection example of the previous mentioned. This time I am preparing to introduce a new tool, I am calling it as Typemap. It contains a HashTable, which contains multiple vectors, but the interface is very simple: you can add (add ()) a new object, you can get (Get ()) a vector, which contains some specific All objects of the type. For this included hash table, its key is the type of corresponding vector. The advantages of this design (according to Larry O'Brien's recommendations) are when encountering a new type, TypeMap will dynamically join a new type. So, no matter when, just add a new type to the system (even during running), it will be accepted correctly.
Our example is also based on the TRASH type structure in C16.Trash (package "(and the trash.dat file used there can be moved here).
952-953 page program
Although the function is strong, it is very simple to define TypeMap. It only includes a hash table, and add () burden most of the work. When adding a new type, the handle of the type of Class object will be extracted. Subsequently, it is determined using this handle whether an VECTOR that is accommodated in the hash table. If the answer is affirmative, you will extract that vector and add objects; contrary, add the Class object and new Vector as a "key-value" pair.
With Keys (), you can get a "enumeration" for all Class objects, and available with GET () can be obtained through the Class object.
The Filler () method is very interesting because it uses Parsetrash.Fillbin () design - not only try to fill a vector, but also use its addtrash () method to fill anything that implements Fillage (filler) interface. . Filter () All things need to do is to return a handle to an interface that implements the Fillage, then passes this handle as a parameter to Fillbin (), just like this:
Parsetrash.Fillbin ("trash.dat", bin.filler ());
In order to produce this handle, we adopted an "anonymous internal class" (which is already in Chapter 7). Since it is not necessary to use a named class to implement Fillage, you only need to belong to an object of the class, so it is very appropriate to use anonymous internal classes here.
For this design, a place to be noted is that although it is not designed to control the classification, it will insert a trash object into BIN when Fillbin () is classified each time.
Most of the Dynatrash classes should be very familiar by learning through those examples of the examples. This time, we no longer place the new trash object into a BIN of the type of Vector. Since the type of bin is TypeMap, the Typemap's internal classification mechanism will be properly classified. Traverse and operate in Typemap and operate every independent vector, this is a quite simple thing:
954 program
As everyone can see, the new type of joining the system will not affect these code at all, nor does it affect the code in TypeMap. This is obviously the most satisfactory solution to solve the problem. Although it does seriously dependence on RTTI, please pay attention to each key-value pair in the hash table. In addition, when we add a new type, it will not fall into the "Forget" to join the correct code to join the correct code, because there is no code to be added.
16.9 Summary
From the surface, since the design like Trashvisitor.java contains more code than the number of early designs, it will leave an impression of high efficiency. What is the purpose of trying to reach a variety of designs should be the focus of our consideration. Design paradigm is particularly suitable for "Things that will change and remain unchanged." "Things that have changed" can represent many changes. The reason why changes may occur, may be due to the program enters a new environment, or because some things of the current environment have changed (for example, "users want to add a new geometry in the current display on the screen"). Or as described in this chapter, changes may be constantly improving the code subject. Although the pre-waste classification has emphasized the addition of new trash to the system, trashvisitor.java allows us to easily add new features, and will not interfere with the TRASH structure. There are still many codes in trashvisitor.java, but add new features in Visitor only requires minimal price. If you often do this activity, there are more code worth it. The discovery of the change sequence is not a normal thing; those analysts generally impossible to predict this change before the initial design of the program. Unless you enter the later period of the project design, some necessary information will not be revealed: sometimes only enters the design or final implementation stage, can experience a more deeper or more unrecognizable need to be in the system. When adding a new type (this is the most important focus of the "Recycling" example), a specific inheritance structure may only be aware of only its own maintenance phase and start the expansion system.
Through the study of the design paradigm, you can experience the most important thing is a point of view that this book has been promoting - versatility is all of OOP (object-oriented programming) - a thorough change has occurred. In other words, it is difficult to "get" versions; and once it is obtained, you need to try all your designs into a specific mold.
The design paradigm is to indicate that "OOP is not only related to the conformity". It should be related to OOP "" There will be changed things that remain unchanged "." Parallelism is a particularly important means to achieve this. Moreover, if the programming language supports the velocity, it is particularly useful (due to direct support, so you don't have to write it yourself, saving a lot of energy and time). But the design paradigm discloses us to reach the basic goals. And once it is familiar with and mastering its usage, you will find yourself more innovative design.
Since "Design Patterns" has created such an important impact on programmers, they have begun to find other paradigms. Such paradigm will inevitably grow as time off. Jimcoplien (http://www.bell-labs.com/~cope Home Bist Author) Recommend some sites to us, there are many very valuable paradigm descriptions:
http://st-www.cs.uiuc.edu/users/patterns
http://c2.com/cgi/wiki
http://c2.com/ppr
Http://www.bell-labs.com/people/cope/patterns/process/index.html
http://www.bell-labs.com/cgi-user/orgpatterns/orgpatterns
http://st-www.cs.uiuc.edu/cgi-bin/wikic/wikic
http://www.cs.wustl.edu/~schmidt/patterns.html
Http://www.espinc.com/patterns/overview.html
At the same time, please pay attention to the first year of authoritative design paradigm, named PLOP. The meeting will publish many academic papers, and the third session has been held at the end of 1997. All information on the meeting is published by Addison-Wesley. 16.10 practice
(1) Create a class using SingletonPattern.java, create a class and manages yourself fixed number of objects.
(2) Add a class named Plastic (plastic) to Trashvisitor.java.
(3) Add a Plastic (plastic) class to Dynatrash.java.