Example code of this article: github.com/JamesZBL/ja…
The Decorator pattern is used to dynamically add some additional responsibilities to an object. The Decorator pattern is more flexible in terms of adding functionality than subclassing. Decorator patterns extend the functionality of objects in a transparent way to clients and are an alternative to inheritance relationships.
Pure decorative patterns are hard to find, and most decorative patterns are “translucent” rather than fully transparent. In other words, the decorator pattern is allowed to change the interface and add new methods. Translucent decoration mode is intermediate between decoration mode and adapter mode. The purpose of the adapter pattern is to change the interface of the class under consideration. It can also enhance or change the functionality of the class under consideration by rewriting one or more methods, or by adding new methods. Most decoration modes are actually semi-transparent decoration modes, also known as semi-decoration, semi-adapter mode.
Applicable scenario
The Decorator pattern is used in the following cases
-
Add responsibilities to a single object dynamically and transparently without affecting other objects.
-
Address responsibilities that can be undone.
-
When the method of subclass generation cannot be used to extend. In one case, there may be a large number of independent extensions, and the number of subclasses to support each combination will explode. Another case might be because the class definition is hidden, or the class definition cannot be used to generate subclasses.
Key points of pattern
Part of the
-
Component: Defines an interface for objects to which responsibilities can be dynamically added.
-
ConcreteComponent: Define an object to which you can add responsibilities.
-
Decorator: Hold a reference to a Component object and define an interface consistent with the Component interface.
-
ConcreteDecorator: Adds responsibilities to a component.
Cooperative principle
- The Decorator forwards the request to its Component object and possibly performs some additional actions before and after forwarding the request.
The example analysis
[image uploaded outside the station…(image-A0AD13-1526278884853)]
A blacksmith and a carpenter make a hammer at the same time. In the first scheme, the carpenter makes the handle and the blacksmith makes the head. The second option is for the blacksmith to make the handle of the hammer before the head (assuming the carpenters here only know how to make the handle). The manufacturing process is divided into three parts: 1. Initial inspection of materials; 2. Fabrication and installation of parts for subsequent operation; 3. Check again after completion to ensure there are no quality problems.
First define the “action” interface, which includes two checks and installation operations.
/ * * * assembly line operation behavior of the interface * /
public interface Operation {
void checkBefore(a);
void join(a);
void chekcAfter(a);
}
Copy the code
Now only the carpenter makes the hammer handle, defining a carpenter’s operation class CarpenterOperation
/** ** Carpenter's work */
public class CarpenterOperation implements Operation {
private static final Logger LOGGER = LoggerFactory.getLogger(CarpenterOperation.class);
@Override
public void checkBefore(a) {
LOGGER.info("Check the wood");
}
@Override
public void join(a) {
LOGGER.info("Forge the hammer");
}
@Override
public void chekcAfter(a) {
LOGGER.info("Check the finished hammer handle."); }}Copy the code
For some reason, the blacksmith decided to make his own hammers, and now the blacksmith doubles as a carpenter. Defines a blacksmith action class HammerSmith
/** * blacksmith */
public class HammerSmithOperation implements Operation {
private static final Logger LOGGER = LoggerFactory.getLogger(HammerSmithOperation.class);
private Operation previousOperation;
public HammerSmithOperation(Operation previousOperation) {
this.previousOperation = previousOperation;
}
@Override
public void checkBefore(a) {
previousOperation.checkBefore();
LOGGER.info("Check the iron");
}
@Override
public void join(a) {
previousOperation.join();
LOGGER.info("Forge the hammer head.");
}
@Override
public void chekcAfter(a) {
previousOperation.chekcAfter();
LOGGER.info("Inspect finished hammer heads"); }}Copy the code
The interface of “operation” is also realized. Each operation of the blacksmith contains the corresponding operation of the carpenter, which is equivalent to adding a layer of wrapping and expansion to the operation of the carpenter. This wrapper is decoration in the Decorator pattern.
Now let the carpenter and the blacksmith perform a series of operations
/** * Decorator */
public class Application {
private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
LOGGER.info("Hammer handles made only by carpenters.");
Operation carpenter = new CarpenterOperation();
carpenter.checkBefore();
carpenter.join();
carpenter.chekcAfter();
LOGGER.info("The handle and head of the hammer were made by the blacksmith.");
Operation hammerSmith = newHammerSmithOperation(carpenter); hammerSmith.checkBefore(); hammerSmith.join(); hammerSmith.chekcAfter(); }}Copy the code
Output the following
Only the carpenter makes the hammer handle check the wood hammer handle check the finished hammer handle the blacksmith makes the hammer handle and hammer head check the wood check the iron hammer handle make the hammer head check the finished hammer handle check the finished hammer headCopy the code
The effect
advantages
1. Both decorator patterns and static inheritance mechanisms add new functionality to existing classes, but decorator patterns can be combined in more flexible ways than static inheritance. Decoration mode can be run to determine whether a decoration needs to be added or removed and what decoration needs to be added. Static inheritance does not have the same flexibility, as the extension of a class’s functionality is determined before it is run.
2. Thanks to the flexibility and convenience of the combination of decoration modes, we can combine various decoration classes, thus creating a variety of behavior sets more easily and realizing a variety of functions.
disadvantages
1. The decorator’s objects are fundamentally different from the objects it decorates, and the decorator pattern generates many objects, making it difficult to distinguish between them
2. The use of the same identifiers makes it more difficult to understand the program and the troubleshooting process
Personal blog updates synchronously, for more technical sharing please pay attention to: Bao Le Cheng’s blog