Blog: bugstack.cn

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

One, foreword

Code a shuttle, brother to take the blame.

Most developers want to write good code from the beginning, and in addition to coding as a job, they are artisans. But a lot of times it’s hard to stick to your original intention, like; I took on a bad project, the urgent need of product functions, my personal ability was insufficient, and other reasons led to the bloated engineering code, frequent online accidents, and finally left my job.

Read a lot of books, learn a lot of knowledge, multithreading can play flowers, but in the end I still can't write code!

This is a bit like home decoration after buying things, MY hundreds of thousands of solid wood sofa, how to put here is not good. Similarly, bad code is not necessarily a lack of basic technology, and it’s not necessarily a product in urgent need. I don’t care how to implement it tomorrow. Most of the time, it is our lack of experience in coding and lack of ability to control the architecture. I believe that the first requirement of a product is often not complex, even what you see. But if you don’t think about whether or not you’ll be able to expand, and which modules you’ll be able to add functionality to in the future, the subsequent code will start to spread with the first seed of evil you’ve planted.

What is the experience of learning design patterns, how to learn to use!

Design pattern books, a bit like driving license, home decoration manuals, or single dog love bible. But!!! As long as you are not practical operation, certain can make of “disorderly code seven bad”. Because these guiding ideas are refined from practical experience, without refined white, it is difficult to control such knowledge. Therefore, in the process of learning, we should first have a case, and then combine the case with our actual business, try to reconstruct, and slowly experience the feeling, so as to learn how to build excellent code.

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 codeTo obtain
engineering describe
itstack-demo-design-2-00 Scene simulation engineering, simulation of the use of Redis upgrade to the cluster class transformation
itstack-demo-design-2-01 Using a lump of code to implement business requirements is also the use of ifelse
itstack-demo-design-2-02 Design patterns are optimized to adapt code to generate contrast for learning

Abstract factory model introduction

Abstract Factory pattern, image from Refactoringguru.cn

Both the abstract factory pattern and the factory method pattern are primarily intended to solve the “interface selection” problem. But in implementation, an abstract factory is a central factory that creates patterns for other factories.

Such a design pattern or similar code structure may not be much of a concern in normal business development, but this scenario is always around us, for example;

  1. Carriage return line feed in different systems

    1. On Unix, each line ends with only< > a new line, i.e.,\n;
    2. On Windows, each line ends with< newline >< enter >, i.e.,\n\r;
    3. On the Mac, each line ends in < enter >.
  2. Differences in IDEA development Tools (Win\Mac)

    Differences in the presentation of IDEA development tools under different systems

“In addition to such obvious examples, we often run into similar problems in business development that require compatibility.” But most inexperienced developers simply add ifelse.

4. Case scenario simulation

Simulate an enterprise-class dual-set Redis cluster upgrade

In many cases, the wild development of the initial business will also affect the development of the system.

It is estimated that low QPS, low system pressure, low concurrent access, no major action for nearly a year, etc., will not invest a lot of manpower to build a very perfect system before considering the time investment cost. As with Redis, it is often possible to just stand alone and be satisfied.

Don't brag about baidu home page I go to school when a day can finish writing, such as graduation work even if give me a year can't finish!

However, with the rapid development of business beyond expectations, the load capacity of the system must keep up with. The original stand-alone Redis can not meet the system requirements. At this time, it is necessary to replace the more robust Redis cluster service, although it needs to be modified but can not affect the current system operation, but also smooth transition past.

With this upgrade, there are foreseeable problems;

  1. Many services that use Redis need to be upgraded to the cluster together.
  2. Clusters A and B must be compatible for subsequent Dr.
  3. The interfaces and methods provided by the two sets of clusters are different and need to be adapted.
  4. The system cannot be affected.

1. Scene simulation engineering

itstack-demo-design-2-00
└ ─ ─ the SRC└ ─ ─ the main└ ─ ─ Java└ ─ ─ org. Itstack. Demo. The design├ ─ ─ matter│ ├ ─ ─ EGM. Java│ └ ─ ─ IIR. Java└ ─ ─ RedisUtils. JavaCopy the code

All code in the project can be followed by the public account:Bugstack wormhole stackReply,Download the source codeGet.

2. Scenario description

2.1 Simulate single-machine service RedisUtils

Redis standalone service
  • Emulated Redis functionality, which assumes that all systems currently use the service
  • Class and method rankings are fixed to each business system, change a little trouble

2.2 Simulating cluster EGM

Simulate cluster EGM
  • Simulate a cluster service, but the method names are different from those used in various business systems. It’s kind of like your MAC. I use Win. Do the same thing, but with a different operation.

2.3 Simulating cluster IIR

Simulate cluster IIR
  • This is another set of cluster services, and sometimes in enterprise development it is quite possible to have two sets of services, so we add two different sets of services that do the same thing to learn from the abstract factory pattern.

To sum up, redis service has been widely used in our current system, but the system cannot meet the rapid development of business, so we need to migrate to cluster service. At this time, there are two sets of cluster services that need to be compatible with each other and meet all business system transformation without affecting online use.

3. Use of single cluster code

The following is the case to simulate the original single cluster Redis usage, and the code here will be reformed in the future.

The class diagram structure of the current function

3.1 Defining interfaces

public interface CacheService {

    String get(final String key);

    void set(String key, String value);
  void set(String key, String value, long timeout, TimeUnit timeUnit);   void del(String key);  } Copy the code

3.2 Implement the calling code

public class CacheServiceImpl implements CacheService {

    private RedisUtils redisUtils = new RedisUtils();

    public String get(String key) {
 return redisUtils.get(key);  }   public void set(String key, String value) {  redisUtils.set(key, value);  }   public void set(String key, String value, long timeout, TimeUnit timeUnit) {  redisUtils.set(key, value, timeout, timeUnit);  }   public void del(String key) {  redisUtils.del(key);  }  } Copy the code
  • The current code is fine and simple for use in the current scenario. But all business systems are in use at the same time, the need for transformation is not so easy. Here you can think about how to transform is reasonable.

Five, with a lump of code implementation

There is no logic that ifelse can't solve. If not, add a line!

This implementation does not modify the class structure diagram, which is consistent with the class hierarchy given above. Add a type field to the interface to distinguish which cluster is being used. It can be said that the current way is very difficult to use, other users change a lot, here is just as an example.

1. Engineering structure

itstack-demo-design-2-01
└ ─ ─ the SRC└ ─ ─ the main└ ─ ─ Java└ ─ ─ org. Itstack. Demo. The design├ ─ ─ impl│ └ ─ ─ CacheServiceImpl. Java└ ─ ─ CacheService. JavaCopy the code
  • There are only two classes, and the class structure is very simple. And we need to complement the extension functionality is only inCacheServiceImplIn the implementation.

2. Ifelse implementation requirements

public class CacheServiceImpl implements CacheService {

    private RedisUtils redisUtils = new RedisUtils();

    private EGM egm = new EGM();
  private IIR iir = new IIR();   public String get(String key, int redisType) {   if (1 == redisType) {  return egm.gain(key);  }   if (2 == redisType) {  return iir.get(key);  }   return redisUtils.get(key);  }   public void set(String key, String value, int redisType) {   if (1 == redisType) {  egm.set(key, value);  return;  }   if (2 == redisType) {  iir.set(key, value);  return;  }   redisUtils.set(key, value);  }   / /... Similar do not show too much, you can download the source code for reference  } Copy the code
  • The implementation process here is very simple and mainly determines which Redis cluster is based on type.
  • Although the implementation is simple, but for the user is troublesome, and it is difficult to cope with the late expansion and continuous maintenance.

3. Test and verify

Next, we verify the interface services through junit unit tests, emphasizing that routine single-test writing can better improve the robustness of the system.

Writing test classes:

@Test
public void test_CacheService(a) {
    CacheService cacheService = new CacheServiceImpl();
    cacheService.set("user_name_01".Little Fuge..1);
    String val01 = cacheService.get("user_name_01".1);
 System.out.println(val01); } Copy the code

Results:

22:26:24.591[the main] INFO org. Itstack. Demo. Design. The matter. The EGM - EGM write data key: user_name_01 val: small Fu Ge22:26:24.593[the main] INFO org. Itstack. Demo. Design. The matter. The EGM - EGM to get the data key: user_name_01Test result: Little Fu ge
Process finished with exit code 0
Copy the code
  • From the results, it seems to be working well and there is no problem. But such code is really hard to change once the generation is running!

Abstract Factory pattern refactoring code

The code is then optimized using the abstract factory pattern, which is a minor refactoring.

The creation and acquisition of the abstract factory will be implemented as a proxy class. The proxied class is the current Redis action method class, allowing this class to implement data services that call clusters A and B without any modifications.

It is also important to note that because cluster A and cluster B have different partial method offerings, an interface adaptation class needs to be made, and this adaptation class is like A factory in A factory to create the same business by abstracting different services into A unified interface. This section is a reference to the factory method model types in our previous chapter.

1. Engineering structure

itstack-demo-design-2-02
└ ─ ─ the SRC├ ─ ─ the main│ └ ─ ─ Java│ └ ─ ─ org. Itstack. Demo. The design│ ├ ─ ─ the factory│ │ ├ ─ ─ impl│ │ ├─ Anti-Flag ─ EgMcAcApter│ ├ ─ ├ ─ garbage, ├ ─ garbage│ │ ├ ─ ─ ICacheAdapter. Java│ │ ├ ─ ─ JDKInvocationHandler. Java│ │ └ ─ ─ JDKProxy. Java│ ├ ─ ─ impl│ │ └ ─ ─ CacheServiceImpl. Java│ └ ─ ─ CacheService. Java└ ─ ─ the test└ ─ ─ Java└ ─ ─ org. Itstack. Demo. Design. The test└ ─ ─ ApiTest. JavaCopy the code

Abstract factory model structure

Abstract factory model structure
  • Some core function codes involved in the project are as follows;
    • ICacheAdapter, defined adaptation interfaces, respectively packaging the different interface names in the two clusters.EGMCacheAdapter,IIRCacheAdapter
    • JDKProxy,JDKInvocationHandlerIs the definition and implementation of the proxy class, which is another implementation of the abstract factory. Through this way can be very good to the original operation of Redis method for proxy operation, through the control of different entry object, control the use of cache.

“Ok”, so I’ll explain the implementation of each class.

2. Code implementation

2.1 Defining adaptation Interfaces

public interface ICacheAdapter {

    String get(String key);

    void set(String key, String value);
  void set(String key, String value, long timeout, TimeUnit timeUnit);   void del(String key);  } Copy the code
  • The main purpose of this class is to allow all cluster providers to operate under a common method name. Also aspect follow-up expansion.

2.2 Enabling the Cluster to Use services

EGMCacheAdapter

public class EGMCacheAdapter implements ICacheAdapter {

    private EGM egm = new EGM();

    public String get(String key) {
 return egm.gain(key);  }   public void set(String key, String value) {  egm.set(key, value);  }   public void set(String key, String value, long timeout, TimeUnit timeUnit) {  egm.setEx(key, value, timeout, timeUnit);  }   public void del(String key) {  egm.delete(key);  } } Copy the code

IIRCacheAdapter

public class IIRCacheAdapter implements ICacheAdapter {

    private IIR iir = new IIR();

    public String get(String key) {
 return iir.get(key);  }   public void set(String key, String value) {  iir.set(key, value);  }   public void set(String key, String value, long timeout, TimeUnit timeUnit) {  iir.setExpire(key, value, timeout, timeUnit);  }   public void del(String key) {  iir.del(key);  }  } Copy the code
  • Both of these implementations are very easy to wrap under a unified method name.

2.3 Define abstract engineering agent classes and implementations

JDKProxy

public static <T> T getProxy(Class<T> interfaceClass, ICacheAdapter cacheAdapter) throws Exception {
    InvocationHandler handler = new JDKInvocationHandler(cacheAdapter);
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Class<? >[] classes = interfaceClass.getInterfaces();    return (T) Proxy.newProxyInstance(classLoader, new Class[]{classes[0]}, handler);
} Copy the code
  • The main role here is to complete the proxy class, while having external input arguments for which cluster to use.

JDKInvocationHandler

public class JDKInvocationHandler implements InvocationHandler {

    private ICacheAdapter cacheAdapter;

    public JDKInvocationHandler(ICacheAdapter cacheAdapter) {
 this.cacheAdapter = cacheAdapter;  }   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  return ICacheAdapter.class.getMethod(method.getName(), ClassLoaderUtils.getClazzByArgs(args)).invoke(cacheAdapter, args);  }  } Copy the code
  • In the implementation of the proxy class, it is actually quite simple, with the method operation through the cluster service that penetrates in.
  • In the otherinvokeBy using the method name reflection method, call the corresponding method function, also simplifies the overall use.
  • At this point we have the overall functionality of the implementation is complete, the abstract factory part can also be implemented in a non-proxy way.

2. Test and verify

Writing test classes:

@Test
public void test_CacheService(a) throws Exception {
    CacheService proxy_EGM = JDKProxy.getProxy(CacheServiceImpl.class, new EGMCacheAdapter());
    proxy_EGM.set("user_name_01".Little Fuge.);
    String val01 = proxy_EGM.get("user_name_01");
 System.out.println(val01);   CacheService proxy_IIR = JDKProxy.getProxy(CacheServiceImpl.class, new IIRCacheAdapter());  proxy_IIR.set("user_name_01".Little Fuge.);  String val02 = proxy_IIR.get("user_name_01");  System.out.println(val02); } Copy the code
  • By passing in different cluster types in the test code, you can invoke methods under different clusters.JDKProxy.getProxy(CacheServiceImpl.class, new EGMCacheAdapter());
  • If there is a need for further expansion, it can also be supplemented in this way. Meanwhile, the original method is not changed in terms of transformation, which reduces the modification cost.

Results:

23:07:06.953[the main] INFO org. Itstack. Demo. Design. The matter. The EGM - EGM write data key: user_name_01 val: small Fu Ge23:07:06.956[the main] INFO org. Itstack. Demo. Design. The matter. The EGM - EGM to get the data key: user_name_01Test result: Little Fu ge23:07:06.957[the main] INFO org. Itstack. Demo. Design. The matter. The key: writing IIR - IIR user_name_01 val: small Fu Ge23:07:06.957[the main] INFO org. Itstack. Demo. Design. Matter. IIR - IIR to get the data key: user_name_01Test result: Little Fu ge Process finished with exit code 0 Copy the code
  • The result is normal, the code meets the requirements of this expansion, and your technical ability has also impressed the boss deeply.
  • The development of your own ability to improve is far more than the external pressure of writing a bunch of code interface, if you have a lot of skills, you can come up with a good solution even in an emergency situation.

Seven,

  • Abstract factory mode, to solve the problem is in a product family, there are many different types of products (Redis cluster, operating system), the problem of interface selection. Such scenarios are also common in business development, but they may not be abstracted at times.
  • Your code is just buried by ifelse!When you know what scenarios and when code can be optimized by abstract engineering, your code hierarchy can achieve functionality and improve scalability and elegance to meet business needs.
  • So this design pattern satisfies; Single responsibility, open and close principle, decoupling and other advantages, but with the continuous expansion of business, it may cause the complexity of class implementation. But it’s not a disadvantage, because it can be mitigated with the introduction of other design approaches and proxy classes and auto-generated loading.

Recommended reading

  • Relearn the Java design pattern: Practice the Factory Method pattern
  • Java Development Architecture: Introduction to domain-driven Design DDD Landing
  • Java Development Architecture: DDD Model Domain-level decision Tree Service Design
  • Java development architecture: domain-driven design architecture builds microservices based on SpringCloud
  • 110,000 words bytecode programming series collection broadcast

Nine, eggs

CodeGuide | programmers coding guidelines Go! This code base is a technical summary of the author’s learning process of Internet Java development for many years. It aims to provide you with a clear and detailed learning tutorial, focusing on the writing of Java core content. If the warehouse can help you, please give support (attention, like, share)!

CodeGuide | programmers coding guidelines