Wrap Class
When using a wrap method, we want to add a new behavior that can be added around the method, such as before or after the existing code.
However, there are times when we need to add behavior in more than one method, like logging before or after some, or all public methods.
Description and how to apply
There are at least 2 ways of doing it:
1. Using the Decorator Pattern
When using the **Decorator Pattern**, we want to make sure both classes implement the same interface or inherit from the same base class, because we want the subclass to have the same “public” interface. This is because a Decorator class can be used interchangeably between the other Decorators. For instance, having a logging decorator or a performance decorator should be as easy as just pass one instance or the other in a class constructor, and its use should be transparent.
2. Aggregating the current class
If you think you want to be more specific about the class being refactored,
then a Wrap Class just wrapping the existing one should be enough. In this case, you don’t need to *extract interface* since the class is passed in the constructor parameters and used in the method. In this approach, you can do a “method rename”, for instance, if the original method was called *process*, the new exposed method could be *processWithLogging*. Another advantage is that in the constructor, you can pass other information useful for the new behavior, something that in the Decorator Pattern can “violate” the Decorator Pattern principle.
Which one?
From the examples below you can observe the Aggregating solution is more specific than the Decorator. Both will work, but if you need help choosing, choose the Decorator if you think that other changes will happen in the future that can be added creating more Decorators, and/or the updates are just adding new behavior, and probably you expect changes in multiple methods, like logging every public method. Use the Aggregating solution if the change is very specific, just in a few places and you don’t expect great changes in the future for these methods.
Examples
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// // Existing code // public interface PaymentProcessor { void pay(); } public class CustomerPaymentProcessor implements PaymentProcessor { public void pay() { System.out.println("Pay it forward!"); } } |
Using the Decorator Pattern
You just have to replace the calls to CustomerPaymentProcessor with the Decorator, sice both implement the same interface.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// // Now the decorator (from the code is easy to observe how easy is to create any decorator) // public class LogPaymentDecorator implements PaymentProcessor { private PaymentProcessor paymentProcessor; public LogPaymentDecorator(PaymentProcessor paymentProcessor) { this.paymentProcessor = paymentProcessor; } public void pay() { System.out.println("You are starting to pay"); paymentProcessor.pay(); System.out.println("Ok, now we are even ;)"); } } |
Using a Wrapper Class
Here, the solution is specific to the code refactoring being done, it cannot be reused.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// // The Warp Class // - You can observe this solution as more specific to the target class than the Decorator // public class LoggingCustomerPaymentProcessor private CustomerPaymentProcessor customerPaymentProcessor; public LoggingCustomerPaymentProcessor(CustomerPaymentProcessor cpp) { CustomerPaymentProcessor = cpp; } // You can give the name you want to the method public void loggedPayWithStars() { System.out.println("You are starting to pay"); customerPaymentProcessor.pay(); System.out.println("Ok, now we are even ;)"); } } |