Para não fazer o código parecer um monte de *, usei isso repetidamente em meu trabalho

Na maioria das vezes eu escrevo algum código de negócios, talvez um monte de CRUD possa resolver o problema, mas esse tipo de trabalho não melhora muito o pessoal técnico, como me libertar do negócio e encontrar a diversão de escrever código, eu faço Depois de algumas tentativas, usar padrões de design para melhorar seu código de negócios é uma delas.

Padrão de design da cadeia de responsabilidade

definição

A solicitação é processada em uma cadeia e o aceitante na cadeia decide se continua encaminhando ou interrompe o fluxo de processamento atual após o processamento.

Cena aplicável

Adequado para processamento de processos de vários nós, cada nó cumpre sua própria parte de responsabilidade e os nós não conhecem a existência uns dos outros, como o fluxo de aprovação do OA, o mecanismo de filtro no desenvolvimento web java. Para dar um exemplo de vida, quando estava alugando uma casa, conheci um chamado intermediário negro. Quando aluguei uma casa, senti que era um deus, mas quando lhe pedi para consertar algo quebrado, ele parecia um neto. O intermediário me pediu para encontrar uma loja de atendimento ao cliente. O serviço ao cliente pediu-me que procurasse novamente o senhorio e o senhorio pediu-me que procurasse o marido dela.Finalmente o assunto foi resolvido no final (tem de arranjar um agente regular para alugar uma casa).

Experiência

O negócio que faço atualmente é o pagamento agregado para refeições em grupo no campus. O processo de negócios é muito simples: 1. O aluno abre o código de pagamento móvel para pagar, 2. A tia do refeitório usa a máquina para escanear o código de pagamento para coletar o pagamento. O pano de fundo da cantina universitária é o seguinte. A cantina é subsidiada e os pratos são relativamente baratos. Portanto, a escola não está disposta a deixar o público ir à cantina para consumir. Diante disso, adicionamos um conjunto de lógica para verificar se o pagamento é permitido antes de pagar. do seguinte modo: 

  1. Uma determinada barraca permite que apenas determinados tipos de usuários consumam, por exemplo, a barraca do professor só permite que os professores consumam e a barraca do aluno não permite que usuários externos consumam;

  2. Uma certa barraca permite apenas que certos tipos de usuários consumam algumas vezes ao dia, por exemplo, a lanchonete de um professor permite que os alunos consumam apenas uma vez por dia;

  3. Se os alunos não halal estão autorizados a consumir, como certos restaurantes halal, os alunos não halal não estão autorizados a consumir;

Para esses tipos de situações, estabeleci três tipos de filtros, a saber:

SpecificCardUserConsumeLimitFilter: determine se permite o consumo de acordo com o tipo de usuário 

DayConsumeTimesConsumeLimitFilter: determine se permite o consumo de acordo com o número de consumo diário 

MuslimConsumeLimitFilter: se os usuários não halal têm permissão para consumir 

A lógica de julgamento é primeiro julgar se o usuário atual pode consumir nesta tenda por meio de SpecificCardUserConsumeLimitFilter. Se for permitido continuar, DayConsumeTimesConsumeLimitFilter determinará se o número de consumos do dia se esgotou, caso não seja, continue a usar MuslimConsumeLimitFilter para determinar se o usuário atual atende às condições de jantar do restaurante. Três julgamentos, enquanto um não estiver satisfeito, volte mais cedo.

Parte do código é o seguinte:

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);
        }

Resumindo

A lógica de julgamento de cada condição de restrição é encapsulada em um filtro específico. Se a lógica de uma determinada condição de restrição for modificada, outras condições não serão afetadas. Se você precisar adicionar uma nova condição de restrição, você só precisa reconstruir um filtro e entrelaçá-lo no FilterChain.

Padrão de design de estratégia

definição

Defina uma série de algoritmos, encapsule cada algoritmo e torne-os intercambiáveis

Cena aplicável

O objetivo principal é eliminar um grande número de códigos if else e extrair a lógica do algoritmo por trás de cada julgamento em um objeto de estratégia específico. Quando a lógica do algoritmo é modificada, o usuário não perceberá o usuário e só precisará modificar a lógica interna do objeto de estratégia. Esses objetos de política geralmente implementam uma interface comum e podem atingir o propósito de intercâmbio.

Experiência

O autor tem um requisito antes que, depois que o usuário escaneie o código QR e pague, envie uma mensagem de pagamento para o dispositivo de caixa na barraca. O dispositivo de caixa transmitirá a mensagem após receber a mensagem. A lógica é muito simples, basta chamar a plataforma push para enviar uma mensagem para o dispositivo, mas Devido a razões históricas, a plataforma push para determinados dispositivos é diferente. Os dispositivos de classe A preferem usar o pigeon push. Se falhar, será necessário fazer o downgrade para o mecanismo de pesquisa longa. Os dispositivos de classe B podem usar diretamente a plataforma de push autodesenvolvida. A situação atual é que os formatos de mensagem do Tipo A e do Tipo B são diferentes (desenvolvidos por equipes diferentes e integrados posteriormente). Em vista disso, abstraiai a interface PushStrategy. As implementações específicas são IotPushStrategy e XingePushStrategy, que correspondem ao desenvolvimento próprio Para a estratégia push da plataforma push e a estratégia push da plataforma pigeon, os usuários podem usar diferentes estratégias de push para diferentes tipos de dispositivos. Parte do código é o seguinte:

/**
 * 推送策略
 * /
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);
        }
    }
}

Resumindo

A lógica push de cada canal é encapsulada em uma estratégia específica. A mudança de uma determinada estratégia não afetará outras estratégias. Como a interface comum é implementada, as estratégias podem ser substituídas entre si, o que é amigável, como a estratégia de rejeição de tarefa em java ThreadPoolExecutor , Quando o pool de threads estiver saturado, a estratégia de rejeição será executada e a lógica de rejeição específica será encapsulada na rejeiçãoExecução do RejectedExecutionHandler.

Padrão de design de modelo

definição

O valor do template reside na definição do esqueleto.O processo de processamento do problema dentro do esqueleto foi definido.A lógica de processamento geral é geralmente implementada pela classe pai, e a lógica de processamento personalizada é implementada pela subclasse. Por exemplo, pedaços de batata frita e tofu mapo frito, a lógica geral é 1. cortar vegetais, 2. colocar óleo, 3. fritar, 4. cozinhar, 1, 2, 4 são semelhantes, mas a terceira etapa é diferente. Pedaços de batata frita devem ser refogados com uma pá, mas o tofu Mapo frito deve ser cutucado com uma colher, caso contrário, o tofu pode estragar.

cenas a serem usadas

No fluxo de processamento de diferentes cenários, parte da lógica é universal e pode ser colocada na classe pai como uma implementação universal, e parte da lógica é personalizada e precisa ser implementada pela subclasse.

Experiência

Seguindo o exemplo da transmissão de voz anterior, adicionamos dois novos requisitos posteriormente: 1. O envio de mensagem precisa adicionar rastreamento 2. Alguns envios de canal falham e precisam ser repetidos, então o processo atual fica assim: 1. O rastreamento inicia 2. Canal Iniciar push 3. Permitir nova tentativa, se a execução da lógica de nova tentativa for permitida 4. O rastreamento termina onde 1 e 4 são universais, 2 e 3 são personalizados. Em vista disso, adicionei uma classe pai antes da estratégia de push específica A estratégia de colocar a lógica geral na classe pai, o código modificado é o seguinte:

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
    }
}

Resumindo

O processo é definido por meio do modelo e a lógica comum é implementada na classe pai, o que reduz o código repetitivo. A lógica personalizada é implementada pela própria subclasse, e a modificação do código entre as subclasses não interfere entre si nem destrói o processo.

Padrão de design do observador

Definição de modo

Como o nome sugere, este modo requer duas funções: Observer e Observable. Quando o status do Observable muda, ele notifica o Observer. O Observer geralmente implementa uma interface comum, como java.util.Observer, que é exigida pelo Observable. Ao notificar o Observer, basta chamar o método de atualização do Observer um a um.O sucesso ou falha do processamento do Observer não deve afetar o processo Observable.

cenas a serem usadas

Uma mudança de status de objeto (Observable) precisa notificar outros objetos. A existência de Observer não afeta o resultado do processamento de Observable. A adição e exclusão de Observer não reconhece Observable, como a assinatura de mensagem de Kafka, o produtor envia uma mensagem para o tópico, se for 1 ou 10 Os consumidores se inscrevem neste tópico, os produtores não precisam prestar atenção.

Experiência

No modelo de desenho da cadeia de responsabilidade resolvi o problema de fiscalização da restrição de consumo através de três filtros.Um dos filtros é usado para verificar a quantidade de consumos.Aqui eu apenas li os tempos de consumo do usuário, então como se completa o acúmulo de tempos de consumo? A respeito? Na verdade, o modo observador é usado para o acúmulo. Especificamente, quando o sistema de transação recebe o retorno de chamada de sucesso do pagamento, ele publicará o "evento de sucesso do pagamento" através do mecanismo de evento Spring, que é responsável por acumular o número de consumos e a assinatura da transmissão de voz A pessoa receberá o "evento de sucesso do pagamento" e, em seguida, fará sua própria lógica de negócios, desenhando um diagrama simples para descrever:

Para não fazer o código parecer um monte de *, usei isso repetidamente em meu trabalho

A estrutura do código é aproximadamente a seguinte:

/**
支付回调处理者
*/
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) {
        //语音播报逻辑
    }
}

//其他处理者的逻辑类似

Resumindo

O modo de observador separa o observador e o observador, e a presença ou ausência do observador não afetará a lógica existente do observador.

Padrão de design do decorador

definição

O decorador é usado para encapsular a classe original e aprimorar a função, sendo transparente para o usuário.Por exemplo, o BufferedInputStream em java pode aprimorar o InputStream que encapsula para fornecer uma função de buffer.

cenas a serem usadas

Quando você deseja aprimorar as funções da classe original, mas não deseja adicionar muitas subclasses, pode usar o modo decorador para obter o mesmo efeito.

Experiência

O autor estava promovendo toda a empresa para acessar o sistema de rastreamento antes, então eu também forneci algumas ferramentas para resolver a tecelagem automática de rastreamento e a transmissão automática de contexto. Se você estiver interessado, você pode ler meu outro blog jaeger usando exploração preliminar, a fim de apoiar inter-thread Para transferência de contexto, adicionei a classe decorativa TraceRunnableWrapper para transferir de forma transparente o contexto do thread pai para o thread filho, que é completamente transparente para o usuário. O código é o seguinte:

/**
可以自动携带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()

Resumindo

Use o modo decorador para aprimorar a função, pois os usuários só precisam fazer uma combinação simples para continuar a usar a função original.

Modo de design de aparência

definição

O que é aparência é fornecer uma entrada unificada para o mundo externo. Uma é ocultar os detalhes do sistema e a outra é reduzir a complexidade dos usuários. Por exemplo, o DispaterServlet em SpringMvc, todos os controladores são expostos por meio de DispaterServlet.

cenas a serem usadas

Reduza a complexidade dos usuários e simplifique os custos de acesso do cliente.

Experiência

A empresa do autor fornece alguns recursos abertos para ISVs de terceiros, como gerenciamento e controle de equipamentos, pagamento unificado e recursos de download de extratos. Como eles pertencem a equipes diferentes, as interfaces externas fornecidas estão em formas diferentes. Se houver mais, os ISVs também podem aceitá-lo. No entanto, quando há mais interfaces, os ISVs começam a reclamar do alto custo de acesso. Para resolver esse problema, adicionamos um controlador de front-end GatewayController na frente da interface aberta, que na verdade é o protótipo de nossa plataforma aberta posterior. , GatewayController expõe uniformemente uma interface gateway.do para o mundo externo. Os parâmetros de solicitação e de resposta da interface externa são unificados no GatewayController para convergência. O GatewayController também usa uma interface unificada ao rotear para o serviço de back-end. A comparação antes e depois da transformação é a seguinte:

Para não fazer o código parecer um monte de *, usei isso repetidamente em meu trabalho

Provavelmente, o código é o seguinte:

使用者:
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()};
   }

}

Resumindo

O modo de aparência é usado para blindar alguns detalhes dentro do sistema e reduzir o custo de acesso do usuário. Tome como exemplo o GatewayController. A autenticação do ISV, verificação de interface e outras tarefas repetitivas são unificadas por ele. Os ISVs só precisam conectar diferentes interfaces Preocupada com um conjunto de interfaces de protocolo de interface, a convergência é feita pela camada GatewayController.

Acho que você gosta

Origin blog.51cto.com/14570694/2540472
Recomendado
Clasificación