Summary
This article describes three different implementations of Decorator mode: inheritance, packaging and plug-in. In-depth discussion, the advantages and deficiencies of each implementation. Original link:
Three Approaches for Decorating your code
Everyone who reads GOF's famous design pattern will know the Decorator mode. Now, let us forget what the Decorator concept is known, try to understand the Decorator mode from our development experience. Decorator is another thing (or human) for decorating a thing (or person). A Decorator directly changes the duties or feature of the decorative object, but cannot change the own properties of the decorative object. For example: a frame can be decorated, cosmetics can decorate the girl's face, and so on. Discuss some examples from our professional perspective: 1 JScrollPane can decorate the view part of JComponent. JComponent itself will not be changed, but add a new attribute (can scroll). 2 BufferedInputStream is an INPUTSTREAM or decorative child, itself bufferedinputStream is an InputStream, but it is faster because it provides a cache for data. 3 Consider DebugButton, which is the same as JButton, but it can add a message to the log file when it is clicked. DebugButton is a JButton's decorative subsequent, because it changed JButton directly but did not change its own properties. 4 It's like ScrolloverButton, which increases a mouse slide. When the mouse is moved, it is flat when the mouse passes, it has a raised border. Obviously, ScrolloverButton is also a decorative child of JButton. Now, we know that Decorator may have three different implementations: 1 Inheritance 2 Package 3 External This article will discuss each implementation model and its advantages and disadvantages.
inherit
For developers, the most intuitive Decorator implementation is: Write a derived class, which inherits the self-decorated class and emphasizes new duties. New duties can be implemented by adding methods or modifying existing methods. public class DebugButton extends JButton {public DebugButton () {addActionListener (new ActionListener () {System.out.println ( "debug message");});}} In addition, we can also use the same way to achieve ScrollOverButton: not increase ActionListener, but increases Mouselistener. Change the JButton border in the MouseListener callback method, when the mouseEntered () is called, turn the border from EmPetyBorder to RaiseDbevelborder. When the mouseexited () method is called, the border is restored from RaiseDbevelBorder to EmpetyBorder. It is also very simple for BufferedInputStream. Modify each read data, let it read data from the memory buffer. If the buffer is empty, it can get data and fill the buffer through the super.read () method. JscrollPane, it is a bit complicated to achieve, and I will discuss why it will be more difficult to use inheritance. Discuss the advantages and disadvantages of the inheritance mode to achieve Decorator mode: Advantages 1 We can almost all Decorator can be implemented in this way. 2 Use the inheritance mode to implement Decorator mode, which can retain the original type of decorative class, which is very important. With inheritance, we can still use the type of decorative class before being decorated, for example, we can use ScrolloverButton in our app to replace JButton, but JscrollPane can not replace objects included inside it. Disadvantages 1 use inheritance method still not directly enough. Imagine we implement ScrolloverButton and DebugButton, but we need to implement a button that has both ScrolloverButton features and DebugButton behavior. How to do? With inherited mode our only choice is to repay a scrolloverdebugbutton class. If we have the implementation of ScrolloverDebugButton, do you still need to keep ScrolloverButton or debugButton implementation? Because we can add two method is ScrollOverDebugButton to open or close the debug or scroll-over behavior: public void setDebug (boolean b); public boolean isDebug (); public void setScrollOver (boolean b); public boolean isScrollOver (); and then Further consideration, if we have more decorative functions in the future, add new U1, U2, ... UN behavior. Do we want to write a class, called U1U2 ... unbutton? Is it to include 2N such a method: Public void setu (Boolean B); public boolean getu; (); each additional new behavior (UN 1) The decorator needs to increase two new methods and modify the code implementation of this decorator. This is obviously contrary to object-oriented ideas, which may have serious consequences.
(Note: javax.swing.jbutton is achieved. 2 Many acts of visual objects are specified by style parameters, and the change in style is unpredictable. When the style has changed, we have to adjust your changes. As mentioned above, the way to use inheritance may need to change the code implemented. 3 To ensure that the original type of decorative class is not an easy task. We need to overload each construct, sometimes even static. Although this is not difficult, it is always quite troublesome. Using the inheritance method to implement Decorator mode is not as simple as we have previously imagined. Many times, we don't know which one of the decorators we need in the future, the result is that Decorator inheritance is quite difficult in scalability, and conflicts with object-oriented principles. Wrapper
The most important idea of encapsulation is to encapsulate the decorated object into Decorator mode. Decorator forwards the external request to the packaged object and performs its own new features before forwarding (or), or providing new independent methods to implement new features. Let us return to the examples just and re-put them with a packaging method: 1 BufferedInputStream is an InputStream package (for this point can refer to the Java.Io.bufferedInputStream class in JDK or source). Although in fact, BufferedInputStream is also a derived class of InputStream. As a package, another INPUTSTREAM object is acquired in the configuration of the bufferedInputStream, and it saves it as an instance variable, which can forward the request to this built-in InputStream object. We can use BufferedInputStream in our original use of InputStream. 2 JScrollPane is also an implementation of a package. It forwards the request to the packaged component (they are called view). It should be noted that we cannot use JScrollPane instead of its internal components because it does not support all view features. For example, at GetFont () of JScrollPane is a font of JScrollPane instead of the font of the view. 3 this way we can achieve DebugButton: public class DebugButton extends JButton implements ActionListener {private JButton butt = null; public DebugButton (JButton butt) {this.butt = butt; butt.addActionListener (this);} // ActionListener public void ActionPerformed ("debug message for button" butt);}.......................................... {Butt.m (Params)} * / This maintains the type of decorative object (it inherits from jbutton), but this still looks directly. Note: We cannot use java.lang.Reflect.Proxy as a proxy because JButton is a class rather than an interface.
Another implementation may be so: public class DebugButton extends JComponent implements ActionListener {private JButton butt = null; public DebugButton (JButton butt) {this.butt = butt; butt.addActionListener (this);} public JButton getUnderlineButton () {return butt } // ActionListener Public Void ActionPerformed (ActionEvent E) {System.out.Println ("Debug Message for Button" Butt);}.................... Method, like Get / setFont, Get / setBackground, etc. * /} This implementation is quite simple, but such debugbutton is not derived from JButton, we can't use DebugButton instead of JButton. JscrollPane is implemented in this way. 4 There is also the same problem like DebugButton in ScrolloverButton. Deporting from JButton may result in additional code, but you can keep JButton types, which can be easier and direct from Jcomponent, but it cannot keep JButton types. It also discusses the advantages and disadvantages of the package: Advantages As mentioned above, encapsulate Decorator can reduce the required method to reduce the amount of encoding (icon InputStream). All the advantages can be attributed to this implementation to get a short-sighted class. 1 Realize a lack of simple enough, and can maintain type 2 of the encapsulated object 2 per decorator independently of other decorators. 3 In many cases, multiple decorators can be used at the same time. Disadvantages, however, for those that have many methods themselves, the use of packages can also lead to very lengthy class code. For visualized objects, we need to provide hundreds of methods or sacrifices the type of decorative object. According to the GOF book, the package (Wrapper) is the true decorator. It applies to code short decorative classes. For long classes, developers have to make a choice: Is it providing hundreds of methods to keep the original type of decorative object? Or sacrificed the type of decorative object to exchange simple refined code? Plug-in
In order to describe the implementation of such a plug, and let us look at DebugButton DebugDecorator implementation code class: public class DebugDecorator implements ActionListener {public void decorateDebug (JButton butt) {butt.addActonListenr (this);} public void undecorateDebug (JButton butt ) {Butt.removectonlistenr (this);} // ActionListener
public void actionPerformed (ActionEvent evt) {JButton src = (JButton) evt.getSource (); System.out.println ( "debug message for button" src);}} Method decorateDebug () adds a ActionListener, method undecorateDebug ( The ActionListener is removed. Method ActionPerformed () is responsible for outputting Debug information. Now see how to use the above DebugDecorator class: debugDecorator Decor = new debugDecorator () ;........................... DecorateDebug (MyButt) );..........................................
Decor.undecorateDebug (MyButt);...................................................................................................................................................................................................................................................................... Use two decorators in the code to be like this: debugdecorator debugdecor = new debugdecorator (); debugdecorator rolldecor = new debugDecorator ();........................................................................................................................................................................................................................................... // Add debug decorator
DebugDecor.Decoratedebug (mybutt);............ // Add rollover Decorator
Rolldecor.Decoraterollover (mybutt);...........................
Debugdecor.undecoratedebug (mybutt);.......... // Remove Rollover Decorator
ROLLDECOR.UNDECORATEROLLOVER (MyButt); Note: You can get new behaviors without changing any code in increasing a new decorator. We can apply a debugDecorator to any more JButton. From this point, only one debugDecorator instance is needed in a JVM, so the debugDecorator can be implemented as a single mode. I called this monomer as "monomer decorator", which can be (not necessarily) more than one instance. And in principle monomers can only have an example. Now look reconstructed DebugDecorator: public class DebugDecorator implements ActionListener {private static final DebugDecorator inst = new DebugDecorator (); public static getInstance () {return inst;} public void decorateDebug (JButton butt) {butt.addActonListenr (this); } public void undecorateDebug (JButton butt) {butt.removeActonListenr (this);} // ActionListenerpublic void actionPerformed (ActionEvent evt) {JButton src = (JButton) evt.getSource (); System.out.println ( "debug message for button " SRC);}} Its usage is as follows: jbutton mybutt = ............. // Add External Decorator
DEBUGDECorator.getInstance (). DecorateDebug (mybutt);............. // Remove External Decorator
.......... DebugDecorator.getInstance () undecorateDebug (myButt); the decorate add new () method and undecoratedDebugContainer () methods: public void decorateDebugContainer (JComponent container) {Component [] comps = container.getComponents (); For (int i = 0; i