Pattern Refactoring This chapter we will focus on the problem of designing design patterns by gradually evolving. That is, we will use a relatively rough design as the initial solution and then check this solution, which uses different design modes for this problem (some models are feasible, some are inappropriate). During the process of finding a better solution, the most critical problem is, "What is changed?" This process is a bit like Martin Fowler in "Refactoring: Improved Code Design". That (although he is discussing the reconstruction) through the code snippet rather than the mode level design. Start with a solution, when you find this solution, you cannot fix it when you need it. Of course, this is a natural practice, but for process-based computer programming, it is quite difficult. Everyone has more indicated that object-oriented programming is a "good thing" for code reconstruction and design reconstruction. .
The nature of the problem of simulating a waste recycle regenerator is such that the waste is thrown into the trash in the case of unsociated, so the specific category information is lost. However, in order to give these scraps correctly, those specific categories information have to be recovered. At first, we use RTTI (THINKINGIN JAVA Second Edition Chapter 12) as a solution. This is not a designed designed to complete because it has some additional limits. It is also because of these restrictions is more interesting to this problem - it is more like those thorny problems you may encounter in your work. Additional restrictions are that they are mixed when trash recycling plant is transported. Our program must simulate waste classification. This is where RTTI is required, you have a lot of waste fragments that can't name the name, and our program needs to find their exact type.
//: Refactor: Recyclea: Recyclea.java
// recycling with rtti.
Package refactor.recyclea;
Import java.util. *;
Import java.io. *;
Import junit.framework. *;
Abstract class trash {
PRIVATE DOUBLE Weight;
Trash (double wt) {weight = wt;}
Abstract Double getValue ();
Double getWeight () {return weight;
// sums the value of trash in a bin:
Static void Sumvalue (Iterator IT) {
Double Val = 0.0F;
While (it.hasnext ()) {
// One Kind of RTTI:
// a Dynamically-Checked CAST
TRASH T = (trash) it.next ();
// Polymorphism in action:
VAL = T.GetWeight () * t.GetValue ();
System.out.println (
Weight of "
// using rtti to get Type
// Information about the class:
T. GetClass (). getname ()
"=" T.GetWeight ());
}
System.out.println ("Total Value =" VAL);
}
}
Class aluminum extends trash {static double val = 1.67f;
Aluminum (Double WT) {Super (WT);
Double getValue () {return val;}
Static void setValue (double newval) {
Val = newval;
}
}
Class paper extends trash {
Static Double Val = 0.10f;
Paper (double wt) {super (wt);
Double getValue () {return val;}
Static void setValue (double newval) {
Val = newval;
}
}
Class Glass Extends Trash {
Static Double Val = 0.23F;
Glass (double wt) {super (wt);
Double getValue () {return val;}
Static void setValue (double newval) {
Val = newval;
}
}
Public Class Recyclea Extends Testcase {
COLLECTION
bin = new arraylist (),
Glassbin = new arraylist (),
Paperbin = new arraylist (),
Albin = new arraylist ();
Private static random rand = new random ();
Public recyclea () {
// Fill Up The THE TRASH BIN:
For (int i = 0; i <30; i )
Switch (Rand.Nextint (3)) {
Case 0:
bin.add (New
Aluminum (Rand.Nextdouble () * 100));
Break;
Case 1:
bin.add (New
Paper (rand.nextdouble () * 100));
Break;
Case 2:
bin.add (New
Glass (rand.nextdouble () * 100));
}
}
Public void test () {
Iterator sORTER = bin.iterator ();
// sort the trash:
While (sORTER.hasNext ()) {
Object t = sORTER.NEXT ();
// rtti to show class membership:
IF (TinstanceOf Aluminum)
Albin.add (t);
IF (t instanceof paper)
Paperbin.add (t);
IF (T INSTANCEOF GLASS)
Glassbin.add (t);
}
Trash.sumvalue (albin.iterator ());
Trash.sumvalue (Paperbin.Iitrator ());
TRASH.SUMVALUE (Glassbin.iterator ());
Trash.sumvalue (bin.iterator ());
}
Public static void main (string args []) {
Junit.textui.teStrunner.Run (Recyclea.class);
}
} ///: ~
On the source code list with this book, the file above is in the Recyclea subdirectory, and Recyclea is a branch of the Refactor subdirectory. Unpacking tools will put it in the appropriate subdirectory. The reason for this is that this chapter has rewritten this particular example many times, put each version in their own directory (by using the default package of each directory, it is also very simple) to avoid class name conflicts . The program creates several ArrayList objects to store the reference to the trash object. Of course, ArrayLists actually stores the Objects object so they can store anything. They store the trash object (or the object derived from trash) is just because of your careful wing, you don't pass the things other than trash to them. If you put the "error" in ArrayList, you won't get warnings or errors in compilation moments - you can only find these errors through exceptions at runtime.
When the reference to the trash object is added to the ArrayList, they lose specific category information, which is just a reference to the Object object (they are Upcast). However, due to the presence of the polymorphism, it still generates the right behavior when calling the dynamic binding method through the Iterator Sorter, once the final Object object is Cast returned to the TRASH object, trash. Sumvalue () also uses an Iterator to complete Operation of each object in ArrayList.
First put different types of trash objects Upcast and put them in a container that can store base classes, then come out again, so it looks stupid. Why not put the TRASH object in the appropriate container at the beginning? (In fact, this is a confusing place for garbage recycling this example). It is easy to make such a change to the above programs, but sometimes it is very beneficial for the structure and flexibility of certain systems.
The above program meets the design requirements: it works. If you only require a one-time solution, the method above is fine. However, practical procedures usually need to evolve over time, so you have to ask, "If the situation changed?" For example, now the cardboard has become a useful recyclable item, how to It integrates to the above system (especially when the program is big and complicated). Because the code in the Switch statement is scattered throughout the program, you will find the code for all types of checks every time you add a new type. If you miss a compiler, it will not be reported. The wrong way provides you with any help.
Here is a test for each type, in fact, a misuse of RTTI. If you just test it because a subtype requires special treatment, it may be appropriate. But if you are testing every type of Switch statement, you may have missed some important things, and this will be destined to make your code more difficult to maintain. Next section, we will see how this program has become more flexible through several phases. For programming, this is a valuable example.
Improve the existing design IMPROVING THE Design "Design Mode" book, is surrounding "What is the continuous evolution of the program will change?" This problem will organize solutions. This is usually one of the most important issues for any design. If you can build your system around the answer to this question, you will bring you two benefits: not only your system is easy to maintain (and cheap), and you are also likely to create a reusable component. This other is easier to build. This is an object-oriented programming original, but it will not occur automatically; it requires your thinking and insight of the problem. Let's take a look at how it happened in the process of perfecting the system. For our waste recycling system, "What is changed?" The answer to this question is very common: more types of (waste) will be added to the system. That is, the ultimate goal of this design is to make the added new types as possible. For waste recycling procedures, what we want to do is to encapsulate all places involved in specific type information, so that any changes can be placed in those packages. The final result is that this process is also a considerable extent makes the code of the rest of the program become neat.
More objects "make more objects" will lead to a commonly used object-oriented design principle, I first heard from Grady Booch: "If your design is too complicated, then more objects." The principle not only violates intuition and is simple to be ridiculous, but it is the most useful guidelines I have ever seen. (You may have aware of "more objects" often equivalent to add another intermediate layer. ") Generally, if you find which place code is very messy, you can consider what kind of class can make it Never. The sorting code often brings another advantage that the system will have better structural and flexibility.
The TRASH object was originally created in the Switch statement of the main () function.
For (int i = 0; i <30; i )
Switch ((int) (Rand.Nextint (3)) {
Case 0:
bin.add (New
Aluminum (Rand.Nextdouble () * 100));
Break;
Case 1:
bin.add (New
Paper (rand.nextdouble () * 100));
Break;
Case 2:
bin.add (New
Glass (rand.nextdouble () * 100));
}
There is no doubt that the above code is significant, and you must change this code when you join the new type. If you often need to add a new type, the better solution is to use a separate method (Method), this method uses all the necessary information to generate a reference to a certain type of object, this reference will be called a trash by Upcast Object. The "Design Mode" is mentioned when this method is called, it is called Creational Pattern (there are several types of creation modes). The specific mode to be used here is a variant of the factory method (Factory Method). Here, the factory method is a static member function of TRASH, while more cases it exists as a method of derived class.
For (int i = 0; i <30; i )
Switch ((int) (Rand.Nextint (3)) {
Case 0:
bin.add (New
Aluminum (rand.nextdouble () * 100);
Case 1:
bin.add (New
Paper (rand.nextdouble () * 100));
Break;
Case 2:
bin.add (New
Glass (rand.nextdouble () * 100));
}
There is no doubt that the above code is significant, and you must change this code when you join the new type. If you often need to add a new type, the better solution is to use a separate method (Method), this method uses all the necessary information to generate a reference to a certain type of object, this reference will be called a trash by Upcast Object. The "Design Mode" is mentioned when this method is called, it is called Creational Pattern (there are several types of creation modes). The specific mode to be used here is a variant of the factory method (Factory Method). Here, the factory method is a static member function of TRASH, while more cases it exists as a method of derived class.
For (int i = 0; i <30; i )
Switch ((int) (Rand.Nextint (3)) {
Case 0:
bin.add (New
Aluminum (Rand.Nextdouble () * 100));
Break;
Case 1:
bin.add (New
Paper (rand.nextdouble () * 100));
Break;
Case 2:
bin.add (New
Glass (rand.nextdouble () * 100));
}
There is no doubt that the above code is significant, and you must change this code when you join the new type. If you often need to add a new type, the better solution is to use a separate method (Method), this method uses all the necessary information to generate a reference to a certain type of object, this reference will be called a trash by Upcast Object. The "Design Mode" is mentioned when this method is called, it is called Creational Pattern (there are several types of creation modes). The specific mode to be used here is a variant of the factory method (Factory Method). Here, the factory method is a static member function of TRASH, while more cases it exists as a method of derived class.
For (int i = 0; i <30; i )
Switch ((int) (Rand.Nextint (3)) {
Case 0:
bin.add (New
Aluminum (Rand.Nextdouble () * 100));
Break;
Case 1:
bin.add (New
Paper (rand.nextdouble () * 100));
Break;
Case 2:
bin.add (New
Glass (rand.nextdouble () * 100));
}
There is no doubt that the above code is significant, and you must change this code when you join the new type. If you often need to add a new type, the better solution is to use a separate method (Method), this method uses all the necessary information to generate a reference to a certain type of object, this reference will be called a trash by Upcast Object. The "Design Mode" is mentioned when this method is called, it is called Creational Pattern (there are several types of creation modes). The specific mode to be used here is a variant of the factory method (Factory Method). Here, the factory method is a static member function of TRASH, while more cases it exists as a method of derived class. The idea of Factory Method mode is this. You pass the critical information you need to create an object to it, then it will pass (already UPAST to base class) to pass it as a return value to you. Then you can take advantage of this object. In this way, you even never need to know the exact type of the object being created. In fact, Factory Method hides its type information in order to prevent you accidentally misused. If you want to manipulate the object without using a polymorphism, you must explicitly use RTTI and Casting.
But there will be some small problems, especially when you use a more complicated method (not listed here), define Factory Method in the base class and write it in the derived class.
What should I do if the information you need to derive class is required (than base class) more or different parameters. What should I do?
"Creating more objects" can solve this problem. In order to implement the Factory Method mode, the Trash class adds a new method called Factory. In order to hide the data needed to create objects, a Messenger class is added, which carries all the information necessary to create the appropriate trash object (when the book starts, we call Messenger as a design pattern, but it is indeed Too simple, maybe you don't want to upgrade it to such a high height). Below is a simple implementation of Messenger:
Class Messenger {
INT TYPE;
// Must Change this to Add ANOTHER TYPE:
Static firm int max_num = 4;
Double Data;
Messenger (int Typenum, Double Val) {
TYPE = Typenum% max_num;
Data = VAL;
}
}
The only task of the Messenger object is to save the information it needs for the factory () method. Now, if a Factory method requires more or different information in order to create a new type of trash object, the Factory () interface is not necessary to change. The Messenger class can be changed by adding new data and new constructors, or using a more typical object-oriented method - Subclassing.
The factory () method in this simple example looks like the following:
Static Trash Factory (Messenger i) {
Switch (i.type) {
Default: // to quiet the compiler
Case 0:
Return New Aluminum (I.DATA);
Case 1:
Return New Paper (I.DATA);
Case 2: Return New Glass (I.DATA);
// Two Lines Here:
Case 3:
Return New Cardboard (I.DATA);
}
}
Here, you can easily determine the exact type of the object, but you can imagine a more complex system, in which the factory () method uses complex and unknown algorithms. The key issue is that these things are now hidden in the same place. When adding new types, you are clear to change here.
Now, create a new object is more simpler than in the main () function.
For (int i = 0; i <30; i )
bin.add
Trash.Factory
NEW MESSENGER
Rand.Nextint (Messenger.max_num),
Rand.nextdouble () * 100))));
Creating a Messenger object is to use it to deliver data to the factory () method, then Factory () method creates a type of trash object in the heap (HEAP) and returns the reference what added to the arraylist bin.
Of course, if you change the number and type of the parameters, the above code needs to be changed, but if the Messenger object is automatically generated, you can avoid such a change. For example, it is possible to use a constructor to pass the arraylist to the Messenger object (or directly to the factory () method for the ArrayList. Do you need to resolve and verify the incoming parameters at runtime, but it does provide maximum flexibility. From this code you can see that Factory is responsible for solving which "a series of changes" issues: If you add a new type to your system (so-called changes), you must change the code within the factory, that is, Factory isolates the effects of this part of the change.
To Be Continued ... catalog