preface

This series of articles refer to “Zen of Design Patterns”, cainiaotuo.com and some articles on the Internet to summarize, and develop their own applications. The design pattern is named in zen of Design Pattern.

Design pattern is only a summary of coding skills of some developers in daily development and is not fixed. It can be extended and applied according to the actual situation of the project business, and must not be bound by this. Don’t use it just for the sake of using it. Design patterns are a double-edged sword, and excessive design can lead to less readable code and larger code.

This series will not cover the seven Principles of design patterns in detail, nor will it categorize design patterns. This will only increase the cost of learning and memory, and can also lead to when using the idea of curing, always want to such a design is in line with the principle of xx, xx design pattern is, what is xx model type, and so on, not want to see in this series, only one goal, combined with the application of daily development to share, to provide a kind of code optimization.

Learning and forgetting may be the best way.

Just as the saying goes: there is no way in the world, but when more people walk, it becomes a way. In my opinion, design pattern is the same, it is not a law, but the experience of the predecessors, we learn and apply, rather than mechanically copy.

define

Bureaucratic: A one-to-many dependency between objects in which all dependent objects are notified and automatically updated when an object’s state changes.

In person: The concept may not be clear, but you are familiar with the publish-subscribe model in Spring or MQ. Without considering the distribution, generally a publisher will correspond to multiple subscribers, and the information of the publisher will be accepted and processed by multiple subscribers.

Extension:

On the differences between the publish/subscribe model and the observer model.

My personal point of view is that the two are the same, slightly different, but not completely different. The publish/subscribe model is an upgraded version of the observer model, and there is no need to differentiate and memorize it. It’s a waste of time.

The following is purely personal, please take it rationally.

I took a look at some articles on the Internet and concluded in one sentence that the publish/subscribe model has a task scheduling center compared to the observer model, and there is no direct relationship between publisher and subscriber.

The publish/spec model is basically MQ based design model, and simple boilerplate code when comparing observers! One is a design concept, and one is a simple demo code based entirely on book concepts. The latter has seen the concept of observer mode, and does not think about flexibility at all, believing that only the observer and the observed can be considered as conforming to its mode. Imagine that in a mall, after the user has paid, you need to push a message to the order system to update the status, to the inventory system to reduce the inventory, to the coupon system to destroy the coupons used, to the points system to deduct the points, to the messaging system to notify the user of the success of the purchase, and so on. Suppose, for example, that the messaging system is overwhelmed and timed out, causing the entire purchase process to fail and roll back. So in order to ensure stability, we add a message queue, so in the messaging system and payment system is not observer and observed?

My comparison is not to suggest that you accept that there is no difference between the two models, but to suggest to anyone reading this article that you should not get stuck in a rut. Don’t dwell on a problem that doesn’t make any sense. Both the publish/subscribe model and the observer model do not exist, but are simply tools and coding tricks. If you have time to go to Baidu, you’d better think about how to improve the extensibility and readability of the code in front of you.

The author’s personal view remains that design patterns are not rigid theorems, fixed patterns such as 1+1=2. It’s a tool you can manipulate to improve code compatibility and stability.

Design patterns are trade-offs between readability and extensibility. Don’t open your mouth and say, look, I’m using the policy pattern here, that’s a singleton, and there’s an iterator pattern nested here. I’m doing this with future extensions in mind, extracting as much of the common stuff as possible, and annotating it appropriately. The benefit of this is XXXX!

Programming is an art, just like writing. In the process of creation, writers don’t care about whether they should use metaphors, parallelism and other chaotic so-called writing techniques, but more about inspiration. Only by doing reading comprehension for students can they answer these writing techniques that the authors themselves don’t know. As I’m sure you all know, when you get inspired to write code, it’s like you can’t stop. There are a million ways in your mind to achieve this tiny function. I hope that design patterns are to you what writing is to you, which you don’t know when you’re using it and leave to posterity to analyze.

Learning design patterns by rote is better than learning them at all.

The above is purely personal explosion! Don’t like light spray!

Application scenarios

Sample 1

As always, let’s do a simple demo.

I believe that how many of you have seen or understood the police movie, there will be a general undercover police infiltrated into the gang, secretly to the police message.

Suppose there is a gang of axes in the Lighthouse country. The gang is not very harsh, so we call it “big black”. And the police of lighthouse country already notice this to help for a long time, want to look for an opportunity all the time a nest end, but helpless big black is too cunning, always can avoid encirclings suppress. Therefore, this day the new transfer of a police chief “white”, about the gang, white is also very distressed, after high-level negotiations, we agreed to arrange an undercover to help axe, liyingwaihe, can certainly catch all. So Baymax began to look for candidates, in order to avoid familiar faces, Baymax decided to let a just graduated from the police academy, has not reported to the police station “white” to undertake this task, and for white forged an identity. He needs to start at the bottom and get as close to management as possible. So how to gain the trust of big Black? So baymax carried out another siege and suppression activities, in the activity, the white desperate for the black block a bullet. The big black that escaped a robbery praises to small white add, regard as right-hand man, from now on small white began after 3 years again 3 years of fugitive career…

Baymax doesn’t want to bet on just one person, so he sends in another undercover agent. But in order not to reveal another person when exposed, the two do not know each other’s existence.

End of the story to start the whole code:

1. Define the observer
Public interface Observer {/** */ void doAction(); }Copy the code
2. Build 2 undercover agents
Public class XiaoBai implements Observer{@override public void doAction() {// XiaoBai implements Observer{@override public void doAction() {// XiaoBai implements Observer{@override public void doAction() {// Implements Observer{@override public void doAction() {// implements Observer{@override public void doAction() {Copy the code

If the structure is complete, I need to specify another “Baymax” class. However, in the observer mode, the observer and the observed are described. Although “Baymax” is the observer subjectively, he does not directly observe, but entrust it to two undercover agents. Therefore, between “Big Black” and two undercover agents belongs to the concept of the observed and the observer. This is based on what’s called the definition.

3. Construct the observed
public abstract class Subject { List<Observer> observers = new ArrayList<>(); Public void goOut() {this.notifyob (); } /** * public void addObserver(Observer Observer) {observers.add(Observer); } /** * notifyOb() {private void notifyOb() {// Observers ::doAction, observers.forEach(Observer::doAction); } } public class DaHei extends Subject{ }Copy the code

This scenario is relatively simple, so it defines a common object to be observed, and “Big Black” has no separate other content.

4. Call
    public static void main(String[] args) {
        DaHei daHei = new DaHei();

        XiaoBai xiaoBai = new XiaoBai();
        daHei.addObserver(xiaoBai);
        XiaoXin xiaoXin = new XiaoXin();
        daHei.addObserver(xiaoXin);
        daHei.goOut();
    }
Copy the code

Every time “big black” go out, small white and small new will be tipped off, it can be said that the most miserable boss… Ha, ha, ha.

This demo may not be quite the same as the web demo, which is a bit more complete, with the top layer of both the observer and the observed being interfaces, the middle layer using abstract classes to extract common parts, and the implementation layer customising operations for the business.

The sample 2

Spring’s built-in asynchronous event listener and AOP can be viewed as the observer pattern, and the application is familiar, so it is not superfluous here.

But we can implement a simple, practical event handler modeled after asynchronous events.

You can control a type of event to listen on a service by distinguishing the parameter type of the event. Here we take the business processing after successful payment as an example.

Suppose, after successful payment, the order status needs to be updated, inventory deducted, coupons destroyed, SMS sent, etc.

1. Define the observer
public interface LogicObserver {

    <T extends ObserverBaseBean> void  doAction(T t);
}

Copy the code
2. Define the observer parameter object

ObserverBaseBean is the top level class for uniform observer parameters, and different businesses customize their parameter objects and group observers accordingly.

@Data
public class ObserverBaseBean implements Serializable {
}
Copy the code

Here we define two parameter objects. Are:

After successful payment: used to process other business operations after successful payment in a unified manner

@EqualsAndHashCode(callSuper = true)
@Data
public class PayObserverBean extends ObserverBaseBean {
}
Copy the code

Other business: used to test, distinguish and pay after successful operation.

@EqualsAndHashCode(callSuper = true)
@Data
public class OtherObserverBean extends ObserverBaseBean{
}
Copy the code
3. Transition abstract classes

An abstract class AbstractLogicObserver is used to transition between the business observer and the observer interface, internally defining a getBean method that returns the observer parameter object for the current business.

public abstract class AbstractLogicObserver implements LogicObserver {

    @PostConstruct
    public void init() {
        ObserverManager.addObservers(getBean().getClass(), this.getClass());
    }


    protected abstract ObserverBaseBean getBean();
}
Copy the code

Also, in the init method, the current observer is injected into the ObserverManager, which I like to call the dispatch center. The ** @postconstruct ** annotation is used. If you are not familiar with it, you can find out for yourself. In simple terms, after the bean property is automatically injected, the annotation annotation method is executed, which is the void method with no arguments.

4. Dispatch center

ObserverManager: the so-called scheduling center is relatively simple, mainly for the observer by business (in this case through the parameter object of different business) group save, use can be traversed.

@Slf4j @SuppressWarnings("unused") public class ObserverManager { private final static Map<Class<? extends ObserverBaseBean>, List<Class<? extends LogicObserver>>> observerMap = new HashMap<>(16); Public static void addObservers(Class<? extends ObserverBaseBean> bean, Class<? extends LogicObserver> service) { List<Class<? extends LogicObserver>> observers = observerMap.get(bean); if (observers == null || observers.isEmpty()) { observers = new ArrayList<>(); observers.add(service); observerMap.put(bean, observers); } else { observers.add(service); Public static <T extends ObserverBaseBean> void notifyOb(T T) {List<Class<? extends LogicObserver>> observers = observerMap.get(t.getClass()); If (observers = = null | | observers. IsEmpty ()) {the warn (not set "observer"); return; } observers. ForEach (ob - > {/ / still static get bean LogicObserver observer. = SpringContextUtil getBean (ob); observer.doAction(t); }); }}Copy the code
5. Service objects to be observed

I won’t say much about the 3 observers, but I’ll just go to the code.

Message after payment:

@Component public class PayAfterMsgObserver extends AbstractLogicObserver { @Override public <T extends ObserverBaseBean> void doAction(T T) {system.out.println (" send message alert "); } @Override protected ObserverBaseBean getBean() { return new PayObserverBean(); }}Copy the code

Discount after payment:

@Component public class PayAfterCouponObserver extends AbstractLogicObserver{ @Override protected ObserverBaseBean getBean() { return new PayObserverBean(); } @override public <T extends baseBean > void doAction(T T) {system.out.println (); }}Copy the code

Other business:

@Component public class OtherObserver extends AbstractLogicObserver{ @Override protected ObserverBaseBean getBean() { return new OtherObserverBean(); } @override public <T extends ObserverBaseBean> void doAction(T T) {system.out.println (); }}Copy the code
6. Business objects

Payment:

@service public class PayService {public void payOrder() {// Payment process // Processing after payment is complete obServerManager.notifyob (new PayObserverBean()); } public void other() {// Other business processing // After processing obServerManager.notifYOb (new OtherObserverBean()); }}Copy the code
7. Test

To use SpringBoot unit tests, or invoke them via SpringMVC, they must be used internally in Spring. It cannot be called directly from main, otherwise the ApplicationConext parameter in observerMap of ObserverManager and SpringContextUtil will be null, because main is starting another application service.

@RunWith(SpringRunner.class) @SpringBootTest public class ObserverDemo { @Resource private PayService payService; @Test public void testPay() { payService.payOrder(); } @Test public void testOther(){ payService.other(); }}Copy the code

A simple event handler, but synchronous, if the call time is changed to multiple threads, can achieve asynchronous concurrent execution. The sample is for reference only, there are other ways to implement and optimize.

UMl diagrams

Same old, from the rookie tutorial handling.

The Observer pattern uses three classes Subject, Observer, and Client. The Subject object has methods for binding observers to and unbinding observers from the Client object. We create the Subject class, the Observer abstract class, and an entity class that extends the abstract class Observer.

ObserverPatternDemo, our demo class uses Subject and entity-class objects to demonstrate the observer pattern.

summary

One of the reasons this article dragged on was that when I tested it in the main method, the results were far from what I expected, ignoring that the main function would start a new Java application.

At the same time, when I checked online materials, I saw many people comparing the difference between publish and subscribe model and observer model, and comparing them with words, which really felt meaningless.

I hope that through personal explanation, you can have the goods. Even a little inspiration would have been worth it.