淺析責任鏈模式在視頻審核場景中的應用

本文字數:3161

預計閱讀時間:20分鐘

01

設計模式

設計模式的概念出自《Design Patterns - Elements of Reusable Object-Oriented Software》中文名是《設計模式 - 可復用的面向對象軟件元素》,該書是在1994 年由?Erich Gamma、Richard Helm、Ralph Johnson?和?John Vlissides?四人合力完成。書中提出的面向對象的設計模式七大基本準則也是我們在平時編碼時要廣泛遵守的,它們包括:

1、開閉原則(Open Closed Principle,OCP)

對外擴展開放,對內修改關閉。就是說新增需求時盡量通過擴展新類實現,而不是對原有代碼修改增刪。

2、單一職責原則(Single Responsibility Principle, SRP)

每個類、每個方法的職責(目的)都是單一的。不要讓一個類或方法做太多縱向關聯或橫向并列的事,否則會增加維護負擔。

3、里氏代換原則(Liskov Substitution Principle,LSP)

繼承必須確保超類所擁有的性質在子類中仍然成立。就是只要父類出現的地方,都可以用子類替換。

4、依賴倒轉原則(Dependency Inversion Principle,DIP)?

高層模塊不應該依賴低層模塊,二者都應該依賴其抽象。就是盡量使用接口來做標準和規范,降低耦合度。

5、接口隔離原則(Interface Segregation Principle,ISP)

接口的功能盡可能單一。一個接口只做一類行為或事物的標準。

6、合成/聚合復用原則(Composite/Aggregate Reuse Principle,CARP)

類間關系盡量使用聚合、組合來實現,如果不可以的話再使用繼承。目的還是為了降低類間耦合度,類間關系有六種,它們的耦合度大小關系是:泛化>實現>組合>聚合>關聯>依賴,我們在設計架構時,盡量讓類間關系靠近右邊(就是采用耦合度更小的關系)。

7、最少知道原則(Least Knowledge Principle,LKP)/迪米特法則(Law of Demeter,LOD)

一個對象要對其他對象的成員盡可能少的知道。目的就是降低類間耦合度,提高代碼的可復用性。

02

責任鏈模式

責任鏈模式屬于行為型設計模式,英文名稱Chain of Responsibility,其定義:為了避免請求發送者與多個請求處理者耦合在一起,將所有請求的處理者通過前一對象記住其下一個對象的引用而連成一條鏈;當有請求發生時,可將請求沿著這條鏈傳遞,直到有對象處理它為止。

責任鏈模式將對請求的處理過程劃分為多個獨立的處理節點,節點間互相不感知具體計算過程。處理節點存在前后順序處理關系,也可以視具體的業務特點跳過某些節。

03

業務場景應用案例

1、業務背景

一個視頻文件從用戶電腦到發布到網絡上通常要經歷,上傳、轉碼、審核、發布四個階段,本文描述的業務場景適用于視頻的審核與發布階段。不同來源的視頻審核策略不同,不同時期的視頻審核策略不同,不同級別用戶生成的視頻審核策略不同等等。視頻在審核階段需要根據不同的審核策略進行處理。

2、不用責任鏈模式

在業務發展的初期,沒有采用設計模式來指導編寫代碼。針對不同的審核策略處理視頻的業務代碼偽碼如下:

if?(videoInfo.getUploadFrom().equals("狐友") || videoInfo.getUploadFrom().equals("新聞")) {
}else{
}
if?(videoInfo.getUploadFrom().equals("特邀作者上傳") || videoInfo.getUploadFrom().equals("抓取來源")) {
}?else{
}
if?(videoInfo.getUploadFrom().equals("廣告不適") || videoInfo.getUploadFrom().equals("嚴肅")) {
}?else{
}
if?("命中MD5黑名單") {
}?else{
}
if?(videoInfo.getUploadFrom().equals("普通視頻")) {
}?else{
}
if?(videoInfo.getUploadFrom().equals("免審標簽") || videoInfo.getUploadFrom().equals("免審產品") || videoInfo.getUploadFrom().equals("免審用戶")) {
}?else{
}
if?(videoInfo.getUploadFrom().equals("適用圖片檢測")) {
}?else{
}
if?(videoInfo.getUploadFrom().equals("適用音頻檢測")) {
}?else{
}
if?(videoInfo.getUploadFrom().equals("適用文本檢測")) {
}?else{
}
if?(videoInfo.getUploadFrom().equals("適用全部類型AI檢測")) {
}?else{
}
if?(videoInfo.getUploadFrom().equals("適用風險預測")) {
}?else{
}
if?(videoInfo.getUploadFrom().equals("適用基因檢測")) {
}?else{
}
if?(now().between(time1, time2)&& isSensitiveAreaIp()) {
}?else{
}
if?(videoInfo.getUploadFrom().equals("適用產品策略檢測")) {
}?else{
}

代碼使用了大量的if else語句,且如果隨著業務的發展需要增加新的處理邏輯則又要對源碼添加if else語句,違反了開閉原則,使得代碼的可擴展性非常差。如果if else中的邏輯比較復雜,常常會由于增加新的邏輯而引入bug。這種業務代碼特別適合使用責任鏈模型進行重構。

3、應用責任鏈模式

責任鏈模式的本質是將請求與處理解耦,允許將請求沿著處理器鏈進行發送。收到請求后,每個處理器均可對請求進行處理,或將其傳遞給鏈上的下個處理器。當新增處理邏輯時,不會對請求邏輯造成影響,兩者互不干擾。示例圖如下:

4、UML類圖

通過對責任鏈模式的定義,需要定義 處理器接口或父類;具體的處理器類;請求接口或父類;具體的請求類;客戶端類。但在實際的使用過程中,請求接口和請求類往往退化成一個request對象,在處理器鏈路里面傳遞,類圖如下所示:

04

初級實現

我們根據uml類圖,嘗試一下如何應用責任鏈模式對原來的業務代碼進行改造。首先定義一個Handler接口。

public interface Handler {void handle(Object videoInfo);void setNext(Handler handler);
}

然后構建三個具體的處理器實現類,實際工作中要對應原業務代碼實現相應數量的處理器類,這里為了陳述方便,只實現三個處理器類。

1、處理器1:

public class Handler1 implements Handler {
private Handler next;@Overridepublic void handle(Object videoInfo) {System.out.println("handler1處理");if?(next != null) {next.handle(videoInfo);}}@Overridepublic void setNext(Handler handler) {this.next = handler;}
}

2、處理器2:

public class Handler2 implements Handler {
private Handler next;@Overridepublic void handle(Object videoInfo) {System.out.println("handler2處理");if?(next != null) {next.handle(videoInfo);}}@Overridepublic void setNext(Handler handler) {this.next = handler;}
}

3、處理器3:

public class Handler3 implements Handler {
private Handler next;@Overridepublic void handle(Object videoInfo) {System.out.println("handler3處理");if?(next != null) {next.handle(videoInfo);}}@Overridepublic void setNext(Handler handler) {this.next = handler;}
}

4、最后是客戶端代碼,client:

public class Client {
public static void main(String[] args) {
Handler handler1 = new Handler1();
Handler handler2 = new Handler2();
Handler handler3 = new Handler3();handler1.setNext(handler2);handler2.setNext(handler3);handler1.handle("videoInfo");}
}
打印結果為:
handler1處理
handler2處理
handler3處理

上面示例中的實現實際上是責任鏈模式的一個變種,即每個處理器都做出了自己的處理,沒有中途終止的情況。而責任鏈的編排工作全部由客戶端client實現,且每個處理器除了要關注自己的處理器代碼外,還需要持有下一個處理器的引用,以串聯成鏈,并且還負責對下一個處理器的調用。違反了職責單一原則,并不利于系統的維護與擴展,耦合性還是比較高的。

05

進階實現

上面示例的實現,并不利于系統的擴展,后續再增加處理器,仍然需要對client的代碼進行改動,以把新增加的處理器編排入鏈,增加了拼錯或拼少處理器的風險,實際上是clent的設計沒有遵循單一職責的原則,承接了不屬于其的職責導致,我們看看如何進一步的改造,來解決這個問題。可以嘗試增加一個處理器鏈路管理模塊負責承接處理器的編排職責,將處理器的編排職責從client代碼中解放出來,uml類圖如下:

首先仍然需要先定義一個Handler接口:

1、Handler接口如下:

public interface Handler {void handle(Object videoInfo);
}

這次接口只有一個handle方法了,去掉了setNext方法,因為責任鏈的編排交給鏈路管理類了,處理器的職責也變的單一了,只剩下了處理邏輯,十分簡潔。

2、處理器1的實現:

public class Handler1 implements Handler {@Overridepublic void handle(Object videoInfo) {if?(videoInfo.toString().contains("handler1")) {System.out.println("handler1處理");}}
}

這次加入了一個條件用于演示,僅當傳入的request對象包含了handler1字符串時,才觸發處理器1的處理,否則跳過處理器1。

3、處理器2的實現

public class Handler2 implements Handler {
private Handler next;@Overridepublic void handle(Object videoInfo) {System.out.println("handler2處理");}
}

4、處理器3的實現:

public class Handler3 implements Handler {
private Handler next;@Overridepublic void handle(Object videoInfo) {System.out.println("handler3處理");}
}

5、鏈路管理器HandlerChain的實現

public class HandlerChain {
private List<Handler> list = new ArrayList<>();public void add(Handler handler) {this.list.add(handler);}public void handle(Object videoInfo) {list.forEach(handler -> handler.handle(videoInfo));}
}

鏈路管理器負責處理器的編排,在本示例中,只是簡單的按順序調用每一個處理器進行處理,在實際工作中會增加處理器的優先級特性,也可以結合apollo等配置中心實現動態的處理器編排。

6、客戶端client的代碼

public class Client {
public static void main(String[] args) {
Handler handler1 = new Handler1();
Handler handler2 = new Handler2();
Handler handler3 = new Handler3();HandlerChain handlerChain = new HandlerChain();handlerChain.add(handler1);handlerChain.add(handler2);handlerChain.add(handler3);handlerChain.handle("videoInfo");}
}
運行結果為:
handler2處理
handler3處理

在這一個版本的實現中,客戶端不再關心處理器的編排;處理器也不再持有下一個處理器的引用,只關注自身處理器業務代碼的實現;把處理器的編排工作交給了鏈路管理器,隨著業務增長需要新增處理器代碼時,只需要修改鏈路管理器即可。但這似乎還是沒有做到完全的遵守開閉原則,仍然需要修改鏈路管理器的源碼,那么有沒有一種實現可以完全做到遵守開閉原則呢??

06

最終實現

想要完全的遵守開閉原則,即當新增處理器代碼時,無需對已有的系統源碼進行修改,我們需要結合框架來實現。而在實際的工作中是通過結合spring框架的控制反轉與依賴注入特性實現的。首先將處理器的實現類定義為spring的bean,利用控制反轉特性,將處理器交由spring框架管理。

1、示例代碼為:

@Component
public class Handler1 implements Handler {@Overridepublic void handle(Object videoInfo) {if?(videoInfo.toString().contains("handler1")) {System.out.println("handler1處理");}}
}

通過添加一個@Component注解,即可實現控制反轉,將Handler1交由spring管理。其他處理器的實現類似。控制反轉的特性也體現了依賴倒轉原則,這里不再進一步討論了。

然后利用依賴注入特性,將所有的控制器注入到鏈路管理器中。

2、示例代碼為:

@Component
public class HandlerChain {
@Autowired
private List<Handler> list;public void handle(Object videoInfo) {list.forEach(handler -> handler.handle(videoInfo));}
}

可以看到鏈路管理器代碼新增了@Component注解將其自身交由spring管理,然后在list屬性上新增了@Autowired注解,spring會把其管理的所有實現了Handler接口的bean自動注入到該list對象中,這里遵循了里氏代換原則。當新增一個handler實現時,只要其實現了Handler接口,該處理器就會在spring啟動時自動注入到鏈路管理器的list屬性中,從而實現了完全遵守開閉原則,即當新增處理器邏輯時,無需對系統原有代碼進行修改,只需要擴展新的處理器代碼即可。

而客戶端client的實現也會更加精簡:

3、示例代碼為:

@Component
public class Client {
@Autowired
private HandlerChain handlerChain;public void handle(Object videoInfo) {handlerChain.handle(videoInfo);}
}

同樣的client也將自身交由spring管理,利用spring的控制反轉與依賴注入特性實現了對鏈路管理器的調用。?

07

總結

通過使用責任鏈模式并遵循開閉原則、單一職責等設計原則對視頻審核與發布業務進行了重構,當業務新增處理器時,無需對原有的系統源碼進行修改,只需要關注新增的處理器本身即可,極大的增加了系統的靈活性與擴展性,也提高了系統的可維護性,減少了對復雜業務編排導致出現bug的幾率。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/91652.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/91652.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/91652.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

洛谷 P3372 【模板】線段樹 1-普及+/提高

題目描述 如題&#xff0c;已知一個數列 {ai}\{a_i\}{ai?}&#xff0c;你需要進行下面兩種操作&#xff1a; 將某區間每一個數加上 kkk。求出某區間每一個數的和。 輸入格式 第一行包含兩個整數 n,mn, mn,m&#xff0c;分別表示該數列數字的個數和操作的總個數。 第二行包含 n…

flink寫paimon表的過程解析

背景 apache paimon是構建湖倉一體的重要組成部分&#xff0c;由于paimon的寫入速度很快&#xff0c;通過flink進行數據寫入是很自然的選擇&#xff0c;本文就介紹下使用flink寫入paimon的兩階段協議的大概邏輯 技術實現 flink通過兩階段協議寫入paimon表&#xff0c;分成三個步…

迅為RK3568開發板OpeHarmony學習開發手冊-點亮 HDMI 屏幕

OpenHarmony 源碼中默認支持 HDMI 屏幕&#xff0c;但是默認的分辨率是采用 mipi 的分辨率&#xff0c;我們修改代碼&#xff0c;關閉 MIPI 就可以正常顯示了。在之前視頻修改的基礎上&#xff0c;修改/home/topeet/OH4.1/OpenHarmony-v4.1-Release/OpenHarmony/out/kernel/src…

北京理工大學醫工交叉教學實踐分享(1)|如何以實踐破解數據挖掘教學痛點

如何有效提升醫工交叉領域數據挖掘課程的教學效果&#xff1f;近日&#xff0c;北京理工大學醫學技術學院辛怡副教授在和鯨組織的分享會上&#xff0c;系統介紹了其團隊在《數據挖掘在生物醫學中的應用》課程中的創新實踐&#xff0c;為解決普遍教學痛點提供了可借鑒的“平臺化…

Vue 3 入門教程 8 - 路由管理 Vue Router

一、Vue Router 簡介Vue Router 是 Vue.js 官方的路由管理器&#xff0c;它與 Vue.js 核心深度集成&#xff0c;用于構建單頁面應用&#xff08;SPA&#xff09;。單頁面應用是指整個應用只有一個 HTML 頁面&#xff0c;通過動態切換頁面內容來模擬多頁面跳轉的效果&#xff0c…

django的數據庫原生操作sql

from django.db import connection from django.db import transaction from django.db.utils import (IntegrityError,OperationalError,ProgrammingError,DataError ) from django.utils import timezoneclass Db(object):"""數據庫操作工具類&#xff0c;封裝…

FreeRTOS---基礎知識2

1. FreeRTOS 調度機制概述FreeRTOS 是一個實時操作系統&#xff08;RTOS&#xff09;&#xff0c;其核心功能是通過 調度器&#xff08;Scheduler&#xff09; 管理多個任務的執行。調度機制決定了 何時切換任務 以及 如何選擇下一個運行的任務&#xff0c;以滿足實時性、優先級…

Docker安裝(精簡述版)

1. 安裝&#xff1a;Docker 環境&#xff08;Docker desktop&#xff09; #Windows架構版本查看&#xff0c;Win R? 輸入 ?cmd? 打開命令提示符&#xff1b;輸入命令?&#xff1a; bash echo %PROCESSOR_ARCHITECTURE%#安裝Docker desktop&#xff08;安裝時勾選 WSL2&am…

Postman-win64-7.3.5-Setup.exe安裝教程(附詳細步驟+桌面快捷方式設置)?

Postman 是一款超常用的接口調試工具&#xff0c;程序員和測試人員用它來發送網絡請求、測試API接口、調試數據交互? 1. 雙擊安裝包? 安裝包下載地址&#xff1a;https://pan.quark.cn/s/4b2960d60ae9&#xff0c;找到你下的 Postman-win64-7.3.5-Setup.exe 文件&#xff08…

149. Java Lambda 表達式 - Lambda 表達式的序列化

文章目錄149. Java Lambda 表達式 - Lambda 表達式的序列化為什么要序列化 Lambda 表達式&#xff1f;Lambda 表達式的序列化規則示例代碼&#xff1a;序列化 Lambda 表達式代碼解析&#xff1a;Lambda 序列化的限制總結&#xff1a;149. Java Lambda 表達式 - Lambda 表達式的…

頤頓機電攜手觀遠BI數據:以數據驅動決策,領跑先進制造智能化升級

頤頓機電簽約觀遠數據&#xff0c;聚焦財務分析、銷售管理等場景&#xff0c;以 BI 數據解決方案推進數據驅動決策&#xff0c;助力先進制造企業提效與競爭力升級。一、合作官宣&#xff1a;頤頓機電 觀遠數據&#xff0c;開啟數據應用新征程浙江頤頓機電有限公司&#xff08;…

【PHP】幾種免費的通過IP獲取IP所在地理位置的接口(部分免費部分收費)

目錄 一、獲取客戶端IP地址 二、獲取IP所在地理位置接口 1、IP域名歸屬地查詢 2、騰訊地圖 - IP定位 3、聚合數據 - IP地址&#xff08;推薦&#xff09; 4、高德地圖 - IP定位&#xff08;推薦&#xff09; 5、360分享計劃 - IP查詢 6、天聚ip地址查詢 7、百度IP地址…

【Excel】制作雙重餅圖

一、效果話不多說&#xff0c;直接上數據和效果圖&#xff01;&#xff08;示例軟件&#xff1a;WPS Office&#xff09;類別現金刷卡小計蘋果10.005.0015.00荔枝20.0015.0035.00西瓜30.0025.0055.00總計60.0045.00105.00二、步驟&#xff08;一&#xff09;制作底圖插入餅圖&a…

gcc-arm-none-eabi安裝后,找不到libgcc.a的拉置

位置在&#xff1a;/usr/lib/gcc/arm-none-eabi/6.3.1/libgcc.a查找方法&#xff1a;arm-none-eabi-gcc --print-libgcc-file-name以前沒找到&#xff0c;是因為進錯目錄&#xff1a;/usr/lib/arm-none-eabi/lib

上證50期權2400是什么意思?

本文主要介紹上證50期權2400是什么意思&#xff1f;“上證50期權2400”通常指上證50ETF期權的某個具體合約代碼&#xff0c;其中“2400”是合約代碼的一部分&#xff0c;需結合完整代碼格式理解其含義。上證50期權2400是什么意思&#xff1f;一、上證50期權合約代碼的組成上證5…

發那科機器人P點位置號碼自動變更功能為禁用狀態

通過改變變量的狀態&#xff0c;發那科機器人可以實現&#xff0c;當在程序中進行記錄、修改、插入、刪除、復制/粘貼包含有P點位置號碼的行時&#xff0c;P點位置號碼會自動從小到大自動排列&#xff0c;可以實現自動排列&#xff0c;或者點擊編輯變更編號也可以下圖所示女變量…

什么叫湖倉一體

文章目錄概念一、理解湖倉一體&#xff1a;先搞懂“數據湖”和“數據倉庫”1. 數據倉庫&#xff08;Data Warehouse&#xff09;2. 數據湖&#xff08;Data Lake&#xff09;3. 傳統架構的痛點&#xff1a;“湖”與“倉”的割裂二、湖倉一體的核心特點&#xff1a;融合“湖”與…

網絡安全突發事件應急預案方案

最近有要求需要出一個網絡安全突發事件應急預案方案&#xff0c;本文僅就應急預案問題提出一點初步思考&#xff0c;意在拋磚引玉&#xff0c;盼各位讀者不吝賜教&#xff0c;共同完善對這一領域的認識。一、總則 &#xff08;一&#xff09;目的 為有效應對規劃建筑設計院企業…

【基于3D Gaussian Splatting的三維重建】保姆級教程 | 環境安裝 | 制作-訓練-測試自己數據集 | torch | colmap | ffmpeg | 全過程圖文by.Akaxi

目錄 一.【3DGS環境配置】 1.1 克隆3DGS倉庫 1.2 安裝Visual Studio 2022 1.2.1 下載Visual Studio 2022 1.2.2 更改環境變量 1.3 創建環境 1.3.1 創建python環境 1.3.2 離線安裝torch包 1.3.3 安裝依賴包 1.3.4安裝子模塊 &#xff08;1&#xff09;報錯解決&…

C#泛型委托講解

1. 泛型&#xff08;Generics&#xff09; 泛型允許編寫類型安全且可重用的代碼&#xff0c;避免裝箱拆箱操作&#xff0c;提高性能。 泛型類 // 定義泛型類 public class GenericList<T> {private T[] items;private int count;public GenericList(int capacity){items …