1. Course objectives
1. Master the characteristics and application scenarios of decorator mode.
2. Master the fundamental difference between decorator mode and adapter mode.
3. Application and implementation principle of observer mode in source code.
4. Understand the advantages and disadvantages of decorator mode and observer mode.
2. Decorator mode
2.1 definition of decorator mode
Decorator pattern refers to attaching functions to objects without changing the original objects, providing a more flexible alternative than inheritance (expanding the functions of the original objects).
It belongs to structural mode
2.2 applicable scenarios of decorator mode
1. It is used to expand the functions of a class or add additional responsibilities to a class.
2. Dynamically add functions to an object, and these functions can be dynamically revoked
2.3 Demo cases
Case of buying pancakes
v1: traditional scheme
@Data public class Battercake { public String getMsg (){ return "A pancake"; } public int getPrice(){ return 5; } } /** * @PackageName: com.raven.pattern.decorator.battercake.v1 * @ClassName: BattercakeWithEgg * @Blame: raven * @Date: 2021-08-07 18:52 * @Description: A pancake and an egg */ public class BattercakeWithEgg extends Battercake{ @Override public String getMsg() { return super.getMsg() + "+ An egg"; } @Override public int getPrice() { return super.getPrice() + 1; } } /** * @PackageName: com.raven.pattern.decorator.battercake.v1 * @ClassName: BattercakeWithSausage * @Blame: raven * @Date: 2021-08-07 18:54 * @Description: One PANCAKE + one ham */ public class BattercakeWithSausage extends Battercake { @Override public String getMsg() { return super.getMsg() + "+ A ham"; } @Override public int getPrice() { return super.getPrice() + 2; } } package com.raven.pattern.decorator.battercake.v1; /** * @PackageName: com.raven.pattern.decorator.battercake.v1 * @ClassName: BattercakeTest * @Blame: raven * @Date: 2021-08-07 18:55 * @Description:The traditional way of test class expands the original class "Battercake", and new classes need to be created constantly * When the requirements change slightly, you need to create a new class to meet the existing requirements */ public class BattercakeTest { public static void main(String[] args) { Battercake battercake = new Battercake(); System.out.println("buy" + battercake.getMsg() + "Total need" + battercake.getPrice() + "element"); battercake = new BattercakeWithEgg(); System.out.println("buy" + battercake.getMsg() + "Total need" + battercake.getPrice() + "element"); battercake = new BattercakeWithSausage(); System.out.println("buy" + battercake.getMsg() + "Total need" + battercake.getPrice() + "element"); // If you need to buy pancakes with two eggs and two hams, you need to create a new class. With the change of needs, there will be more and more classes. } }
V2: use decorator mode
package com.raven.pattern.decorator.battercake.v2; import lombok.Data; /** * @PackageName: com.raven.pattern.decorator.battercake.v1 * @ClassName: Battercake * @Blame: raven * @Date: 2021-08-07 18:49 * @Description:Simulated sales pancake case pancake entity */ @Data public abstract class Battercake { /** * Purchase pancake details * @return */ public abstract String getMsg(); /** * Pancake price * @return */ public abstract int getPrice(); } /** * @PackageName: com.raven.pattern.decorator.battercake.v2 * @ClassName: BaseBattercake * @Blame: raven * @Date: 2021-08-07 19:03 * @Description:Describe the information of a basic pancake */ public class BaseBattercake extends Battercake { @Override public String getMsg() { return "A pancake"; } @Override public int getPrice() { return 5; } }
Decorator
/** * @PackageName: com.raven.pattern.decorator.battercake.v2 * @ClassName: BattercakeDecorator * @Blame: raven * @Date: 2021-08-07 19:05 * @Description: The decorator parent class of pancake regulates the behavior of egg decorator, */ public class BattercakeDecorator extends Battercake { private Battercake battercake; public BattercakeDecorator(Battercake battercake) { this.battercake = battercake; } @Override public String getMsg() { return this.battercake.getMsg(); } @Override public int getPrice() { return this.battercake.getPrice(); } } /** * @PackageName: com.raven.pattern.decorator.battercake.v2 * @ClassName: EggDecorator * @Blame: raven * @Date: 2021-08-07 19:10 * @Description: Garnish with 1 egg */ public class EggDecorator extends BattercakeDecorator { public EggDecorator(Battercake battercake) { super(battercake); } @Override public String getMsg() { return super.getMsg() + "+ An egg"; } @Override public int getPrice() { return super.getPrice() + 1; } } /** * @PackageName: com.raven.pattern.decorator.battercake.v2 * @ClassName: SausageDecorator * @Blame: raven * @Date: 2021-08-07 19:13 * @Description: */ public class SausageDecorator extends BattercakeDecorator { public SausageDecorator(Battercake battercake) { super(battercake); } @Override public String getMsg() { return super.getMsg() + "+ A ham"; } @Override public int getPrice() { return super.getPrice() + 2; } }
main
/** * @PackageName: com.raven.pattern.decorator.battercake.v2 * @ClassName: BattercakeTest * @Blame: raven * @Date: 2021-08-07 19:13 * @Description: Pancake selling case encapsulated by decorator mode */ public class BattercakeTest { public static void main(String[] args) { // After wrapping in decorator mode, you can buy + pancakes of different styles, and there is no need to create more classes after the requirements change Battercake battercake = new BaseBattercake(); System.out.println("buy" + battercake.getMsg() + "Total need" + battercake.getPrice() + "element"); battercake = new EggDecorator(battercake); System.out.println("buy" + battercake.getMsg() + "Total need" + battercake.getPrice() + "element"); battercake = new EggDecorator(battercake); System.out.println("buy" + battercake.getMsg() + "Total need" + battercake.getPrice() + "element"); battercake = new SausageDecorator(battercake); System.out.println("buy" + battercake.getMsg() + "Total need" + battercake.getPrice() + "element"); } }
2.4 comparison between decorator mode and adapter mode
\ | Decorator mode | Adapter mode |
---|---|---|
form | Is a very special adapter pattern | There is no hierarchical relationship, and the decorator mode has hierarchical relationship |
definition | The decorator and the dresser have implemented the same interface, and the main purpose is to retain the OOP relationship after expansion | The adapter has no necessary connection with the Adaptee, and is usually wrapped in the form of inheritance or proxy |
relationship | Satisfy is-a relation | Satisfy has-a relation |
function | Focus on coverage and expansion | Focus on compatibility and conversion |
Design | Pre consideration | Post consideration |
2.5 decorator mode in the source code
Decorator mode in jdk: IO stream
Decorator mode in spring:
TransactionAwareCacheDecorator,
HttpHeadResponseDecorator
Decorator mode in mybatis:
LruCache,FifoCache
2.6 advantages of decorator mode
1. Decorator mode is a powerful supplement to inheritance, which is more flexible than inheritance. It dynamically expands the function of an object without changing the original object, plug and play
2. Different effects can be achieved by using decorative classes and the arrangement and combination of these decorative classes
3. The decorator shall fully abide by the opening and closing principle.
2.7 disadvantages of decorator mode
1. More codes and classes will appear, increasing the complexity of the program.
2. In dynamic decoration, multi-layer decoration will be more complex.
2.8 difference between decorator mode and static agent
1. The main difference between decorator mode and static agent is different responsibilities
2. Static agents do not have to meet the is-a relationship. Static agents will enhance their functions, and the same responsibility will become different
3. The decorator mode is more about expansion
3. Observer mode
3.1 definition of observer mode
1. Observer pattern defines one to many dependencies between objects, allowing multiple observer objects to listen to a subject object at the same time. When the subject object sends a change, all its dependents (observers) will receive a notification and update.
2. It belongs to behavioral model.
3. Observer mode is sometimes called publish subscribe mode.
3.2 Demo case
Community Q & A cases:
Observed (producer):
/** * @PackageName: com.raven.pattern.observer.advice * @ClassName: Coder * @Blame: raven * @Date: 2021-08-07 19:30 * @Description: The observer is implemented by inheriting Observable, which is an observer implementation method provided by JDK */ @Data public class Coder extends Observable { private String name = "Coder Ecosphere"; private static volatile Coder coder = null; private Coder() { } public static Coder getInstance() { if (Objects.isNull(coder)) { synchronized (Coder.class) { if (Objects.isNull(coder)) { coder = new Coder(); } } } return coder; } public void publishQuestion(Question question) { System.out.println(question.getUsername() + "stay" + name + "Asked a question"); // Submit questions through this access setChanged(); // Inform all observers of this class to pass the problem on notifyObservers(question); } } @Data public class Question { private String username; private String content; }
Observer (consumer)
public class Teacher implements Observer { private String name; public Teacher(String name) { this.name = name; } @Override public void update(Observable o, Object arg) { // Observed Coder coder = (Coder) o; System.out.println("================="); Question question = (Question) arg; System.out.println(name + "Teacher, where are you" + coder.getName() + "I got it"+question.getUsername()+"Questions raised,\n" + "The content of the question is:\n" + question.getContent()); } }
main
/** * @PackageName: com.raven.pattern.observer.advice * @ClassName: ObserverTest * @Blame: raven * @Date: 2021-08-07 19:52 * @Description: JDK Observer mode demo test class */ public class ObserverTest { public static void main(String[] args) { // Get an observed coder Coder coder = Coder.getInstance(); Question question = new Question(); question.setContent("How to learn design patterns well?"); question.setUsername("Xiao Wang"); // Create an observer teacher Teacher teacher = new Teacher("raven"); // Bind observer and observed coder.addObserver(teacher); // The observed person submits the problem coder.publishQuestion(question); } }
Event event listening case
main
public class MouseEventTest { public static void main(String[] args) throws NoSuchMethodException { MouseEventCallback callback = new MouseEventCallback(); Mouse mouse = new Mouse(); mouse.addListener(MouseEventType.ON_CLICK, callback); mouse.click(); mouse.doubleClick(); // When the addListener method is called, the event named onClick is registered in the events event management // When the click method is called, the registered events in events will be executed, and the unregistered events will not be triggered } }
event
@Data public class Event { /** * Event source, who initiated the event */ private Object source; /** * Who should be notified when the event is triggered */ private Object target; /** * What action should be taken when the event is triggered */ private Method callback; /** * Event trigger, what event to trigger */ private String trigger; private long time; public Event(Object target, Method callback) { this.target = target; this.callback = callback; } } public class EventListener { /** * JDK The bottom listener is usually designed like this */ private Map<String, Event> eventMap = Maps.newHashMap(); public void addListener(String eventType, Object target) { try { this.addListener(eventType, target, target.getClass().getMethod("on" + toUpperFirstCase(eventType),Event.class)); } catch (NoSuchMethodException e) { e.printStackTrace(); } } private String toUpperFirstCase(String eventType) { char[] chars = eventType.toCharArray(); chars[0] -= 32; return String.valueOf(chars); } /** * Add an event to the event list * @param eventType * @param target * @param callback */ private void addListener(String eventType, Object target, Method callback) { eventMap.put(eventType, new Event(target, callback)); } /** * Event name trigger * @param trigger */ public void trigger(String trigger){ if (eventMap.containsKey(trigger)){ this.eventMap.get(trigger).setTrigger(trigger); trigger(this.eventMap.get(trigger)); } } private void trigger(Event event){ event.setSource(this); event.setTime(System.currentTimeMillis()); try { event.getCallback().invoke(event.getTarget(),event); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } public class Mouse extends EventListener { public void click(){ System.out.println("Call click method"); trigger(MouseEventType.ON_CLICK); } public void doubleClick(){ System.out.println("Call the double-click method"); trigger(MouseEventType.ON_DOUBLE_CLICK); } } public interface MouseEventType { String ON_CLICK = "click"; String ON_DOUBLE_CLICK = "doubleClick"; }
EventCallback
public class MouseEventCallback { public void onClick(Event event){ System.out.println("=======Trigger mouse click event======" + "\n" + event); } public void onDoubleClick(Event event){ System.out.println("=======Trigger mouse double click event======" + "\n" + event); } }
guava
guava event
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>${guava.version}</version> </dependency>
GuavaEvent
public class GuavaEvent { @Subscribe public void register(String name){ System.out.println(name + "Joined coder Forum!"); } }
main
public class GuavaEventTest { public static void main(String[] args) { // Message bus EventBus eventBus = new EventBus(); // Message event GuavaEvent guavaEvent = new GuavaEvent(); // Register message events eventBus.register(guavaEvent); // Send message, receive in real time and trigger event eventBus.post("Zhan San"); // The obServer of jdk and the Event in spring are class oriented, and guava is method oriented. } }
3.3 applicable scenarios of observer mode
Observer mode is mainly used to establish a set of trigger mechanisms between related behaviors
3.4 advantages of observer mode
1. An abstract coupling is established between the observer and the observed.
2. Observer mode supports broadcast communication.
3.5 disadvantages of observer mode
1. There is too much detail dependence between observers, which increases time consumption and program complexity.
2. Use properly and avoid circular call
3.6 application of observer mode in source code
contextLoaderListener