設計模式七:責任鏈模式

文章目錄

      • 1、責任鏈模式
      • 2、spring中的責任鏈模式
        • Spring Interceptor
        • Servlet Filter
        • Netty

1、責任鏈模式

責任鏈模式為請求創建了一個接收者對象的鏈,在這種模式下,通常每個節點都包含對另一個節點者的引用。每個節點針對請求,處理自己感興趣的內容,處理完之后可以結束,也可以向下一個節點傳遞繼續處理;

角色

  1. 抽象處理者角色:處理請求的抽象類,定義了處理請求的抽象方法;(抽象類或接口實現);
  2. 具體處理者角色:處理請求的具體實現類;(持有下家對象的引用);

例:請假流程都是先由本部門審核,根據時間長短再進行下一級審批

在這里插入圖片描述

//抽象類
public abstract class Handler {/*** 請假天數*/public int maxday;/*** 請假人*/public String name;public Handler(String name, int maxday) {this.maxday = maxday;this.name = name;}private Handler nextHandler;public void setNextHandler(Handler nextHandler) {this.nextHandler = nextHandler;}/*** 處理請假:判斷請假天數,超過本部門限定時間則交由上一級部門*/public final void handlingFakeSlips(int day) {if (this.maxday >= day) {this.agree(day);}else {if (nextHandler != null) {System.out.println(name+":天數已超過我的審批權限,已提交我的上級審批");nextHandler.handlingFakeSlips(day);}else {System.out.println("天數時間過長,準備辭職吧!!!");}}}/*** 審批動作:子類來實現* @param day*/abstract void agree(int day);
}//部門實現類
public class RDDepartment extends Handler{public RDDepartment(String name, int maxday) {super(name, maxday);}@Overridevoid agree(int maxday) {System.out.println(name + ":研發部門請假審批通過,請假天數:" + maxday);}
}//主管實現類
public class Supervisor extends Handler{public Supervisor(String name, int maxday) {super(name, maxday);}@Overridevoid agree(int maxday) {System.out.println(name + ":主管請假審批通過,請假天數:" + maxday);}
}//董事實現類
public class Director extends Handler{public Director(String name, int maxday) {super(name, maxday);}@Overridevoid agree(int maxday) {System.out.println(name + ":請假董事審批通過,請假天數:" + maxday);}
}//組裝鏈
public class HandlerChain {private Handler head;private Handler tail;public HandlerContext(){RDDepartment rdDepartment = new RDDepartment("研發部門",5);Supervisor supervisor = new Supervisor("項目主管",30);Director director = new Director("董事",180);rdDepartment.setNextHandler(supervisor);supervisor.setNextHandler(director);head = rdDepartment;tail = director;}public void doHandler(Integer days){head.handlingFakeSlips(days);}
}//請求
public class Request {public static void main(String[] args) {HandlerChain handlerChain = new HandlerChain();handlerChain.doHandler(360);}
}

優點(when,why):

? 1.發送者與接收者之間的耦合度降低(解耦)

? 2.可以靈活添加新的責任鏈中的對象

缺點:

? 1.不能保證請求一定被接收

? 2.一定程度上影響性能

這種形式很難進行動態新增和調整處理節點,一種比較復雜的控制節點的形式如Netty中的責任鏈模式應用,見下一節

2、spring中的責任鏈模式

Spring Interceptor

回顧springmvc處理請求的流程:DispatcherServlet接收到請求后,執行doDispatcher()方法,流程回顧請求處理流程圖

其中通過HandlerMapping獲得的是HandlerExecutionChain 對象

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {//......HandlerExecutionChain mappedHandler = null;//......mappedHandler = getHandler(processedRequest);//......     HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//......
}protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;
}protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) {if (adapter.supports(handler)) {return adapter;}}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

HandlerExecutionChain 中包含一個handler對象(后面匹配能處理handler的適配器對象執行,詳情對應適配器模式中講解),還有一個攔截器列表List<HandlerInterceptor> interceptorList,所有的實現了HandlerInterceptor接口的類都會被加載進這個集合中,在請求處理前后分別以責任鏈的形式調用攔截器的preHandlepostHandle

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = 0; i < interceptors.length; i++) {HandlerInterceptor interceptor = interceptors[i];if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}}return true;
}void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {HandlerInterceptor[] interceptors = 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);}}
}

這里的鏈是由集合List維護,使用List有序的特性一次調用每個攔截器,通過方法返回的結果判斷是否需要傳遞到下一個攔截器

Servlet Filter

servletFilter的調用也是通過責任鏈模式,通過FilterChain作為鏈條的管理者

//FilterChain接口
public interface FilterChain {public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
}//FilterChain實現類
public class MockFilterChain implements FilterChain {//......private final List<Filter> filters;//......@Overridepublic void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {Assert.notNull(request, "Request must not be null");Assert.notNull(response, "Response must not be null");Assert.state(this.request == null, "This FilterChain has already been called!");if (this.iterator == null) {this.iterator = this.filters.iterator();}if (this.iterator.hasNext()) {Filter nextFilter = this.iterator.next();nextFilter.doFilter(request, response, this);}this.request = request;this.response = response;}
}

由上可知,FilterChain中管理的 List<Filter> filters即為所有實現了Filter的過濾器,調用過濾器的時候,通過FilterChain進行鏈條的調用。


//Filter接口
public interface Filter {default public void init(FilterConfig filterConfig) throws ServletException {}public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain)throws IOException, ServletException;default public void destroy() {}
}//Filter實現類
public class AuthFilter extends AuthenticationFilter {//......public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest httpRequest = toLowerCase((HttpServletRequest)request);String tokenString = httpRequest.getParameter("delegation");if (tokenString != null) {filterChain.doFilter(httpRequest, response);} else {super.doFilter(httpRequest, response, filterChain);}}//......
}

FilterdoFilter方法最后調用filterChain.doFilter(httpRequest, response);即傳遞至下一個Filter進行處理

Netty

Netty中的handler使用了責任鏈模式,但是其中回調過多,責任鏈模式的體現不清晰,參考該文章**Spring中如何使用責任鏈模式**,將責任鏈抽離出來,完成在spring中的調用


在這里插入圖片描述

該模型中,具有多條鏈,每條鏈屬于不同層級,鏈中節點為HandlerContextHandlerContext包含相鄰接點的引用,還有Handler的引用

Pipeline:為鏈條的管理者,通過Pipeline來調用責任鏈

HandlerContext:為鏈條中節點的上下文,它里面有鏈條的前一個節點和后一個節點的HandlerContext引用

Handler: 具體的處理程序,與HandlerContext一一對應


我們先僅看Filter事件這一條鏈,整個結構由Pipeline管理整條鏈

在這里插入圖片描述

//Pipelie接口
public interface Pipeline {Pipeline fireTaskFiltered();
}//Pipeline實現類
Component("pipeline")
@Scope("prototype")
public class DefaultPipeline implements Pipeline, ApplicationContextAware, InitializingBean {// 創建一個默認的handler,將其注入到首尾兩個節點的HandlerContext中,其作用只是將鏈往下傳遞private static final Handler DEFAULT_HANDLER = new Handler() {};// 將ApplicationContext注入進來的主要原因在于,HandlerContext是prototype類型的,因而需要// 通過ApplicationContext.getBean()方法來獲取其實例private ApplicationContext context;// 創建一個頭結點和尾節點,這兩個節點內部沒有做任何處理,只是默認的將每一層級的鏈往下傳遞,// 這里頭結點和尾節點的主要作用就是用于標志整個鏈的首尾,所有的業務節點都在這兩個節點中間private HandlerContext head;private HandlerContext tail;// 用于業務調用的request對象,其內部封裝了業務數據private Request request;// 用于執行任務的task對象private Task task;// 最初始的業務數據需要通過構造函數傳入,因為這是驅動整個pipeline所需要的數據,// 一般通過外部調用方的參數進行封裝即可public DefaultPipeline(Request request) {this.request = request;}// 這里我們可以看到,每一層級的調用都是通過HandlerContext.invokeXXX(head)的方式進行的,// 也就是說我們每一層級鏈的入口都是從頭結點開始的,當然在某些情況下,我們也需要從尾節點開始鏈// 的調用,這個時候傳入tail即可。// 觸發任務過濾的鏈調用@Overridepublic Pipeline fireTaskFiltered() {HandlerContext.invokeTaskFiltered(head, task);return this;}// 用于往Pipeline中添加節點的方法,讀者朋友也可以實現其他的方法用于進行鏈的維護void addLast(Handler handler) {HandlerContext handlerContext = newContext(handler);tail.prev.next = handlerContext;handlerContext.prev = tail.prev;handlerContext.next = tail;tail.prev = handlerContext;}// 這里通過實現InitializingBean接口來達到初始化Pipeline的目的,可以看到,這里初始的時候// 我們通過ApplicationContext實例化了兩個HandlerContext對象,然后將head.next指向tail節點,// 將tail.prev指向head節點。也就是說,初始時,整個鏈只有頭結點和尾節點。@Overridepublic void afterPropertiesSet() throws Exception {head = newContext(DEFAULT_HANDLER);tail = newContext(DEFAULT_HANDLER);head.next = tail;tail.prev = head;}// 使用默認的Handler初始化一個HandlerContextprivate HandlerContext newContext(Handler handler) {HandlerContext context = this.context.getBean(HandlerContext.class);context.handler = handler;return context;}// 注入ApplicationContext對象@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {this.context = applicationContext;}
}

Pipeline的實現類內部除了實現接口的方法,其他方法均為初始化

  • DEFAULT_HANDLER: Pipeline管理一條鏈表,該鏈表的每個節點包含HandlerContextHandler兩個對象, 而鏈表的首和尾兩個節點由Pipeline自己指定(其他自定義的節點放在首尾兩節點之間),DEFAULT_HANDLER用來作為首尾節點的Handler,不起任何作用
  • context: 由于這些類的作用域均不是單例,所以要使用ApplicationContext.getBean()方法獲取,所以類實現了ApplicationContextAware接口的setApplicationContext方法,用于注入ApplicationContext對象
  • private HandlerContext head, tail: 為一條鏈的首尾兩個節點,從這兒也可以看出,鏈條的每個節點都是通過HandlerContext來引用的,HandlerContext再引用一個Handler

@Component
@Scope("prototype")
public class HandlerContext {HandlerContext prev;HandlerContext next;Handler handler;private Task task;public void fireTaskFiltered(Task task) {invokeTaskFiltered(next(), task);}/*** 處理任務過濾事件*/static void invokeTaskFiltered(HandlerContext ctx, Task task) {if (null != ctx) {try {ctx.handler().filterTask(ctx, task);} catch (Throwable e) {ctx.handler().exceptionCaught(ctx, e);}}}private HandlerContext next() {return next;}private Handler handler() {return handler;}
}

HandlerContext作為節點,應有前后兩個節點的引用pre next,還有具體處理任務的Handler

fireTaskFiltered方法供Hanndler調用,將請求傳遞給下一個節點處理(方法實現中區下一個HandlerContext去執行)

invokeTaskFiltered靜態方法供Pipeline 和 上一個節點的fireTaskFiltered方法調用,去執行Handler的方法


public interface Handler {/*** 查詢到task之后,進行task過濾的邏輯*/default void filterTask(HandlerContext ctx, Task task) {ctx.fireTaskFiltered(task);}
}

Handler定義了感興趣的事件(暫時只看過濾事件)

Handler的實現類由我們根據自己的需要去編寫,實現Handler接口即可

@Component
public class DurationHandler implements Handler {@Overridepublic void filterTask(HandlerContext ctx, Task task) {System.out.println("時效性檢驗");ctx.fireTaskFiltered(task);}
}@Component
public class RiskHandler implements Handler {@Overridepublic void filterTask(HandlerContext ctx, Task task) {System.out.println("風控攔截");ctx.fireTaskFiltered(task);}
}@Component
public class TimesHandler implements Handler {@Overridepublic void filterTask(HandlerContext ctx, Task task) {System.out.println("次數限制檢驗");ctx.fireTaskFiltered(task);}
}

這里我們已經實現了PipelineHandlerContextHandler,知道這些bean都是被Spring所管理的bean,那么我們接下來的問題主要在于如何進行整個鏈的組裝。這里的組裝方式比較簡單,其主要需要解決兩個問題:

  • 對于后續寫業務代碼的人而言,其只需要實現一個Handler接口即可,而無需處理與鏈相關的所有邏輯,因而我們需要獲取到所有實現了Handler接口的bean;
  • 將實現了Handler接口的bean通過HandlerContext進行封裝,然后將其添加到Pipeline中。

以上可以由spring完成,通過生命實現接口BeanPostProcessor的類,實現postProcessAfterInitialization方法,可以在初始化完Pipeline后,獲取所有實現了Handlerbean,并添加到pipeline中,spring自動調用該方法

@Component
public class HandlerBeanProcessor implements BeanPostProcessor, ApplicationContextAware {private ApplicationContext context;// 該方法會在一個bean初始化完成后調用,這里主要是在Pipeline初始化完成之后獲取所有實現了// Handler接口的bean,然后通過調用Pipeline.addLast()方法將其添加到pipeline中@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {if (bean instanceof DefaultPipeline) {DefaultPipeline pipeline = (DefaultPipeline) bean;Map<String, Handler> handlerMap = context.getBeansOfType(Handler.class);handlerMap.forEach((name, handler) -> pipeline.addLast(handler));}return bean;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {this.context = applicationContext;}
}
  • postProcessAfterInitialization(Object bean, String beanName): 實現了BeanPostProcessor的該方法,在spring啟動之后沒初始化完成一個Bean之后,都會調用該方法

如此,當初始化完pipeline之后,獲取實現了Handler接口的所有實現類,在addLast()方法中為每一個Handler初始化一個HandlerContext,并添加到Pipeline


如此下來,整個過程調用如下

@Service
public class ApplicationService {@Autowiredprivate ApplicationContext context;public void mockedClient(Request request) {Pipeline pipeline = newPipeline(request);pipeline.fireTaskFiltered();}private Pipeline newPipeline(Request request) {return context.getBean(DefaultPipeline.class, request);}
}

1、spring項目啟動,加載和初始化Bean,當加載到DefaultPipeline的時候,由于實現了InitializingBean接口,所以會調用初始化方法afterPropertiesSet(),為DefaultPipeline添加兩個首尾節點HandlerContext

2、當初始化完成Pipeline之后,調用postProcessAfterInitialization(Object bean, String beanName);

3、加載所有實現了Hnadler接口的Bean,并通過pipeline.addLast(Handler handler方法為每一個handler創建一個HandlerContext,并添加到鏈條中;

至此形成了一條鏈

4、service想執行該鏈的時候,通過有參構造方法,將請求request傳遞給pipeline,調用pipeline.fireTaskFiltered();

5、pipeline.fireTaskFiltered()中,會調用HandlerContext的靜態方法HandlerContext.invokeTaskFiltered(HandlerContext ctx, Task task)將第一個HandlerContext傳入去執行,其handlerfilterTask(HandlerContext ctx, Task task)方法執行具體邏輯

6、handlerfilterTask(HandlerContext ctx, Task task)方法最后會調用ctxinvokeTaskFiltered(HandlerContext ctx, Task task)方法,該方法會使用invokeTaskFiltered(next(), task)去執行下一個節點ctx.handler().filterTask(ctx, task)

7、直至最后到節點tail沒有下一個節點會停止執行;


至此單條鏈的責任鏈模式已完成

在netty中,并不是一條鏈,每一個handler有很多針對不同的事件的處理

在pipeline中有所有的事件,我們相對某一個事件處理是,實現handler的對應方法的處理邏輯,就會在對應層級的鏈中加入該handler,netty多層級代碼責任鏈參考:Spring中如何使用責任鏈模式

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

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

相關文章

openGauss學習筆記-233 openGauss性能調優-系統調優-資源負載管理-資源管理準備-啟動資源負載管理功能

文章目錄 openGauss學習筆記-233 openGauss性能調優-系統調優-資源負載管理-資源管理準備-啟動資源負載管理功能233.1 背景信息233.2 前提條件233.3 操作步驟 openGauss學習筆記-233 openGauss性能調優-系統調優-資源負載管理-資源管理準備-啟動資源負載管理功能 233.1 背景信…

輕松解決電腦卡頓,CleanMyMac X2024讓你的Mac電腦重回巔峰性能!

在當今這個信息爆炸的時代&#xff0c;我們的電腦往往會因為安裝了過多的軟件、積累了太多的垃圾文件以及運行緩慢而變得卡頓、不流暢。然而&#xff0c;CleanMyMac X2024的出現&#xff0c;將為你解決這些問題&#xff0c;讓你的電腦重獲新生&#xff01; CleanMyMac X2024全新…

java的JDK選擇和在win11的安裝與配置

一.背景 還是公司安排的師帶徒的任務。自己也回頭看看。 二.JDK的選擇 開發的版本java1.8。所以準備使用JDK8。 1.JDK有誰在給我們提供呢&#xff1f; 我以前知道的是sun、RedHat、Oracle、IBM。 我以前一般都是去sun的網站下載&#xff0c;后來被Oracle收購后去的Oracle…

前端導出word文件的多種方式、前端導出excel文件

文章目錄 純前借助word模板端導出word文件 &#xff08;推薦&#xff09;使用模板導出 前端通過模板字符串導出word文件前端導出 excel文件&#xff0c;node-xlsx導出文件&#xff0c;行列合并 純前借助word模板端導出word文件 &#xff08;推薦&#xff09; 先看效果&#xf…

nginx如何配置命令啟動

我安裝好nginx后&#xff0c;發現不能使用systemctl start nginx或者systemctl stop nginx來控制啟停 解決方法如下 首先要建一個nginx.pid的文件 一般是建在 /var/run/這個路徑下面 sudo touch /var/run/nginx.pid 添加權限 sudo chmod 644 /var/run/nginx.pid可以進入到…

springboot 定時任務備份mysql數據庫

記錄在Linux 系統上定時備份MySQL數據庫 1、在代碼中添加備份 package org.jeecg.modules.xczxhhr.job;import lombok.extern.slf4j.Slf4j; import org.quartz.Job; import org.quartz.JobExecutionContext;import java.io.BufferedReader; import java.io.File; import java…

數據結構-----反射

文章目錄 反射1.定義2 用途(了解)3 反射基本信息4 反射相關的類&#xff08;重要&#xff09;4.1 Class類(反射機制的起源 )4.1.1 Class類中的相關方法(方法的使用方法在后邊的示例當中) 4.2 反射示例4.2.1 獲得Class對象的三種方式4.2.2 反射的使用 5、反射優點和缺點6 重點總…

雙周回顧#005 - 零

一件悲傷的事實&#xff0c;這兩周&#xff0c;成長值為零&#xff5e;&#xff5e; 從大數據部門臨時抽調到互聯網部門&#xff0c;支援重構的“配置下單”項目。 一個變種的低代碼架構設計&#xff0c;唯一比較有意思的是它的業務組件的設計與校驗設計&#xff0c;算是學習…

怎么把人物從圖中摳出?分享幾種好用的摳圖方法

在日常生活中&#xff0c;我們時常需要將人物從繁雜的背景中優雅地提取出來&#xff0c;無論是為了制作一張精美的證件照&#xff0c;還是為了設計一幅引人注目的海報或宣傳畫。然而&#xff0c;對于許多非專業人士來說&#xff0c;這仿佛是一場與細節的捉迷藏游戲&#xff0c;…

MySQL深入——23

主機內存只有100G&#xff0c;現在對一個200G的大表進行掃描&#xff0c;會不會把數據庫的內存用完。 對大表做全表掃描對Sever層的影響 假設現對一個200G的InnoDB表db1&#xff0c;做一個全表掃描&#xff0c;當然要把掃描結果保存到客戶端。 InnoDB的數據時保存在主鍵索引…

數據結構從入門到精通——順序表

順序表 前言一、線性表二、順序表2.1概念及結構2.2 接口實現2.3 數組相關面試題2.4 順序表的問題及思考 三、順序表具體實現代碼順序表的初始化順序表的銷毀順序表的打印順序表的增容順序表的頭部/尾部插入順序表的頭部/尾部刪除指定位置之前插入數據和刪除指定位置數據順序表元…

Linux如何查看端口是否占用

在Linux中&#xff0c;有多種方法可以用來檢查端口是否被占用。以下是一些常用的命令&#xff1a; netstat&#xff1a;這是一個非常通用的命令&#xff0c;可以用來查看所有端口的使用情況。如果你想查找特定的端口是否被占用&#xff0c;可以使用netstat命令配合grep。例如&…

【MySQL】DQL

DQL&#xff08;數據查詢語言&#xff09;用于在MySQL數據庫中執行數據查詢操作。它主要包括SELECT語句&#xff0c;用于從表中檢索數據。 0. 基本語法 SELECT 字段列表 FROM 表名列表 WHERE 條件列表 GROUP BY 分組字段列表 HAVING 分組后條件列表 ORDER BY 排序字段列表 …

未來醫療技術的發展方向在Python中的重要性

未來醫療技術的發展方向在Python中的重要性體現在以下幾個方面&#xff1a; 數據分析和人工智能&#xff1a;Python作為一種強大的數據分析語言&#xff0c;可以進行大規模的數據處理、分析和挖掘。在醫療領域&#xff0c;大量的醫療數據可以通過Python進行分析&#xff0c;幫助…

【市工信】2024年青島市綠色工廠、綠色工業園區等綠色制造示范申報

科大睿智小編從青島市工信局了解到&#xff0c;為深入貫徹綠色發展理念&#xff0c;牢固樹立綠色低碳發展導向&#xff0c;進一步完善綠色制造體系&#xff0c;培育綠色制造先進典型&#xff0c;根據《工業和信息化部關于印發<綠色工廠梯度培育及管理暫行辦法>的通知》&a…

springcloud:3.1介紹雪崩和Resilience4j

災難性雪崩效應 簡介 服務與服務之間的依賴性,故障會傳播,造成連鎖反應,會對整個微服務系統造成災難性的嚴重后果,這就是服務故障的“雪崩”效應。 原因 1.服務提供者不可用(硬件故障、程序bug、緩存擊穿、用戶大量請求) 2.重試加大流量(用戶重試,代碼邏輯重試) 3.服…

在golang中使用protoc

【Golang】proto生成go的相關文件 推薦個人主頁&#xff1a;席萬里的個人空間 文章目錄 【Golang】proto生成go的相關文件1、查看proto的版本號2、安裝protoc-gen-go和protoc-gen-go-grpc3、生成protobuff以及grpc的文件 1、查看proto的版本號 protoc --version2、安裝protoc-…

Acwing 周賽132 解題報告 | 珂學家 | 并查集 + floyd尋路

前言 整體評價 A. 大小寫轉換 Q: 把字符串s統一成小寫字母形態 題型&#xff1a;簽到 知識點: 考察字符串的API題 c可以借助transform函數&#xff0c;進行轉化 #include <bits/stdc.h>using namespace std;int main() {string s;cin >> s;// 把自己轉化為小寫…

10-Java裝飾器模式 ( Decorator Pattern )

Java裝飾器模式 摘要實現范例 裝飾器模式&#xff08;Decorator Pattern&#xff09;允許向一個現有的對象添加新的功能&#xff0c;同時又不改變其結構 裝飾器模式創建了一個裝飾類&#xff0c;用來包裝原有的類&#xff0c;并在保持類方法簽名完整性的前提下&#xff0c;提供…

代購程序api接口采集商品信息接入演示示例

以下是一個使用Java編寫的簡單示例&#xff0c;演示如何通過API接口采集商品信息并接入到代購程序中&#xff1a; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; impor…