Práctica de una variedad de patrones de diseño comunes en el trabajo

La mayoría de las veces escribo algún código comercial, tal vez un montón de CRUD pueda resolver el problema, pero este tipo de trabajo no mejora mucho al personal técnico, cómo liberarme del negocio y encontrar la diversión de escribir código, lo hago. Después de algunos intentos, usar patrones de diseño para mejorar su código comercial es uno de ellos.

Práctica de patrón de diseño

Patrón de diseño de cadena de responsabilidad

Definición del patrón de diseño de la cadena de responsabilidad

La solicitud se procesa en una cadena y el aceptador en la cadena decide si continuar reenviando o interrumpir el flujo de procesamiento actual después del procesamiento.

Escena aplicable

Adecuado para el procesamiento de procesos de múltiples nodos, cada nodo completa su propia parte de responsabilidad y los nodos no conocen la existencia de los demás, como el flujo de aprobación de OA, el mecanismo de filtro en el desarrollo web java. Para dar un ejemplo en la vida, cuando estaba alquilando una casa, conocí a un supuesto intermediario negro. Cuando alquilé una casa, sentí que era un dios, pero cuando le pedí que arreglara algo roto, él era como un nieto. El intermediario me pidió que buscara una tienda para atención al cliente. El servicio de atención al cliente me pidió que volviera a buscar al arrendador y el arrendador me pidió que buscara a su marido, al final el asunto se resolvió al final (hay que buscar un agente habitual para alquilar una casa).

Experiencia

El negocio que hago actualmente es el pago total de las comidas grupales del campus El proceso de negocio es muy simple: 1. El estudiante abre el código de pago móvil para pagar, 2. La tía de la cafetería usa la máquina para escanear el código de pago para cobrar el pago. El trasfondo del comedor universitario es así. El comedor está subvencionado y los platos son relativamente baratos. Por tanto, la escuela no está dispuesta a dejar que el público vaya al comedor escolar a consumir. Ante esto, hemos añadido un conjunto de lógicas para comprobar si se permite el pago antes de pagar. como sigue:

1. Un puesto permite consumir sólo a ciertos tipos de usuarios, como los puestos de profesores sólo permiten que los profesores consuman, los puestos de estudiantes no permiten que los usuarios externos consuman;

2. Un determinado puesto solo permite que ciertos tipos de usuarios consuman unas pocas veces al día, por ejemplo, el comedor de profesores solo permite a los estudiantes consumir una vez al día;

3. Si a los estudiantes no halal se les permite consumir, como en ciertos restaurantes halal, no se les permite consumir a los estudiantes no halal;

Para este tipo de situaciones, he establecido tres tipos de filtros, a saber:

SpecificCardUserConsumeLimitFilter: determina si se permite el consumo según el tipo de usuario

DayConsumeTimesConsumeLimitFilter: Determinar si se permite el consumo según el número de consumo diario

MuslimConsumeLimitFilter: si los usuarios no halal pueden consumir

La lógica del juicio es primero juzgar si el usuario actual puede consumir en este puesto a través de SpecificCardUserConsumeLimitFilter. Si se permite continuar, DayConsumeTimesConsumeLimitFilter determinará si el número de consumos del día se ha agotado, si no se ha agotado, continuará usando MuslimConsumeLimitFilter para determinar si el usuario actual halal las condiciones del restaurante. Tres juicios, siempre que uno no esté satisfecho, regresan antes.

Parte del código es el siguiente:

public boolean canConsume(String uid,String shopId,String supplierId){
    //获取用户信息,用户信息包含类型(student:学生,teacher:老师,unknown:未知用户)、名族(han:汉族,mg:蒙古族)
    UserInfo userInfo = getUserInfo(uid);    //获取消费限制信息,限制信息包含是否允许非清真消费、每种类型的用户是否允许消费以及允许消费的次数
   ConsumeConfigInfo consumeConfigInfo = getConsumeConfigInfo(shopId,supplierId)     // 构造消费限制过滤器链条
    ConsumeLimitFilterChain filterChain = new ConsumeLimitFilterChain();
    filterChain.addFilter(new SpecificCardUserConsumeLimitFilter());
    filterChain.addFilter(new DayConsumeTimesConsumeLimitFilter());
    filterChain.addFilter(new MuslimConsumeLimitFilter());
    boolean checkResult = filterChain.doFilter(filterChain, schoolMemberInfo, consumeConfigInfo);        //filterChain.doFilter方法
   public boolean doFilter(ConsumeLimitFilterChain filterChain,UserInfo userInfo,    ConsumeConfigInfo consumeConfigInfo ){  //迭代调用过滤器
  if(index<filters.size()){
      return filters.get(index++).doFilter(filterChain, userInfo, consumeConfigInfo);
  }    }       //SpecificCardUserConsumeLimitFilter.doFilter方法
     public boolean doFilter(ConsumeLimitFilterChain filterChain,UserInfo userInfo,    ConsumeConfigInfo consumeConfigInfo ){                //获取某一类型的消费限制,比如student允许消费,unknown不允许消费
  CardConsumeConfig cardConsumeConfig = findSuitCardConfig(userInfo, consumeConfigInfo);      // 判断当前卡用户是否允许消费
  if (consumeCardConfig != null) {
   if ((!CAN_PAY.equals(cardConsumeConfig .getEnabledPay()))) {
       return false;
   }  }                //其余情况,继续往后传递
            return filterChain.doFilter(filterChain, memberInfo, consumeConfig);
        }    //DayConsumeTimesConsumeLimitFilter.doFilter方法
     public boolean doFilter(ConsumeLimitFilterChain filterChain,UserInfo userInfo,    ConsumeConfigInfo consumeConfigInfo ){                //获取某一类型的消费限制,比如student可以消费2次
  CardConsumeConfig cardConsumeConfig = findSuitCardConfig(userInfo, consumeConfigInfo);                  //获取当前用户今天的消费次数
                int consumeCnt = getConsumeCnt(userInfo)    if(consumeCnt >= cardConsumeConfig.getDayConsumeTimesLimit()){
                    return false;
                }                //其余情况,继续往后传递
                return filterChain.doFilter(filterChain, memberInfo, consumeConfig);
        }

para resumir

La lógica de juicio de cada condición de restricción está encapsulada en un Filtro específico. Si se modifica la lógica de una condición de restricción determinada, otras condiciones no se verán afectadas. Si necesita agregar una nueva condición de restricción, solo necesita reconstruir un Filtro y tejerlo en la Cadena de Filtro.

Patrón de diseño de estrategia

Definición de patrón de diseño de estrategia

Definir una serie de algoritmos, encapsular cada algoritmo y hacerlos intercambiables

Escena aplicable

El objetivo principal es eliminar una gran cantidad de códigos if else y extraer la lógica del algoritmo detrás de cada juicio en un objeto de estrategia específico. Cuando se modifica la lógica del algoritmo, el usuario no percibirá al usuario y solo necesitará modificar la lógica interna del objeto de estrategia. Estos objetos de política generalmente implementan una interfaz común y pueden lograr el propósito de intercambio.

Experiencia

El autor tiene un requisito antes de que después de que el usuario escanee el código QR y pague, envíe un mensaje de pago al dispositivo del cajero en el puesto. El dispositivo del cajero transmitirá el mensaje después de recibir el mensaje. La lógica es muy simple, simplemente llame a la plataforma push para enviar un mensaje al dispositivo, pero Debido a razones históricas, la plataforma push para ciertos dispositivos es diferente. Los dispositivos de clase A prefieren usar pigeon push. Si falla, debe ser degradado al mecanismo de sondeo largo. Los dispositivos de clase B pueden usar directamente la plataforma push de desarrollo propio. La situación actual es que los formatos de mensaje de Tipo A y Tipo B son diferentes (desarrollados por diferentes equipos e integrados posteriormente). En vista de esto, abstraí la interfaz PushStrategy. Las implementaciones específicas son IotPushStrategy y XingePushStrategy, que corresponden a autodesarrollo Para la estrategia push de la plataforma push y la estrategia push de la plataforma pigeon, los usuarios pueden usar diferentes estrategias push para diferentes tipos de dispositivos. Parte del código es el siguiente:

/**
 * 推送策略 * /
public interface PushStrategy {
 /**
         @param deviceVO设备对象,包扣设备sn,信鸽pushid         @param content,推送内容,一般为json        */
 public CallResult push(AppDeviceVO deviceVO, Object content);
}
IotPushStrategy implements PushStrategy{
        /**
         @param deviceVO设备对象,包扣设备sn,信鸽pushid         @param content,推送内容,一般为json        */
 public CallResult push(AppDeviceVO deviceVO, Object content){
            //创建自研推送平台需要的推送报文
            Message message = createPushMsg(deviceVO,content);
            
            //调用推送平台推送接口
            IotMessageService.pushMsg(message);
        }
}
XingePushStrategy implements PushStrategy{
        /**
         @param deviceVO设备对象,包扣设备sn,信鸽pushid         @param content,推送内容,一般为json        */
 public CallResult push(AppDeviceVO deviceVO, Object content){
            //创建信鸽平台需要的推送报文
            JSONObject jsonObject = createPushMsg(content);
            
            //调用推送平台推送接口
            if(!XinggePush.pushMsg(message)){
                //降级到长轮询
                ...
            }
        }
}
/**
消息推送Service*/
MessagePushService{
    pushMsg(AppDeviceVO deviceVO, Object content){
        if(A设备){
            XingePushStrategy.push(deviceVO,content);
        } else if(B设备){
            IotPushStrategy.push(deviceVO,content);
        }
    }
}

para resumir

La lógica de empuje de cada canal está encapsulada en una estrategia específica. El cambio de una determinada estrategia no afectará a otras estrategias. Debido a que se implementa la interfaz común, las estrategias se pueden reemplazar entre sí, lo cual es fácil de usar, como la estrategia de rechazo de tareas en java ThreadPoolExecutor , Cuando el grupo de subprocesos está saturado, se ejecutará la estrategia de rechazo y la lógica de rechazo específica se encapsula en la ejecución del objeto RejectedExecutionHandler.

Patrón de diseño de plantilla

Definición de patrón de diseño de plantilla

El valor de la plantilla reside en la definición del esqueleto. Se ha definido el proceso de procesamiento de problemas dentro del esqueleto. La lógica de procesamiento general generalmente es implementada por la clase padre y la lógica de procesamiento personalizado es implementada por la subclase. Por ejemplo, patatas fritas en tiras y mapo tofu frito, la lógica general es 1. cortar verduras, 2. poner aceite, 3. sofreír, 4. cocinar, 1, 2, 4 son similares, pero el tercer paso es diferente. Las tiras de papa salteadas se deben sofreír con una pala, pero el tofu Mapo frito debe empujarse con una cuchara, de lo contrario el tofu se pudrirá.

escenas a utilizar

En el flujo de procesamiento de diferentes escenarios, parte de la lógica es universal y se puede colocar en la clase principal como una implementación universal, y parte de la lógica está personalizada y debe ser implementada por la subclase.

Experiencia

Siguiendo el ejemplo de la transmisión de voz anterior, agregamos dos nuevos requisitos más adelante: 1. La inserción del mensaje debe aumentar el seguimiento.

2. Algunos canales no se pueden enviar y es necesario volver a intentarlo.

Entonces el proceso actual se vuelve así:

1.Se inicia el rastreo

2. El canal comienza a empujar

3. Si se permite el reintento, si se permite ejecutar la lógica de reintento.

4. Fin del seguimiento

Entre ellos, 1 y 4 son universales, y 2 y 3 son personalizados. En vista de esto, agregué una capa de estrategia principal antes de la estrategia de empuje específica y puse la lógica general en la clase principal. El código modificado es el siguiente:

abstract class AbstractPushStrategy implements PushStrategy{
    @Override
    public CallResult push(AppDeviceVO deviceVO, Object content) {
        //1.构造span
        Span span = buildSpan();
        //2.具体通道推送逻辑由子类实现
        CallResult callResult = doPush(deviceVO, content);
        
        //3.是否允许重试逻辑由子类实现,如果允许执行重试逻辑
        if(!callResult.isSuccess() && canRetry()){
            doPush(deviceVO, content);
        }
        
        //4.trace结束
        span.finish() 
    }
    
    //具体推送逻辑由子类实现
    protected abstract CallResult doPush(AppDeviceVO deviceDO, Object content) ;
    //是否允许重试由子类实现,有些通道之前没有做消息排重,所有不能重试
    protected abstract boolean canRetry(CallResult callResult);
}
XingePushStrategy extends AbstractPushStrategy{
    @Override
    protected CallResult doPush(AppDeviceVO deviceDO, Object content) {
        //执行推送逻辑
    }
    
    @Override
    protected boolean canRetry(CallResult callResult){
        return false
    }
}

para resumir

El proceso se define a través de la plantilla y la lógica común se implementa en la clase padre, lo que reduce el código repetitivo. La lógica personalizada es implementada por la propia subclase, y la modificación del código entre las subclases no interfiere entre sí ni destruye el proceso.

Patrón de diseño de observador

Definición del patrón de diseño del observador

Como sugiere el nombre, este modo requiere dos roles: Observador y Observable. Cuando el estado del Observable cambia, notificará al Observador. El Observador generalmente implementa una interfaz común, como java.util.Observer, que es requerida por Observable. Cuando notifique al Observador, simplemente llame al método de actualización del Observador uno por uno. El éxito o fracaso del procesamiento del Observador no debería afectar el proceso Observable.

escenas a utilizar

El cambio de estado de un objeto (Observable) debe notificar a otros objetos. La existencia de Observer no afecta el resultado de procesamiento de Observable. La adición y eliminación de Observable no es consciente de Observable, como la suscripción al mensaje de Kafka, el productor envía un mensaje al tema, ya sea 1 o 10 Los consumidores se suscriben a este tema, los productores no necesitan prestar atención.

Experiencia

En el modelo de diseño de cadena de responsabilidad, resolví el problema de inspección de restricción de consumo a través de tres filtros. Uno de los filtros se usa para verificar el número de consumos. Aquí solo leo los tiempos de consumo del usuario, entonces, ¿cómo se completa la acumulación de tiempos de consumo? ¿Qué pasa? De hecho, el modo de observador se utiliza para la acumulación. Específicamente, cuando el sistema de transacción recibe la devolución de llamada de éxito de pago, publicará el "evento de éxito de pago" a través del mecanismo de evento de Spring, que se encarga de acumular el número de consumo y la suscripción de la transmisión de voz. La persona recibirá el "evento de éxito del pago", y luego hará su propia lógica comercial, dibujará un diagrama simple para describir:

Práctica de una variedad de patrones de diseño comunes en el trabajo

 

 

La estructura del código es aproximadamente la siguiente:

/**
支付回调处理者*/
PayCallBackController implements ApplicationContextAware {
     private ApplicationContext applicationContext;
    //如果想获取applicationContext需要实现ApplicationContextAware接口,Spring容器会回调setApplicationContext方法将applicationContext注入进来
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext = applicationContext;
    }
     @RequestMapping(value = "/pay/callback.do")
     public View callback(HttpServletRequest request){
        if(paySuccess(request){
            //构造支付成功事件
            PaySuccessEvent event = buildPaySuccessEvent(...);
            //通过applicationContext发布事件,从而达到通知观察者的目的
            this.applicationContext.publishEvent(event);
        } 
    }
}
/**
 * 语音播报处理者 * */
public class VoiceBroadcastHandler implements ApplicationListener<PaySuccessEvent>{
    @Override
    public void onApplicationEvent(PaySuccessEvent event) {
        //语音播报逻辑
    }
}
//其他处理者的逻辑类似

para resumir

El modo de observador desacopla al observador y el observador, y la presencia o ausencia del observador no afectará la lógica existente del observador.

Patrón de diseño de decorador

Definición de patrón de diseño de decorador

El decorador se utiliza para envolver la clase original y mejorar la función sin dejar de ser transparente para el usuario. Por ejemplo, BufferedInputStream en java puede mejorar el InputStream que envuelve para proporcionar una función de almacenamiento en búfer.

escenas a utilizar

Cuando desee mejorar las funciones de la clase original, pero no desee agregar demasiadas subclases, puede usar el modo decorador para lograr el mismo efecto.

Experiencia

El autor anteriormente estaba promocionando a toda la empresa para acceder al sistema de seguimiento, por lo que también proporcioné algunas herramientas para resolver el tejido automático de seguimiento y la transmisión automática de contexto. Para apoyar la transmisión de contexto entre hilos, agregué la clase decorativa TraceRunnableWrapper para ayudar El contexto del hilo principal se transmite de forma transparente al hilo secundario, que es completamente transparente para el usuario. El código es el siguiente:

/**
可以自动携带trace上下文的Runnable装饰器
*/
public class TraceRunnableWrapper implements Runnable{
    //被包装的目标对象
    private Runnable task;
    private Span parentSpan = null;
    public TraceRunnableWrapper(Runnable task) {
        //1.获取当前线程的上下文(因为new的时候还没有发生线程切换,所以需要在这里将上下文获取)
        //对这块代码感兴趣的可以查看opentracing API
        io.opentracing.Scope currentScope = GlobalTracer.get().scopeManager().active();
        //2.保存父上下文
        parentSpan = currentScope.span();
        this.task = task;
    }
    @Override
    public void run() {
        //run的时候将父线程的上下文绑定到当前线程
        io.opentracing.Scope scope = GlobalTracer.get().scopeManager().activate(parentSpan,false);
        task.run();
    }
}
//使用者
new Thread(new Runnable(){run(...)}).start()替换为new TraceRunnableWrapper(new Runnable(){run(...)}).start()

para resumir

Use el modo decorador para mejorar la función, ya que los usuarios solo necesitan hacer una combinación simple para continuar usando la función original.

Modo de diseño de apariencia

Definición del modelo de diseño de apariencia

Lo que es apariencia es proporcionar una entrada unificada al mundo exterior. Una es ocultar los detalles internos del sistema, y ​​la otra es reducir la complejidad de los usuarios. Por ejemplo, DispaterServlet en SpringMvc, todos los Controladores están expuestos uniformemente a través de DispaterServlet.

escenas a utilizar

Reduzca la complejidad de los usuarios y simplifique los costos de acceso de los clientes.

Experiencia

La empresa del autor proporciona algunas capacidades abiertas a los ISV de terceros, como la gestión y el control de equipos, el pago unificado y la descarga de estados de cuenta. Dado que pertenecen a diferentes equipos, las interfaces externas proporcionadas tienen diferentes formas. Si hay más, los ISV también pueden aceptarlo. Sin embargo, cuando hay más interfaces, los ISV comenzaron a quejarse del alto costo de acceso. Para resolver este problema, agregamos un controlador de front-end GatewayController frente a la interfaz abierta, que en realidad es el prototipo de nuestra plataforma abierta posterior. , GatewayController expone uniformemente una interfaz gateway.do al mundo exterior. Los parámetros de solicitud y los parámetros de respuesta de la interfaz externa se unifican en el GatewayController para la convergencia. El GatewayController también utiliza una interfaz unificada cuando se enruta al servicio back-end. La comparación antes y después de la transformación es la siguiente:

Práctica de una variedad de patrones de diseño comunes en el trabajo

 


Probablemente el código sea el siguiente:

使用者:
HttpClient.doPost("/gateway.do","{'method':'trade.create','sign':'wxxaaa','timestamp':'15311111111'},'bizContent':'业务参数'")
GatewayController:
@RequestMapping("/gateway.do")
JSON gateway(HttpServletRequest req){   //1.组装开放请求
   OpenRequest openRequest = buildOpenRequest(req);   OpenResponse openResponse = null;   //2.请求路由
   if("trade.create".equals(openRequest.getMethod()){
       //proxy to trade service by dubbo
       openResponse = TradeFacade.execute(genericParam);   } else if("iot.message.push".equals(openRequest.getMethod()){
       //proxy to iot service by httpclient
        openResponse = HttpClient.doPost('http://iot.service/generic/execute'genericParam);
   }   if(openResponse.isSuccess()){
        return {"code":"10000","bizContent":openResponse.getResult()};
   }else{
        return {"code":"20000","bizCode":openResponse.getCode()};
   }    }

para resumir:

El modo de apariencia se utiliza para proteger algunos detalles dentro del sistema y reducir el costo de acceso del usuario. Tome GatewayController como ejemplo. La autenticación ISV, la verificación de la interfaz y otras tareas repetitivas están unificadas por él. Los ISV solo necesitan conectar diferentes interfaces Preocupado por un conjunto de interfaces de protocolo de interfaz, la capa GatewayController realiza la convergencia.

Supongo que te gusta

Origin blog.csdn.net/weixin_45132238/article/details/108536684
Recomendado
Clasificación