Um den Code nicht wie ein Haufen * aussehen zu lassen, habe ich ihn in meiner Arbeit wiederholt verwendet

Die meiste Zeit schreibe ich Geschäftscode, vielleicht kann ein Haufen CRUD das Problem lösen, aber diese Art von Arbeit verbessert die technischen Leute nicht sehr, wie ich mich aus dem Geschäft befreien und den Spaß am Schreiben von Code finden kann Nach einigen Versuchen ist die Verwendung von Entwurfsmustern zur Verbesserung Ihres Geschäftscodes eine davon.

Entwurfsmuster der Verantwortungskette

Definition

Die Anforderung wird in einer Kette verarbeitet, und der Akzeptor in der Kette entscheidet, ob die Weiterleitung fortgesetzt oder der aktuelle Verarbeitungsfluss nach der Verarbeitung unterbrochen werden soll.

Anwendbare Szene

Jeder Knoten, der für die Prozessverarbeitung mit mehreren Knoten geeignet ist, übernimmt seinen eigenen Teil der Verantwortung, und die Knoten kennen die Existenz des anderen nicht, wie z. B. den Genehmigungsfluss von OA, dem Filtermechanismus in der Java-Webentwicklung. Um ein Beispiel im Leben zu geben, als ich ein Haus mietete, traf ich einen sogenannten schwarzen Vermittler. Als ich ein Haus mietete, fühlte ich mich als Gott, aber als ich ihn bat, etwas kaputtes zu reparieren, war er wie ein Enkel. Der Vermittler bat mich, ein Geschäft für den Kundenservice zu finden. Der Kundendienst bat mich, den Vermieter wieder zu finden, und der Vermieter bat mich, ihren Ehemann zu finden. Am Ende wurde die Angelegenheit erledigt (Sie müssen einen regulären Vertreter für die Anmietung eines Hauses finden).

Erfahrung

Das Geschäft, das ich derzeit mache, ist die Gesamtzahlung für Mahlzeiten in Campusgruppen. Der Geschäftsprozess ist sehr einfach: 1. Der Student öffnet den mobilen Zahlungscode, um zu bezahlen. 2. Die Tante der Cafeteria scannt den Zahlungscode mit dem Automaten, um die Zahlung einzuziehen. Der Hintergrund der Universitätskantine ist wie folgt: Die Kantine wird subventioniert und das Geschirr ist relativ billig. Daher ist die Schule nicht bereit, die Öffentlichkeit zum Konsumieren in die Schulkantine zu lassen. In Anbetracht dessen haben wir eine Reihe von Logik hinzugefügt, um zu prüfen, ob die Zahlung vor der Zahlung zulässig ist. wie folgt: 

  1. Ein bestimmter Stand erlaubt nur bestimmten Benutzertypen zu konsumieren. Beispielsweise erlaubt der Lehrerstand nur Lehrern zu konsumieren, und der Schülerstand erlaubt nicht, dass externe Benutzer konsumieren.

  2. An einem bestimmten Stand können bestimmte Benutzertypen nur einige Male am Tag konsumieren. In der Cafeteria eines Lehrers können Schüler beispielsweise nur einmal am Tag konsumieren.

  3. Unabhängig davon, ob Nicht-Halal-Schüler wie bestimmte Halal-Restaurants konsumieren dürfen, dürfen Nicht-Halal-Schüler nicht konsumieren.

Für diese Art von Situationen habe ich drei Arten von Filtern festgelegt, nämlich:

SpecificCardUserConsumeLimitFilter: Bestimmen Sie, ob der Verbrauch je nach Benutzertyp zulässig ist 

DayConsumeTimesConsumeLimitFilter: Bestimmen Sie, ob der Verbrauch entsprechend der Anzahl des täglichen Verbrauchs zulässig ist 

MuslimConsumeLimitFilter: Gibt an, ob Nicht-Halal-Benutzer konsumieren dürfen 

Die Beurteilungslogik besteht darin, zunächst zu beurteilen, ob der aktuelle Benutzer an diesem Stand über SpecificCardUserConsumeLimitFilter konsumieren kann. Wenn DayConsumeTimesConsumeLimitFilter fortfahren darf, bestimmt DayConsumeTimesConsumeLimitFilter, ob die Anzahl der für den Tag verbrauchten Konsumenten aufgebraucht ist. Drei Urteile, solange eines nicht zufrieden ist, kehren früh zurück.

Ein Teil des Codes lautet wie folgt:

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

um zusammenzufassen

Die Beurteilungslogik jeder Einschränkungsbedingung ist in einem bestimmten Filter gekapselt. Wenn die Logik einer bestimmten Einschränkungsbedingung geändert wird, werden andere Bedingungen nicht beeinflusst. Wenn Sie eine neue Einschränkungsbedingung hinzufügen müssen, müssen Sie nur einen Filter neu erstellen und in die FilterChain einbinden.

Strategie-Design-Muster

Definition

Definieren Sie eine Reihe von Algorithmen, kapseln Sie jeden Algorithmus und machen Sie sie austauschbar

Anwendbare Szene

Der Hauptzweck besteht darin, eine große Anzahl von if else-Codes zu eliminieren und die Algorithmuslogik hinter jeder Beurteilung in ein bestimmtes Strategieobjekt zu extrahieren. Wenn die Algorithmuslogik geändert wird, nimmt der Benutzer den Benutzer nicht wahr und muss nur die interne Logik des Strategieobjekts ändern. Solche Richtlinienobjekte implementieren im Allgemeinen eine gemeinsame Schnittstelle und können den Zweck des Austauschs erreichen.

Erfahrung

Der Autor hat zuvor eine Anforderung, dass, nachdem der Benutzer den QR-Code gescannt und bezahlt hat, eine Zahlungsnachricht an das Kassiergerät am Stand gesendet wird. Das Kassiergerät sendet die Nachricht nach dem Empfang der Nachricht. Die Logik ist sehr einfach. Rufen Sie einfach die Push-Plattform an, um eine Nachricht an das Gerät zu senden Aus historischen Gründen ist die Push-Plattform für bestimmte Geräte unterschiedlich. Geräte der Klasse A bevorzugen die Verwendung von Tauben-Push. Wenn dies fehlschlägt, muss sie auf den Mechanismus für lange Abfragen herabgestuft werden. Geräte der Klasse B können die selbst entwickelte Push-Plattform direkt verwenden. Die aktuelle Situation ist, dass die Nachrichtenformate von Typ A und Typ B unterschiedlich sind (von verschiedenen Teams entwickelt und später integriert). In Anbetracht dessen habe ich die PushStrategy-Schnittstelle abstrahiert. Die spezifischen Implementierungen sind IotPushStrategy und XingePushStrategy, die selbst entwickelt entsprechen Für die Push-Strategie der Push-Plattform und die Push-Strategie der Taubenplattform können Benutzer unterschiedliche Push-Strategien für unterschiedliche Gerätetypen verwenden. Ein Teil des Codes lautet wie folgt:

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

um zusammenzufassen

Die Push-Logik jedes Kanals ist in einer bestimmten Strategie zusammengefasst. Die Änderung einer bestimmten Strategie wirkt sich nicht auf andere Strategien aus. Da die gemeinsame Schnittstelle implementiert ist, können die Strategien durch andere ersetzt werden, was benutzerfreundlich ist, z. B. die Strategie zur Zurückweisung von Aufgaben in Java ThreadPoolExecutor Wenn der Thread-Pool gesättigt ist, wird die Ablehnungsstrategie ausgeführt und die spezifische Ablehnungslogik in der Zurückweisung des RejectedExecutionHandler gekapselt.

Vorlagenentwurfsmuster

Definition

Der Wert der Vorlage liegt in der Definition des Skeletts. Der Prozess der Problemverarbeitung innerhalb des Skeletts wurde definiert. Die allgemeine Verarbeitungslogik wird im Allgemeinen von der übergeordneten Klasse implementiert, und die personalisierte Verarbeitungslogik wird von der Unterklasse implementiert. Zum Beispiel, gebratene Kartoffelschnitzel und gebratener Mapo-Tofu, ist die allgemeine Logik 1. geschnittenes Gemüse, 2. setzen Öl, 3. braten, 4. kochen, 1, 2, 4 sind ähnlich, aber der dritte Schritt ist anders. Gebratene Kartoffelschnitzel müssen mit einer Schaufel gebraten werden, gebratener Mapo-Tofu muss jedoch mit einem Löffel angestupst werden, da sonst der Tofu verfault.

zu verwendende Szenen

Im Verarbeitungsablauf verschiedener Szenarien ist ein Teil der Logik universell und kann als universelle Implementierung in die übergeordnete Klasse eingefügt werden. Ein Teil der Logik ist personalisiert und muss von der Unterklasse implementiert werden.

Erfahrung

In Anlehnung an das Beispiel der vorherigen Sprachübertragung haben wir später zwei neue Anforderungen hinzugefügt: 1. Message Push muss Trace hinzufügen. 2. Einige Channel Pushs schlagen fehl und müssen erneut versucht werden, sodass der aktuelle Prozess folgendermaßen aussieht: 1. Trace startet 2. Channel Push starten 3. Ob ein erneuter Versuch zulässig ist, wenn die Ausführung der Wiederholungslogik zulässig ist 4. Trace endet dort, wo 1 und 4 universell sind, 2 und 3 personalisiert sind. Aus diesem Grund habe ich vor der spezifischen Push-Strategie eine übergeordnete Klasse hinzugefügt Die Strategie, die allgemeine Logik in die übergeordnete Klasse, den geänderten Code, einzufügen, lautet wie folgt:

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

um zusammenzufassen

Der Prozess wird durch die Vorlage definiert, und die allgemeine Logik wird in der übergeordneten Klasse implementiert, wodurch der sich wiederholende Code reduziert wird. Die personalisierte Logik wird von der Unterklasse selbst implementiert, und die Änderung des Codes zwischen den Unterklassen stört sich nicht und zerstört den Prozess nicht.

Beobachter-Entwurfsmuster

Modusdefinition

Wie der Name schon sagt, erfordert dieser Modus zwei Rollen: Observer und Observable. Wenn sich der Status von Observable ändert, wird der Observer benachrichtigt. Der Observer implementiert im Allgemeinen eine gemeinsame Schnittstelle wie java.util.Observer, die von Observable benötigt wird. Wenn Sie den Observer benachrichtigen, rufen Sie einfach die Aktualisierungsmethode des Observer nacheinander auf. Der Erfolg oder Misserfolg der Observer-Verarbeitung sollte den Observable-Prozess nicht beeinflussen.

zu verwendende Szenen

Eine Änderung des Objektstatus (Observable) muss andere Objekte benachrichtigen. Das Vorhandensein von Observer hat keinen Einfluss auf das Verarbeitungsergebnis von Observable. Das Hinzufügen und Löschen von Observer ist Observable nicht bekannt, z. B. Kafkas Nachrichtenabonnement. Der Produzent sendet eine Nachricht an das Thema, ob es sich um 1 oder 10 handelt Verbraucher abonnieren dieses Thema, Hersteller müssen nicht darauf achten.

Erfahrung

Im Entwurfsmodell der Verantwortungskette habe ich das Problem der Verbrauchsbeschränkungsprüfung durch drei Filter gelöst. Einer der Filter wird verwendet, um die Anzahl der Verbrauchswerte zu überprüfen. Hier habe ich nur die Verbrauchszeiten des Benutzers gelesen. Wie wird die Akkumulation der Verbrauchszeiten abgeschlossen? Wie wäre es mit? Tatsächlich wird der Beobachtermodus für die Akkumulation verwendet. Insbesondere wenn das Transaktionssystem den Rückruf für den Zahlungserfolg empfängt, veröffentlicht es das "Zahlungserfolgsereignis" über den Spring-Ereignismechanismus, der für die Akkumulation der Anzahl des Verbrauchs und des Abonnements der Sprachübertragung verantwortlich ist Die Person erhält das "Zahlungserfolgsereignis" und führt dann ihre eigene Geschäftslogik aus. Zeichnen Sie ein einfaches Diagramm, um Folgendes zu beschreiben:

Um den Code nicht wie ein Haufen * aussehen zu lassen, habe ich ihn in meiner Arbeit wiederholt verwendet

Die Codestruktur ist ungefähr wie folgt:

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

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

um zusammenzufassen

Der Beobachtermodus entkoppelt den Beobachter und den Beobachter, und die Anwesenheit oder Abwesenheit des Beobachters hat keinen Einfluss auf die vorhandene Logik des Beobachters.

Dekorateur Design Muster

Definition

Der Dekorator wird verwendet, um die ursprüngliche Klasse zu verpacken und die Funktion zu verbessern, während er für den Benutzer transparent ist. Beispielsweise kann der BufferedInputStream in Java den von ihm umschlossenen InputStream verbessern, um eine Pufferfunktion bereitzustellen.

zu verwendende Szenen

Wenn Sie die Funktionen der ursprünglichen Klasse verbessern möchten, aber nicht zu viele Unterklassen hinzufügen möchten, können Sie den Dekorationsmodus verwenden, um den gleichen Effekt zu erzielen.

Erfahrung

Der Autor bewarb das gesamte Unternehmen zuvor für den Zugriff auf das Trace-System, daher stellte ich auch einige Tools zur Verfügung, um das automatische Weben von Trace und die automatische Übertragung von Kontext zu lösen. Wenn Sie interessiert sind, können Sie meinen anderen Blog Jaeger mithilfe einer vorläufigen Untersuchung lesen, um Inter-Thread zu unterstützen Für die Kontextübertragung habe ich die dekorative Klasse TraceRunnableWrapper hinzugefügt, um den Kontext des übergeordneten Threads transparent in den untergeordneten Thread zu übertragen, der für den Benutzer vollständig transparent ist. Der Code lautet wie folgt:

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

um zusammenzufassen

Verwenden Sie den Dekorationsmodus, um die Funktion zu verbessern, da Benutzer nur eine einfache Kombination erstellen müssen, um die ursprüngliche Funktion weiterhin verwenden zu können.

Erscheinungsbild-Design-Modus

Definition

Das Erscheinungsbild soll einen einheitlichen Zugang zur Außenwelt bieten. Zum einen sollen die Details des Systems ausgeblendet und zum anderen die Komplexität der Benutzer verringert werden. Beim DispaterServlet in SpringMvc werden beispielsweise alle Controller über DispaterServlet verfügbar gemacht.

zu verwendende Szenen

Reduzieren Sie die Komplexität der Benutzer und vereinfachen Sie die Kosten für den Clientzugriff.

Erfahrung

Das Unternehmen des Autors bietet ISVs von Drittanbietern einige offene Funktionen, z. B. Geräteverwaltung und -steuerung, einheitliche Zahlung und Funktionen zum Herunterladen von Kontoauszügen. Da sie zu verschiedenen Teams gehören, liegen die bereitgestellten externen Schnittstellen in unterschiedlichen Formen vor. Wenn es mehr gibt, können ISVs dies auch akzeptieren. Wenn es jedoch mehr Schnittstellen gibt, beklagen sich ISVs über die hohen Zugriffskosten. Um dieses Problem zu lösen, haben wir vor der offenen Schnittstelle einen Front-End-Controller GatewayController hinzugefügt, der eigentlich der Prototyp unserer späteren offenen Plattform ist. GatewayController stellt ein Schnittstellen-Gateway.do einheitlich der Außenwelt zur Verfügung. Die Anforderungsparameter und Antwortparameter der externen Schnittstelle werden im GatewayController zur Konvergenz vereinheitlicht. Der GatewayController verwendet auch eine einheitliche Schnittstelle für das Routing zum Back-End-Dienst. Der Vergleich vor und nach der Transformation lautet wie folgt:

Um den Code nicht wie ein Haufen * aussehen zu lassen, habe ich ihn in meiner Arbeit wiederholt verwendet

Wahrscheinlich lautet der Code wie folgt:

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

}

um zusammenzufassen

Der Darstellungsmodus wird verwendet, um einige Details im System abzuschirmen und die Zugriffskosten des Benutzers zu senken. Nehmen Sie als Beispiel GatewayController. Die ISV-Authentifizierung, die Schnittstellenüberprüfung und andere sich wiederholende Aufgaben werden dadurch vereinheitlicht. ISVs müssen nur verschiedene Schnittstellen verbinden Die Konvergenz erfolgt über eine Reihe von Schnittstellenprotokollschnittstellen durch die GatewayController-Schicht.

Ich denke du magst

Origin blog.51cto.com/14570694/2540472
Empfohlen
Rangfolge