Blog: bugstack.cn

Precipitation, share, grow, let yourself and others can gain something! 😄

One, foreword

80% of the butt wipe paper is hand protection!

After working for about 3 years, a large part of programmers wanted to upgrade their technology stack and began to try to read some source code, such as Spring, Mybaits, Dubbo, etc., but they found it more and more difficult to understand as they read, and went from here to there for a while. Even suspect that their skills are too poor, slowly will not be willing to touch this part of the knowledge.

The main reason for this is that a framework becomes more and more complex over time, from a very core point at the beginning to scattered leaves at the end. It’s like developing your own business code or a component. The core code may only make up 20% at the beginning, and most of the rest is just to keep the core process running. So that’s part of the reason you struggle to read the source code.

Are design patterns used in the framework?

Not only design patterns are used in the framework, but there are many of them, and sometimes they are not a single pattern at all, but a combination of multiple design patterns. CRUD is not the same as CRUD that most of your friends are developing. If it’s all if statements from top to bottom, it’s not much of a framework. If you search for Adapter in Spring’s source code, many implementation classes will appear, such as; UserCredentialsDataSourceAdapter. This design pattern is the adapter pattern we will cover in this article.

Adapters are everywhere in life

What comes to mind if you think about the existence of many adapters in your daily life? You can think about it before you read it.

Second, development environment

  1. JDK 1.8
  2. Idea + Maven
  3. Involving engineering three, can be concerned byThe public,:Bugstack wormhole stackReply,Download the source codeGet (Open the get link and find the serial number 18)
engineering describe
itstack-demo-design-6-00 Scene simulation engineering; Emulated multiple MQ message bodies
itstack-demo-design-6-01 Implement business requirements with a lump of code
itstack-demo-design-6-02 Design patterns are optimized to adapt code to generate contrast for learning

Iii. Introduction to adapter mode

The main function of the adapter pattern is to unify incompatible interfaces through adaptation. Make the user convenient to use, as we mentioned universal charge, data line, MAC laptop conversion head, travel abroad to buy a socket and so on, they are to adapt to a variety of different mouth, do compatible.

In addition to the various adaptive scenarios in our lives, what about in business development?

In business development, we often need to do the compatibility of different interfaces, especially the service of the medium platform. The medium platform needs to make unified packaging of various types of services of each business line, and then provide interfaces for external use. This is very common in our normal development.

4. Case scenario simulation

With the continuous development of the company’s business, when the basic system gradually formed. Business operations need to start recruiting and activating users to ensure DAU growth and ultimately ROI conversion.

And at this time will need to do some marketing system, most of the common are fission, solicitation, for example; You invite a user to open an account, or invite a user to place an order, and the platform will give you a rebate. At the same time, with the increasing number of new customers, I started to set up a reward for the first order every month, etc., various marketing scenarios.

So at this time to do such a system will receive a variety of MQ messages or interfaces, if one by one to develop, it will cost a lot of cost, and there is a certain difficulty for the later expansion. At this point, you want to have a system that can be configured to access external MQ, which, as mentioned above, might be registration messages, merchandise orders, and so on.

The idea of adapters applies here, and I want to emphasize that adapters can adapt not only interfaces but also properties.

1. Scene simulation engineering

itstack-demo-design-6-00├── class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ ├─ PopOrderS.java ├─ PopOrderS.javaCopy the code
  • Three different types of MQ messages are emulated, each with the necessary fields in the message body, such as; User ID, time, business ID, but the field properties of each MQ are different. Just like the userId has different fields in different MQ: uId, userId, and so on.
  • At the same time, it also provides two different types of interfaces, one for querying the quantity of internal orders and one for querying whether the third party is the first order.
  • These different types of MQ and interfaces will be adapted and compatible later.

2. Scenario description

1.1 Registering MQ

public class create_account {

    private String number;      // Account number
    private String address;     / / to open an account
    private Date accountDate;   // Account opening time
    private String desc;        // Account description

    // ... get/set     
}
Copy the code

1.2 Internal Order MQ

public class OrderMq {

    private String uid;           / / user ID
    private String sku;           / / goods
    private String orderId;       / / order ID
    private Date createOrderTime; // Order time

    // ... get/set      
}
Copy the code

1.3 Third Party Order MQ

public class POPOrderDelivered {

    private String uId;     / / user ID
    private String orderId; / / order number
    private Date orderTime; // Order time
    private Date sku;       / / goods
    private Date skuName;   // Product name
    private BigDecimal decimal; / / the amount

    // ... get/set      
}
Copy the code

1.4 Interface for querying internal order quantity of users

public class OrderService {

    private Logger logger = LoggerFactory.getLogger(POPOrderService.class);

    public long queryUserOrderCount(String userId){
        logger.info("Self-owned merchant, query user's order whether the first order: {}", userId);
        return 10L; }}Copy the code

1.5 Interface for Querying a User’s First Third-party Order

public class POPOrderService {

    private Logger logger = LoggerFactory.getLogger(POPOrderService.class);

    public boolean isFirstOrder(String uId) {
        logger.info("POP merchant, query whether the user's order is the first order: {}", uId);
        return true; }}Copy the code
  • These are examples of different MQ messages and interfaces, and we will use these MQ messages and interfaces to adapt them accordingly.

Five, with a lump of code implementation

Most of the time MQ messages are received by creating a class to consume by converting its MQ message properties to its own methods.

We will also present the implementation simulation of this approach first, but there is a big problem with this implementation, as MQ messages become more and more, or even tens of hundreds of years later, how can you optimize as a mid-stage?

1. Engineering structure

itstack-demo-design-6-01└ ─ ─ the SRC └ ─ ─ the main └ ─ ─ Java └ ─ ─ org. Itstack. The demo, the design └ ─ ─ create_accountMqService. Java └ ─ ─ OrderMqService. Java └ ─ ─ POPOrderDeliveredService.javaCopy the code
  • Now we need to receive three MQ messages, so we have three corresponding classes, almost the same as our usual code. This is fine if you have a small amount of MQ, but as the number increases, you need to consider some design patterns to address this.

2. Realization of Mq receiving messages

public class create_accountMqService {

    public void onMessage(String message) {

        create_account mq = JSON.parseObject(message, create_account.class);

        mq.getNumber();
        mq.getAccountDate();

        / /... Take care of your own business}}Copy the code
  • All three sets of MQ messages are used in the same simulation and will not be shown separately. Can obtain the source code after learning.

Adapter mode refactoring code

The adapter pattern is then used to optimize the code, which is a minor refactoring.

The main problem to be solved by the adapter pattern is the unified output of different types of interfaces, which is also mentioned in the factory method pattern of learning different kinds of prize processing, which is also the application of the adapter.

In this article we will also present another multi-MQ receiver, using MQ scenario. To unify the processing of different types of messages to reduce subsequent receptions to MQ.

If you haven’t developed receiving MQ messages before, this scenario may sound confusing. For this, I personally recommend learning about MQ. In addition, even if you do not understand, it does not matter, will not affect the experience of the train of thought.

Again, the core of MQ compatibility shown in this article is the handling of fields that match different types. If, after receiving MQ, we do not want to develop classes individually when configuring different consumer classes, we can use the proxy class approach.

1. Engineering structure

itstack-demo-design-6-02├── class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ class ├─ POPOrderAdapterServiceImpl. Java ├ ─ ─ MQAdapter, Java ├ ─ ─ OrderAdapterService, Java └ ─ ─ RebateInfo, JavaCopy the code

Adapter model structure

  • There are two types of adaptations; Interface adaptation and MQ adaptation. Instead of just simulating interface adaptation, which is common in many cases, the idea of adaptation is shifted to the MQ message body to increase awareness of multiple design patterns.
  • It starts with MQ adaptation, receiving various MQ messages. When the business develops rapidly, users need to be rewarded for their first order. In such scenarios, interface adaptation operations are added.

2. Code implementation (MQ message adaptation)

2.1 Unified MQ message body

public class RebateInfo {

    private String userId;  / / user ID
    private String bizId;   / / business ID
    private Date bizTime;   // Business time
    private String desc;    // Service description
    
    // ... get/set
}
Copy the code
  • There are a variety of type attributes in MQ messages, and although they all have the same value provided to the user, if they are all accessed this way it can be cumbersome when there are too many MQ messages.
  • So in this case we define a generic MQ message body and then unify all incoming messages.

2.2 MQ message body Adaptation classes

public class MQAdapter {

    public static RebateInfo filter(String strJson, Map<String, String> link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        return filter(JSON.parseObject(strJson, Map.class), link);
    }

    public static RebateInfo filter(Map obj, Map<String, String> link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        RebateInfo rebateInfo = new RebateInfo();
        for (String key : link.keySet()) {
            Object val = obj.get(link.get(key));
            RebateInfo.class.getMethod("set" + key.substring(0.1).toUpperCase() + key.substring(1), String.class).invoke(rebateInfo, val.toString());
        }
        returnrebateInfo; }}Copy the code
  • The methods in this class are very important for mapping various properties of different MQ types to the properties we need and returning them. It’s like a property that hasThe user ID; uIdMap to what we need;userId, do unified processing.
  • In this process, map management needs to be passed toMap<String, String> linkThat is, it accurately describes the current MQ property name mapped to one of our property names.
  • And finally because of what we receivemqThe message is basically alljsonFormat, which can be converted to a MAP structure. Finally, we use reflection calls to assign values to our types.

2.3 Testing adaptation classes

2.3.1 Writing unit test classes
@Test
public void test_MQAdapter(a) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    create_account create_account = new create_account();
    create_account.setNumber("100001");
    create_account.setAddress("Langfang, Hebei Province. "Guangyang District. Vocational and Technical College in the University");
    create_account.setAccountDate(new Date());
    create_account.setDesc("Open an account at school");          

    HashMap<String, String> link01 = new HashMap<String, String>();
    link01.put("userId"."number");
    link01.put("bizId"."number");
    link01.put("bizTime"."accountDate");
    link01.put("desc"."desc");
    RebateInfo rebateInfo01 = MQAdapter.filter(create_account.toString(), link01);
    System.out.println("Mq. create_account(before adaptation)" + create_account.toString());
    System.out.println("Mq.create_account (after adaptation)" + JSON.toJSONString(rebateInfo01));

    System.out.println("");

    OrderMq orderMq = new OrderMq();
    orderMq.setUid("100001");
    orderMq.setSku("10928092093111123");
    orderMq.setOrderId("100000890193847111");
    orderMq.setCreateOrderTime(new Date()); 

    HashMap<String, String> link02 = new HashMap<String, String>();
    link02.put("userId"."uid");
    link02.put("bizId"."orderId");
    link02.put("bizTime"."createOrderTime");
    RebateInfo rebateInfo02 = MQAdapter.filter(orderMq.toString(), link02);

    System.out.println("Mq. orderMq(before adaptation)" + orderMq.toString());
    System.out.println("Mq. OrderMq (after adaptation)" + JSON.toJSONString(rebateInfo02));
}
Copy the code
  • Here we simulate the incoming of two different MQ messages and set the mapping of the fields.
  • In the development of real business scenarios, such mapping configuration can be assigned to configuration files or database background configuration to reduce coding.
2.3.2 Test results
Mq.create_account (before adaptation){"accountDate":1591024816000."address":"Langfang, Hebei Province. "Guangyang District. Vocational and Technical College in the University"."desc":"Open an account at school"."number":"100001"} mq.create_account(after adaptation){"bizId":"100001"."bizTime":1591077840669."desc":"Open an account at school"."userId":"100001"} mq.orderMq(before adaptation){"createOrderTime":1591024816000."orderId":"100000890193847111"."sku":"10928092093111123"."uid":"100001"} mq.ordermq (after adaptation){"bizId":"100000890193847111"."bizTime":1591077840669."userId":"100001"}

Process finished with exit code 0
Copy the code
  • As can be seen from the above, the same field value has unified field attributes before and after adaptation. This makes business development very simple.
  • It is also very important that in real business development, in addition to the use of reflection, a proxy class can be added to give it the configuration of the map. This eliminates the need to manually create classes for each MQ.

3. Code implementation (interface adaptation)

As we mentioned earlier, the marketing campaign itself will have to change as the business evolves, and not just be rewarded for picking up MQ. Because more and more new ones are being pulled at this point, some restrictions need to be made.

Because of the increase, you only get rewards for your first order, which means you get rewards for your first order in a year or a new person or a month, rather than for every order you’ve placed before.

You need to restrict this approach, and there is no attribute in MQ to determine the first order. You can only query through the interface, and the interface is as follows;

interface describe
org.itstack.demo.design.service.OrderService.queryUserOrderCount(String userId) Call long to query the order quantity
org.itstack.demo.design.service.OrderService.POPOrderService.isFirstOrder(String uId) Boolean is used to determine whether it is the first order
  • The judgment logic and usage mode of the two interfaces are different. Different interface providers have different outgoing parameters. One is to judge whether the first order is made directly, and the other is to judge according to the number of orders.
  • So you need to use the adapter pattern to do this, and of course you can do it if you write if statements, but we often mention that such code is difficult to maintain.

3.1 Defining Unified Adaptation Interfaces

public interface OrderAdapterService {

    boolean isFirst(String uId);

}
Copy the code
  • Subsequent implementation classes are required to implement this interface and wrap the specific logic in a specified class to fulfill a single responsibility.

3.2 Implement two different interfaces respectively

Internal commodity interface

public class InsideOrderService implements OrderAdapterService {

    private OrderService orderService = new OrderService();

    public boolean isFirst(String uId) {
        return orderService.queryUserOrderCount(uId) <= 1; }}Copy the code

Third Party Commodity Interface

public class POPOrderAdapterServiceImpl implements OrderAdapterService {

    private POPOrderService popOrderService = new POPOrderService();

    public boolean isFirst(String uId) {
        returnpopOrderService.isFirstOrder(uId); }}Copy the code
  • Each of these interfaces implements its own way of determining, especially for the order quantity interface, whether or not the order quantity is currently received by MQ< = 1, to judge whether the first order.

3.3 Testing adaptation classes

3.3.1 Writing unit test classes
@Test
public void test_itfAdapter(a) {
    OrderAdapterService popOrderAdapterService = new POPOrderAdapterServiceImpl();
    System.out.println("Judge first order, interface adaptation (POP) :" + popOrderAdapterService.isFirst("100001"));   

    OrderAdapterService insideOrderService = new InsideOrderService();
    System.out.println("Judge the first order, interface adaptation (proprietary) :" + insideOrderService.isFirst("100001"));
}
Copy the code
3.3.2 Test results
23:25:47.076[the main] INFO O.I.D.D esign. Service. POPOrderService - POP businesses, is headed by a single query the user's order:100001Judge the first order, interface adaptation (POP) :true
23:25:47.079[the main] INFO O.I.D.D esign. Service. POPOrderService - proprietary business, is headed by a single query the user's order:100001Judge the first order, interface adaptation (self-run) :false

Process finished with exit code 0
Copy the code
  • From the test results, the existing interface has been unified packaging, external use does not need to care about the specific internal logic. And in the call only need to pass unified parameters, so that meet the role of adaptation.

Seven,

  • As you can see above, these functions can be achieved without using the adapter pattern, but using the adapter pattern makes the code: clean and easy to maintain, reduces the amount of repeated judgment and use, and makes the code easier to maintain and expand.
  • In particular, by adapting values with different attributes of the same type in multiple message bodies such as MQ, together with proxy classes, we can access MQ messages provided by the other party in a simple configuration mode without requiring a lot of repeated development. It’s very scalable.
  • The learning process of design patterns may involve the embodiment of other design patterns in some chapters, but will not be focused to avoid distracting. However, in practical use, many design patterns are often used comprehensively, and will not appear singly.

Recommended reading

  • 1. Relearn Java design mode: Actual factory method mode (multiple types of commodity award scenario)
  • 2. Relearn Java design pattern: Actual Abstract Factory Pattern (replace Redis dual cluster upgrade scenario)
  • 3. Relearn Java design mode: Actual Constructor mode (decoration material combination package selection scenario)
  • 4. Relearn Java design mode: actual combat prototype mode (multiple sets of tests with each question and answer out of order)
  • 5. Relearn Java design patterns: Effective Java singleton