Decorate code in three ways

Source: Internet
Author: User
Summary

This article describes three different implementations of the decorator mode: inheritance, encapsulation, and plug-in. The advantages and disadvantages of each implementation method are discussed in depth.

Original article: three approaches for decorating your code

Everyone who has read gof's famous design patterns knows the decorator model. Now, let's temporarily
Forget the concept of decorator, and try to understand the decorator mode from our development experience.

Decorator is another thing (or person) that is used to decorate one thing (or person ). A decorator directly changes
The responsibility or feature of the decoration object, but the attributes of the decorated object cannot be changed. For example, a frame can be decorated
Film, cosmetics can decorate the girl's face and so on.

From our professional perspective, we will discuss some examples:

1. jscrollpane can be used to decorate the view section of jcomponent. Jcomponent itself will not be changed, but
A new property (scroll ).

2 bufferedinputstream is the decoration of inputstream, and bufferedinputstream is
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 messages to the log file when clicked.
Debugbutton is the decoration of jbutton because it directly changes jbutton but does not change its own attributes.

4 again, for example, scroloverbutton, it adds a behavior of moving the mouse over. It is flat when the mouse moves out.
It has a raised border. Obviously, scroloverbutton is also the decoration of jbutton.

Now, we know that the decorator may have three different implementations:

1 Inheritance)
2 encapsulation (wrapper)
3 External)

This article will discuss each implementation model and its advantages and disadvantages.

Inheritance
For developers, the most intuitive decorator implementation is to write a derived class that inherits from the decorated class and
Assign new responsibilities. The new responsibility can be achieved by adding methods or modifying existing methods.

Public ClassDebugbuttonExtendsJbutton
{
PublicDebugbutton ()
{
Addactionlistener (NewActionlistener ()
{
System. Out. println ("Debug message ");
});
}
}

In addition, we can also implement scroloverbutton in the same way: instead of adding actionlistener
Add mouselistener. In the mouselistener callback method, change the border of the jbutton when mouseentered ()
When called, change the border from empetyborder to raisedbevelborder. When the mouseexited () method is called
Then, restore the border from raisedbevelborder to empetyborder.

The implementation of bufferedinputstream is also very simple. Modify the method of each data read to allow it
To read data. If the buffer is empty, it can use the super. Read () method to obtain and fill the data
Buffer zone. Jscrollpane is a little complicated to implement. I will discuss why it is more difficult to implement it in an inherited way.

Discuss the advantages and disadvantages of implementing the decorator mode in inheritance mode:

Advantages
1. We can use this method to implement almost all decorator.
2. Use inheritance to implement the decorator mode and retain the original type of the decoration class. This is very important.
. In the inheritance mode, we can still use the type of the decoration class before being decorated. For example, we can
In the application, use scroloverbutton instead of jbutton, but jscrollpane cannot be included in it.
Object.

Disadvantages
1. The Inheritance Method is not direct enough. Suppose we have implemented scroloverbutton and debugbutton,
We also need to implement a button that features both scroloverbutton and debugbutton behavior. What should I do? Use
The only choice for inheritance is to generate a scroloverdebugbutton class. If we have
Whether the implementation of scroloverdebugbutton needs to be retained
Implementation? Because we can add two methods for scroloverdebugbutton to enable or disable debug or scroll-
Over behavior:
 
Public VoidSetdebug (BooleanB );
Public BooleanIsdebug ();

Public VoidSetscrolover (BooleanB );
Public BooleanIsscrolover ();


Further, if we have more decorative functions in the future, we will add new u1, U2,... un behaviors. We are
Not to write a class called u1u2... Unbutton? Does it need to include 2n methods like this:

Public VoidSetu (BooleanB );
Public BooleanGetu ;();

Each time a new behavior (UN + 1) is added to the decorator, two new methods need to be added and the generation of the decorator needs to be modified.
Code implementation. This is obviously contrary to the object-oriented thinking and may have serious consequences. (Note:
Javax. Swing. jbutton is implemented in this way ).

2. The behavior of most visualization objects is specified by style parameters, and style changes are unpredictable. When style occurs
We have to adjust our changes. As described above, the inheritance method may need to change the implementation
Code.

3. Make sure that the original type of the decoration class is not easy. We need to reload each constructor, sometimes even
Static mode. Although this is not difficult, it is always quite troublesome.

Using inheritance to implement the decorator mode is not as simple as we previously imagined. Many times, we do not know
In the future, we need decorator which, as a result, is quite difficult in terms of scalability with the inheritance method.
And conflict with the object-oriented principle. Wrapper)
The main idea of encapsulation implementation is to encapsulate the decorated objects into the decorator mode. Decorator transfers external requests
To encapsulate the decorated object, and execute the new functions before (or after) forwarding, or provide
New Independent methods to implement new features.

Let's go back to the previous example and implement them again in encapsulation mode:

1 bufferedinputstream is an inputstream encapsulation. (For more information, see JDK.
Java. Io. bufferedinputstream class description or source code ). Although bufferedinputstream is actually
A derived class of inputstream. As an encapsulation, The bufferedinputstream constructor obtains another
Inputstream object, and save it as an instance variable, then it 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 encapsulation implementation. It forwards requests to encapsulated components (they are called
Figure ). We cannot use jscrollpane to replace its internal components because it does not support all
View function. For example, getfont () in jscrollpane returns the jscrollpane font instead of the view font.

3. We can use this method to implement debugbutton:

Public ClassDebugbuttonExtendsJbuttonImplementsActionlistener
{
PrivateJbutton butt = NULL;
PublicDebugbutton (jbutton butt)
{
This. Butt = butt;
Butt. addactionlistener (This);
}

// Actionlistener
Public VoidActionreceivmed (actionevent E)
{
System. Out. println ("Debug message for button" + butt );
}

........
/* About 180 methods similar to this need to be provided:
Any jbutton method M (Params)
{
Butt. m (Params)
}
*/

This maintains the type of the decorated object (it inherits from jbutton), but it still does not seem so straightforward.
Note: we cannot use Java. Lang. Reflect. Proxy as a proxy, because jbutton is a class rather than an interface.

Another implementation can be as follows:

Public ClassDebugbuttonExtendsJcomponentImplementsActionlistener
{
PrivateJbutton butt = NULL;
PublicDebugbutton (jbutton butt)
{
This. Butt = butt;
Butt. addactionlistener (This);
}
PublicJbutton getunderlinebutton ()
{
ReturnButt;
}

// Actionlistener
Public VoidActionreceivmed (actionevent E)
{
System. Out. println ("Debug message for button" + butt );
}

........

/*
Some (not many) optional methods can be implemented, such as get/setfont and get/setbackground.
*/

}

This implementation method is quite simple, but such debugbutton is not derived from jbutton, and we cannot use
Debugbutton replaces jbutton. Jscrollpane is implemented in this way.

4. The same problem with debugbutton exists in scroloverbutton. Derived from jbutton may cause additional
Code, but it can maintain the jbutton type. If derived from jcomponent, it can be simpler and more direct, but it cannot
Jbutton type.

We also discuss the advantages and disadvantages of the encapsulation method:

Advantages
As described above, using encapsulation to implement decorator can reduce the required methods and reduce the encoding volume (such
Inputstream ). All the advantages can be attributed to this implementation method which can produce short and refined classes.

1. The implementation is simple enough and can maintain the type of the encapsulated object.
2. Each decorator is independent of other decorator.
3. In many cases, you can use multiple decorators at the same time.
 

Disadvantages
However, for classes with many methods, using encapsulation can also lead to very lengthy class code. For Visualization
Objects, we need to provide hundreds of methods or sacrifice the type of decoration objects.

According to the gof book, wrapper is a real ornament. It is applicable to the decoration of short codes.
Class. For long classes, developers have to make a choice: they provide hundreds of methods to keep the original class of the decorated object
Type? Or sacrifice the type of the decorated object in exchange for simple and refined code? Plug-ins

To describe the implementation of this plug-in, let's take a look at the implementation code of the debugbutton and debugdecorator classes:

Public ClassDebugdecoratorImplementsActionlistener
{
Public VoidDecoratedebug (jbutton butt)
{
Butt. addactonlistenr (This);
}
Public VoidUndecoratedebug (jbutton butt)
{
Butt. removeactonlistenr (This);
}

// Actionlistener
Public VoidActionreceivmed (actionevent EVT)
{
Jbutton src = (jbutton) EVT. getsource ();
System. Out. println ("Debug message for button" + SRC );
}
}

An actionlistener is added to the decoratedebug () method, and the undecoratedebug () method is removed.
Actionlistener. The actionreceivmed () method outputs the debug information.

Now, let's see how to use the above debugdecorator class:

Debugdecorator decor =NewDebugdecorator ();
........
Jbutton mybutt =...

........

// Add external decorator
Decor. decoratedebug (mybutt );

.........

// Remove external decorator
Decor. undecoratedebug (mybutt );

.........

In the same way, we can implement the roloverdecorator class. You can use two decorator in the Code as follows:

Debugdecorator debugdecor =NewDebugdecorator ();
Debugdecorator rolldecor =NewDebugdecorator ();
........
Jbutton mybutt =...

........

// Add debug decorator
Debugdecor. decoratedebug (mybutt );
........

// Add rolover decorator
Rolldecor. decoraterollover (mybutt );

.........

........

// Remove debug decorator
Debugdecor. undecoratedebug (mybutt );
........

// Remove rolover decorator
Rolldecor. undecoraterollover (mybutt );


Note: When you add a new decorator, you can get new actions without changing any code.

We can apply a debugdecorator to any number of jbuttons. In this case, only one debugdecorator instance is required in a JVM. Therefore, debugdecorator can be implemented in the standalone mode.

I call this kind of monomer as a "single ornament". It can (not mandatory) have more than one instance. In principle, a single instance can only have one instance.

Now let's look at the reconstructed debugdecorator:


Public ClassDebugdecoratorImplementsActionlistener
{
Private Static FinalDebugdecorator inst =NewDebugdecorator ();

Public StaticGetinstance ()
{
ReturnInst;
}
Public VoidDecoratedebug (jbutton butt)
{
Butt. addactonlistenr (This);
}
Public VoidUndecoratedebug (jbutton butt)
{
Butt. removeactonlistenr (This);
}

// Actionlistener
Public VoidActionreceivmed (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 );

.........

Add the new decorate () method and undecorateddebugcontainer () method:

Public VoidDecoratedebugcontainer (jcomponent container)
{
Component [] comps = container. getcomponents ();
For(IntI = 0; I <comps. length; I ++)
{
If(Jbutton.Class. Isinstance (comps [I])
{
Comps [I]. addactionlistener (This);
}
}
}

Public VoidUndecoratedebugcontainer (jcomponent container)
{
Component [] comps = container. getcomponents ();
For(IntI = 0; I <comps. length; I ++)
{
If(Jbutton.Class. Isinstance (comps [I])
{
Comps [I]. removeactionlistener (This);
}
}
}

In this way, we can easily apply debugdecorator to containers (such as toolbar.

We can also compile the implementation of roloverdecorator, but bufferedinputstream cannot be implemented because there is no suitable listener or callback method.

Discuss the advantages and disadvantages of the plug-in:

Advantages:

This implementation never needs to change the type of the decorated object.
As long as we need them, we can write any number of decorators and add or remove them as needed.
Each decorator is independent of other decorator.
After adding or removing the decorator, We can get new features without changing any code.
This method can easily adapt to the changing style selection of visual objects.

Disadvantages:
Unfortunately, this method cannot be applied to any object decoration. It is based on the callback of the decorated object (such as the listener) and some other features. In other words, if the decorated object does not have the appropriate features, we cannot apply the plug-in method to decorator implementation.

For a visual object, if we need to change its painting method, we cannot use the plug-in method. For example, if we need to add roundbutton.

Although the external Implementation of the decorator is very simple and easy to use, it also conforms to the object-oriented principle. However, it depends on many features of the decorated object, such as listeners, borders, layout management, and pluggable exterior styles.

Summary

Three different types of decorator implementations are discussed. We can compare them as follows:

I don't know how wise it is to use inheritance to implement the decorator in any situation. It looks simple, but it is difficult in terms of scalability and violates the object-oriented idea. We recommend that you do not use this method to implement the decorator.

The method of encapsulating and implementing the decorator reflects a good object-oriented design and applies to classes with fewer methods. For a long class, the developer must make a painful choice: Is it necessary to write a bunch of code to keep the original type of the decorated object? Or does it discard its original type to get refined code? Moreover, this method is not applicable to visualization objects in many cases.

Plug-ins are easy to use and reflect the object-oriented thinking. However, they can only be used for specific classes. They need to provide some features to support plug-ins, such as listeners, borders, layout managers, and pluggable appearances. It can work very well for visualized objects.

Contact Us

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.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.