asp.net| Interaction | Design Maintenance Two lists
Because we're changing the fill color of the object to achieve the change fill-hot pink button, we maintain a list of two objects that can be drawn: One list is all, and the other is a populated object. We have used the ArrayList class for both lists. The ArrayList object contains a set of object references-such a ArrayList can contain any type of blending in the system.
This actually doesn't help--we want ArrayList to include only the objects that can be drawn/populated. To do this, we set the ArrayList object to private, and then set the process of adding an object to the list to a method that accepts only one dshape.
When you add an object to the list by using the Add method, we add all the objects to wholelist and then check if the object should also be added to the Filledlist collection.
Keep in mind that the Add method (and list) has a type-safe attribute: it only accepts dshape (or types derived from DShape, such as all the types that we created above). You cannot add integers or strings to the list so that we can know that the list contains only the objects that can be drawn. It is very convenient to know this point!
Drawing items
We also have a drawlist method for drawing the objects in the list on the Graphics object that it passes as a parameter. This method has two scenarios: if the list is empty, it draws a string that indicates that the list is empty. If the list is not empty, it traverses the list using a for every constructor and invokes Draw on each object. The actual traversal and drawing code is simple enough, as shown in the following Visual Basic.
Visual Basic
. NET Dim D as DShape
For each d in wholelist
D.draw (g)
Next
C # code is almost exactly the same (fewer lines, of course).
C#
foreach (DShape d in Wholelist)
D.draw (g);
Because the list is encapsulated, we know that it has a type-safe attribute, so you can call only the Draw method without checking the object's type.
Returns a list that can be populated
Finally, our change fills to hot pink (changing the fill color to pink) button requires an array of references to all the objects that can be populated so that the FillBrushColor property is changed. Although you can write a method to traverse the list and change the color to the passed-in value, this time the Dr. GUI chose to return an array of object references. Fortunately, the ArrayList class has a ToArray method that allows you to create a pass-through array. This method gets the type of array element we need--so that it can be passed back to the desired type--ifillable array.
. NET public Function getfilledlist () as IFillable ()
Return Filledlist.toarray (GetType (ifillable))
End Function
In both languages, we use a built-in operator to get a type object of the given kind--in C #, typeof (IFillable), and GetType (ifillable) in Visual Basic.
The calling program uses this array to iterate through the populated object reference array. For example, the Visual Basic code that changes the fill color to pink looks like this:
Dim filledlist as ifillable () = Drawinglist.getfilledlist ()
Dim I as IFillable
For all I in filledlist
I.fillbrushcolor = Color.hotpink
Next
Helper methods and classes to break out common code
You may notice that the Draw and Fill methods have many common code. Specifically, the code that creates pens or brushes in each class, the code that establishes the try/finally block, and the code that cleans the pen or brush is the same-the only difference is the actual method that is invoked when drawing or populating. (because the using syntax in C # is very concise, the amount of extra code is not obvious.) In Visual Basic. NET, there may be a line of special code in every five-element code that is the same in all implementations.
In summary, if there is a large number of duplicate code, it is necessary to seek to break out the common code to form a common subroutine shared by all classes. There are a lot of such methods, and the Dr. GUI is very happy to show you two of them. The first method is used only for classes, and the second method can be used for classes or interfaces, in this case only for interfaces.
Method 1: The public entry point invokes the virtual method
In the first approach, we took advantage of the fact that classes (different from interfaces) could contain code. So we provide an implementation of the Draw method for creating pens, an exception handler and Dispose, and then invoke the Abstract/mustoverride method that actually makes the drawing. Rather, we changed the Dshapes class to accommodate the new Draw method, and then declared the new JustDraw method:
Public MustInherit Class DShape
' Draw is not virtual, it seems a little unusual ...
' Draw should have been abstract (MustOverride).
' But this method is the frame of the drawing, not the drawing code itself,
' The drawing code is completed in JustDraw.
' Also note that this means that these classes have
' Different interfaces, although they do the same work.
Public Sub Draw (ByVal g as Graphics)
Dim p = New Pen (pencolor)
Try
JustDraw (g, p)
Finally
P.dispose ()
End Try
End Sub
' Here's the part that needs to be polymorphic--so it's abstract.
Protected MustOverride Sub JustDraw (ByVal g as Graphics, _
ByVal p as Pen)
Protected Bounding as Rectangle
Protected PenColor as Color ' should also have attributes
' It should also have the means of moving, resizing, and so on.
End Class
A noteworthy interesting place: The Draw method is not virtual/overridable. Because all derived classes will complete this part of the drawing in the same way (if the drawing is on Graphics [as defined in this example], you must assign and clean the pen), so it does not need to be virtual/overridable.
In fact, the Dr. GUI thinks that in this case, Draw should not be virtual/overridable. If you are sure you want to overwrite the Draw behavior (not just the justdraw behavior), you can set it to virtual/overridable. In this case, however, there is no reason to overwrite Draw behavior, and if the programmer is encouraged to overwrite it can also create pitfalls-they may not handle the pen properly, or use other methods to draw objects instead of calling JustDraw, which violates the assumptions we have built into the class. Therefore, setting Draw to non-virtual (without this option in Brand J, by the way) may reduce the flexibility of the code but be more reliable--the Dr. GUI thinks that in this case it's worth it.
The typical implementation of JustDraw is as follows:
Protected Overrides Sub JustDraw (ByVal g as Graphics, ByVal p as Pen)
G.drawellipse (p, bounding)
End Sub
As you can see, we have achieved the desired simplicity of the derived class implementation. (The implementation in the populated class is only slightly more complex--see later.) )
Notice that we added an additional public method to the interface JustDraw, which, in addition to the Graphics object to be drawn, refers to the Pen object we created in Draw. Because the method needs to be abstract/mustoverride, it must be public.
This is not a big problem, but it does change the public interface of the class. So even if this method of decomposing public code is very simple and convenient, you should choose as many other methods as possible to avoid changing the public interface.
Method 2: The virtual method invokes the public helper method, using the callback
The complexity of the code is similar when implementing the interface's Fill method: There may be a line of special code in every six lines of code that is the same in all implementations. But we can't put public implementations into interfaces, because interfaces are just declarations, they don't contain code or data. In addition, the method listed above is unacceptable because it changes the interface--we may not want to do this, or because it's an interface created by someone else, we can't change it at all!
So, we need to write a helper method to set up and callback our class for the actual padding. For this example, the Dr. GUI places the code in a separate class so that any class can use that code. (If this method is used to implement Draw, the helper method can be implemented as a private method in an abstract base class.) )
For the time being, the following are the classes we created:
' Note that the help provided by this delegate still has polymorphic behavior.
Class Fillhelper
Public Delegate Sub Filler (ByVal g as Graphics, ByVal B as Brush)
Shared Sub Safefill (ByVal i as ifillable, ByVal G as Graphics, _
ByVal F as filler)
Dim B = New SolidBrush (i.fillbrushcolor)
Try
F (g, B)
Finally
B.dispose ()
End Try
End Sub
End Class
Our helper method invokes Safefill, which accepts a padded object (note that we use the IFillable interface type instead of the DShape, which can only pass the populated object), a Graphics on which to draw, and a The private variable for the delegate. We can treat delegate as a reference to an opposing method (not an object)--If you frequently use C or C + + programming, you can see it as a function pointer with a type-safe attribute. You can set delegate to point to any method that has the same parameter type and return value, either an instance method or a static/shared method. When delegate is set to point to the appropriate method (for example, when Safefill is invoked), we can invoke the method indirectly through delegate. (Incidentally, there is no delegate in Brand J, which can be very difficult and inflexible if you use this method.)
The declaration of the delegate type filler is above the class declaration-it is declared as a method that does not return any content (a Sub in Visual Basic. NET) and passes Graphics and Brush as parameters. We'll discuss delegate in a future column.
The Safefill operation is simple: it assigns brushes and sets try/finally and Dispose to public code. It does all sorts of things by invoking the method referenced by the delegate we receive as parameters: F (g, B).
To use this class, you need to add a method that can be called by delegate to the populated object class and ensure that the reference (address) of the method is passed to Safefill, and we will call Safefill in the fill implementation of the interface. Here's the code for DFilledCircle:
Public Sub Fill (ByVal g as Graphics) Implements Ifillable.fill
Fillhelper.safefill (Me, G, AddressOf Justfill)
End Sub
Private Sub Justfill (ByVal g as Graphics, ByVal B as Brush)
G.fillellipse (b, bounding)
End Sub
In this way, Ifillable.fill is invoked on the object when the object needs to be populated. It will call our Fill method, and the Fill method calls Fillhelper.safefill, which passes a reference to our populated object, the Graphics object that is passed to draw on it, and a reference to the method that actually completes the fill--in this case, This method is a private Justfill method.
Safefill then sets the brush and call through the delegate-Justfill method, and the Justfill method fills and returns a value by calling Graphics.FillEllipse. Safefill will clean the brush and return to Fill,fill and return to the caller.
Finally, JustDraw, which is similar to the Draw in the original version, because we both called Fill and called the Draw method of the base class (which we did before). The following are the relevant code:
Protected Overrides Sub JustDraw (ByVal g as Graphics, ByVal p as Pen)
Fill (g)
Mybase.justdraw (g, p)
End Sub
Keep in mind that the complexity of assigning brushes and pens lies in its handling in helper functions--in Draw, in the base class, and in Fill, which is in the helper class.
If you think this is more complicated than it used to be, then it does. If you think that because of the extra calls and the need to handle delegate, the speed is slower than before, and it does. There are always a lot of things to weigh in life.
Well, is it worth it? Maybe it's worth it. This depends on the complexity of the public code and how many times the code needs to be repeated. In other words, you need to weigh. If we decide to delete the try/finally and clean the pens and brushes only after the drawing is finished, the code will be very simple, and these methods will not work. And in C #, the using statement is very concise and we don't have to bother using these methods. The Dr. GUI believes that you can use or not use try/finally in Visual Basic, which is intended to show you these methods for use when you encounter a large number of common code.
The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion;
products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the
content of the page makes you feel confusing, please write us an email, we will handle the problem
within 5 days after receiving your email.
If you find any instances of plagiarism from the community, please send an email to:
info-contact@alibabacloud.com
and provide relevant evidence. A staff member will contact you within 5 working days.