1 はじめに
えー、入社当初に勉強ノートを読み返してみたら、ビジネスコードに慣れていた頃にデザインパターンの責任連鎖モデルを勉強して整理していたことが分かりました。サンプルとソースコードを組み合わせて深く探求するのではなく、コードに慣れる機会を利用して体系的に学習し、次のようにドキュメントを整理します。
2. 責任連鎖パターンとは何ですか?
「ルールのないルールはない」と言われるように、仕事でも生活でも、決められた手順を踏まなければならないことは多く、その手順は連動していることが多く、前の手順が完了すると、次の手順に引き継がれます。実行のためのステップ。例えば、私たちが料理をするとき、野菜を買って、野菜を洗い、野菜を切って、野菜を炒めて、お皿に盛り付けるという作業は、前の工程が終わってから初めて次の工程に進み、ようやく準備が整います。他の例としては、昇進推薦の場合、まず報告会のための報告書を作成し、審査チームが採点し、審査通過後、プロジェクトチームリーダーに引き渡して承認を得るという流れです。報告書と審査チームの得点をもとに、プロジェクトチームリーダーが昇進の可否を決定し、プロジェクトチームリーダーが同意した後、最終的に部門リーダーに承認を求め、最終結果を出します。このような段階的なプロセスは、責任連鎖モデルを通じて実現できます。
- はじめに:責任連鎖モデルは、その名前が示すように、異なる責任のステップを連続して実行することであり、次のステップは 1 つのステップの実行が完了した後にのみ実行できます。名前からわかるように、責任連鎖パターンは通常、リンク リストを使用して実装されます。したがって、タスクの実行要求が開始されると、その要求は責任の連鎖の最初のステップから最後のステップが完了するまで引き継がれます。責任連鎖モードでは、クライアントはプロセス開始リクエストを一度実行するだけでよく、プロセスの実行に参加する必要がなく、責任連鎖上のプロセスは継続的に実行できます。実行プロセスの詳細をプロセスから切り離すため。
- モード構造: 責任連鎖モードの主な役割は次のとおりです。
◦ 抽象プロセッサ (ハンドラ) : プロセッサ抽象インターフェイスは、リクエストの処理方法と次のステップを実行するプロセッサを定義します。
◦ コンクリートハンドラー (ConcreteHandler) : 実行リクエストの特定の実装。最初にリクエストに従って処理ロジックを実行し、次にリクエストを次のプロセッサに渡して実行します。
◦ 呼び出し元: 呼び出し元はプロセッサを作成し、処理のためにリクエストをプロセッサに渡します。
- 関連するコード:
// 抽象处理器
public abstract class Handler {
private Handler next;
public Handler getNext() {
return next;
}
public void setNext(Handler next) {
this.next = next;
}
public abstract void handle(Object request);
}
// 具体处理器 1
public class ConcreteHandler1 extends Handler {
@Override
public void handle(Object request) {
System.out.println("concrete handler 1 execute request. request: " + request);
if (getNext() != null) {
getNext().handle(request);
}
}
}
// 具体处理器 2
public class ConcreteHandler2 extends Handler {
@Override
public void handle(Object request) {
System.out.println("concrete handler 2 execute request. request: " + request);
if (getNext() != null){
getNext().handle(request);
}
}
}
// 具体处理器 3
public class ConcreteHandler3 extends Handler {
@Override
public void handle(Object request) {
System.out.println("concrete handler 3 execute request. request: " + request);
if (getNext() != null) {
getNext().handle(request);
}
}
}
public static void main(String[] args) {
Handler concreteHandler1 = new ConcreteHandler1();
Handler concreteHandler2 = new ConcreteHandler2();
Handler concreteHandler3 = new ConcreteHandler3();
concreteHandler1.setNext(concreteHandler2);
concreteHandler2.setNext(concreteHandler3);
concreteHandler1.handle("my request.");
}
上記のコードから、責任連鎖パターンは実際には非常に単純であることがわかりますが、注意すべき点がいくつかあります。
- まず最初に、責任チェーン全体を初期化する必要があります。つまり、各プロセッサの次のプロセッサを設定します。
- 特定のプロセッサが処理された後は、次のプロセッサのハンドルメソッドを手動で呼び出して次の処理を実行する必要がありますが、実際にはテンプレート メソッド モードを使用して最適化することもできます。
コンソール出力は次のとおりです。
concrete handler 1 execute request. request: my request.
concrete handler 2 execute request. request: my request.
concrete handler 3 execute request. request: my request.
3. 具体的なデモ例
毎日の休暇を例に考えてみましょう。休暇申請は、まず直属のリーダーが承認し、承認後に部門リーダーに承認を求め、部門リーダーの承認後、最終的に人事部に報告され、休暇日数が記録されます。従来の企業では、休暇申請書を手書きで書き、次に直属のリーダーのオフィスに行き、直属のリーダーに署名してもらい、次に部門のリーダーのオフィスに行って署名してもらい、最後に部門のリーダーのオフィスに行く必要があります。人事部門は休暇申請書を提出することを要求します。これは休暇申請書を発行することに相当します。3回の申請を経て初めて、休暇申請の全プロセスが完了します。
しかし、さまざまな最新の OA システムの管理下では、休暇プロセス全体が簡素化されています。休暇申請を一度開始するだけで済み、その後、休暇申請は承認者間で自動的に回覧されます。現時点では、私たちの責任連鎖パターンが登場します。便利で。コードは以下のように表示されます:
// 请假抽象处理器
public abstract class DayOffHandler {
private DayOffHandler next;
public DayOffHandler getNext() {
return next;
}
public void setNext(DayOffHandler next) {
this.next = next;
}
public abstract void handle(String request);
}
// 直属 leader 处理
public class GroupLeaderHandler extends DayOffHandler {
@Override
public void handle(String request) {
System.out.println("直属 leader 审查: " + request);
System.out.println("同意请求");
if (getNext() != null) {
getNext().handle(request);
}
}
}
// 部门 leader 处理
public class DepartmentLeaderHandler extends DayOffHandler{
@Override
public void handle(String request) {
System.out.println("部门 leader 审查: " + request);
System.out.println("同意请求");
if (getNext() != null) {
getNext().handle(request);
}
}
}
// 人事处处理
public class HRHandler extends DayOffHandler {
@Override
public void handle(String request) {
System.out.println("人事处审查: " + request);
System.out.println("同意请求,记录请假");
if (getNext() != null) {
getNext().handle(request);
}
}
}
上記のコードは、leave 抽象処理クラスと 3 つの特定のハンドラーを定義しています。次の図に示すように、これら 3 つのハンドラーのプロセス初期化を直列に接続し、ステップごとに実行する必要があります。以下に続きます:
public static void main(String[] args) {
DayOffHandler groupLeaderHandler = new GroupLeaderHandler();
DayOffHandler departmentLeaderHandler = new DepartmentLeaderHandler();
DayOffHandler hrHandler = new HRHandler();
groupLeaderHandler.setNext(departmentLeaderHandler);
departmentLeaderHandler.setNext(hrHandler);
System.out.println("收到面试通知,需要请假");
String request = "家中有事,请假半天,望批准";
System.out.println("发起请求:");
groupLeaderHandler.handle(request);
}
クライアント コードからわかるように、最初に 3 つの特定のハンドラーをインスタンス化し、次にsetNextメソッドを通じてそれらを直列に接続します。必要なのは、直属のリーダーへの休暇リクエストを開始することだけであり、その後、承認プロセス全体を自動化できます。導入により、いちいち役所に出向いて申請する必要はありません。実行後の結果は次のようになります。
收到面试通知,需要请假
发起请求:
直属 leader 审查: 家中有事,请假半天,望批准
同意请求
部门 leader 审查: 家中有事,请假半天,望批准
同意请求
人事处审查: 家中有事,请假半天,望批准
同意请求,记录请假
4. ソースコードにおける責任連鎖モデルの適用
責任連鎖モードに関して言えば、最も有名なものはもちろんServletのフィルターです。インターセプタとフィルターのこのシステムでは、責任連鎖モードは各リクエストを順番に処理するために使用されます。フィルターの使い方。フィルター インターフェイスは次のとおりです。
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
default void destroy() {
}
}
- FilterChainは Filter の責任の連鎖であり、そのコードは次のとおりです。
public interface FilterChain {
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
public final class ApplicationFilterChain implements FilterChain {
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (Globals.IS_SECURITY_ENABLED) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
public Void run() throws ServletException, IOException {
ApplicationFilterChain.this.internalDoFilter(req, res);
return null;
}
});
} catch (PrivilegedActionException var7) {
Exception e = var7.getException();
if (e instanceof ServletException) {
throw (ServletException)e;
}
if (e instanceof IOException) {
throw (IOException)e;
}
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
}
throw new ServletException(e.getMessage(), e);
}
} else {
this.internalDoFilter(request, response);
}
}
private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (this.pos < this.n) {
ApplicationFilterConfig filterConfig = this.filters[this.pos++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);
}
if (Globals.IS_SECURITY_ENABLED) {
Principal principal = ((HttpServletRequest)request).getUserPrincipal();
Object[] args = new Object[]{request, response, this};
SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (ServletException | RuntimeException | IOException var15) {
throw var15;
} catch (Throwable var16) {
Throwable e = ExceptionUtils.unwrapInvocationTargetException(var16);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
} else {
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
if (request.isAsyncSupported() && !this.servletSupportsAsync) {
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);
}
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse && Globals.IS_SECURITY_ENABLED) {
Principal principal = ((HttpServletRequest)request).getUserPrincipal();
Object[] args = new Object[]{request, response};
SecurityUtil.doAsPrivilege("service", this.servlet, classTypeUsedInService, args, principal);
} else {
this.servlet.service(request, response);
}
} catch (ServletException | RuntimeException | IOException var17) {
throw var17;
} catch (Throwable var18) {
Throwable e = ExceptionUtils.unwrapInvocationTargetException(var18);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set((Object)null);
lastServicedResponse.set((Object)null);
}
}
}
}
}
- InternalDoFilter()メソッドでは、 FilterChain全体が配列フィルターを使用して各フィルターとその構成を保存し、posを使用して現在どのフィルターを通過しているかを記録し、取得したFilterのdoFilterメソッドを実行していることがわかります。前述のリンク リスト方式とは異なり、ここでのリンクは配列に格納されます。
- 春の責任連鎖パターン
SpringMVCのインターセプターもChain of Responsibility パターンを使用します。まず、Interceptorの抽象処理クラスを見てみましょう。
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
抽象処理クラスには、前処理、後処理、全処理終了後の処理の3つのメソッドが定義されています。インターセプタはHandlerExecutionChainを介して直列に接続されています。 HandlerExecutionChainでは、 applyPreHandle、applyPostHandle、およびtriggerAfterCompletionの 3 つのメソッドに注意する必要があります。 これら 3 つのメソッドは、 interceptor で定義されたpreHandle、postHandle 、およびafterCompletionメソッドをそれぞれ実行します。また、前のフィルターと同様に、すべてのインターセプターがインターセプター配列に格納されており、対応するメソッドが3 つのメソッドのインターセプター配列を走査することによって順番に実行されることもコードからわかります。
public class HandlerExecutionChain {
@Nullable
private HandlerInterceptor[] interceptors;
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
}
return true;
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for(int i = interceptors.length - 1; i >= 0; --i) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for(int i = this.interceptorIndex; i >= 0; --i) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
} catch (Throwable var8) {
logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
}
}
}
}
}
5. まとめ
責任チェーンモードも共通の設計モードであり、責任の異なるプロセッサを直列に接続し、各プロセッサの処理方法を1回のリクエストで実行することができます。この方法でリクエストの送信者はリクエストを送信するだけでよく、詳細なリンク構造を知る必要はありませんが、リクエストの受信者は自分の処理ロジックのみを気にし、処理後に次の受信者にリクエストを渡します。が完了すると、独自のタスクが完了し、リクエストの送信側と受信側の分離が実現します。ソース コード分析から、責任連鎖モードではリンク リスト構造が一般的に使用されますが、配列やリストを使用しても要件を満たすことができることがわかります。
著者: Jingdong Technology Song Huichao
出典: JD Cloud 開発者コミュニティ
インド国防省が自社開発した Maya OS は、Windows Redis 7.2.0 を完全に置き換えるもので、最も広範囲にわたるバージョンの 7-Zip 公式 Web サイトが、Baidu によって悪意のある Web サイトであると特定されました 。 Xiaomi がCyberDog 2をリリース、オープンソース率80%以上 ChatGPTの1日コスト約70万ドル、OpenAIが破産寸前の可能性 瞑想ソフトが上場へ、「中国初のLinux人」が設立 Apache Doris 2.0.0版正式リリース: ブラインド テストのパフォーマンスが 10 倍向上、より統合され多様な超高速分析エクスペリエンス Linux カーネル (v0.01) のオープン ソース コード解釈の最初のバージョン Chrome 116 が正式リリース