欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Java實踐之適配器模式

 更新時間:2021年06月18日 16:42:29   作者:小傅哥  
在計算機編程中,適配器模式(有時候也稱包裝樣式或者包裝)將一個類的接口適配成用戶所期待的。一個適配允許通常因為接口不兼容而不能在一起工作的類工作在一起,做法是將類自己的接口包裹在一個已存在的類中

一、前言

工作到3年左右很大一部分程序員都想提升自己的技術(shù)棧,開始嘗試去閱讀一些源碼,例如SpringMybaits、Dubbo等,但讀著讀著發(fā)現(xiàn)越來越難懂,一會從這過來一會跑到那去。甚至懷疑自己技術(shù)太差,慢慢也就不愿意再觸碰這部分知識。

而這主要的原因是一個框架隨著時間的發(fā)展,它的復雜程度是越來越高的,從最開始只有一個非常核心的點到最后開枝散葉。這就像你自己開發(fā)的業(yè)務(wù)代碼或者某個組件一樣,最開始的那部分核心代碼也許只能占到20%,而其他大部分代碼都是為了保證核心流程能正常運行的。所以這也是你讀源碼費勁的一部分原因。

框架中用到了設(shè)計模式嗎?

框架中不僅用到設(shè)計模式還用了很多,而且有些時候根本不是一個模式的單獨使用,而是多種設(shè)計模式的綜合運用。與大部分小伙伴平時開發(fā)的CRUD可就不一樣了,如果都是if語句從上到下,也就算得不上什么框架了。就像你到Spring的源碼中搜關(guān)鍵字Adapter,就會出現(xiàn)很多實現(xiàn)類,例如;UserCredentialsDataSourceAdapter。而這種設(shè)計模式就是我們本文要介紹的適配器模式。

適配器在生活里隨處可見

如果提到在日常生活中就很多適配器的存在你會想到什么?在沒有看后文之前可以先思考下。

二、適配器模式介紹

適配器模式的主要作用就是把原本不兼容的接口,通過適配修改做到統(tǒng)一。使得用戶方便使用,就像我們提到的萬能充、數(shù)據(jù)線、MAC筆記本的轉(zhuǎn)換頭、出國旅游買個插座等等,他們都是為了適配各種不同的,做的兼容。。

除了我們生活中出現(xiàn)的各種適配的場景,那么在業(yè)務(wù)開發(fā)中呢?

在業(yè)務(wù)開發(fā)中我們會經(jīng)常的需要做不同接口的兼容,尤其是中臺服務(wù),中臺需要把各個業(yè)務(wù)線的各種類型服務(wù)做統(tǒng)一包裝,再對外提供接口進行使用。而這在我們平常的開發(fā)中也是非常常見的。

三、案例場景模擬

隨著公司的業(yè)務(wù)的不斷發(fā)展,當基礎(chǔ)的系統(tǒng)逐步成型以后。業(yè)務(wù)運營就需要開始做用戶的拉新和促活,從而保障DAU的增速以及最終ROI轉(zhuǎn)換。

而這時候就會需要做一些營銷系統(tǒng),大部分常見的都是裂變、拉客,例如;你邀請一個用戶開戶、或者邀請一個用戶下單,那么平臺就會給你返利,多邀多得。同時隨著拉新的量越來越多開始設(shè)置每月下單都會給首單獎勵,等等,各種營銷場景。

那么這個時候做這樣一個系統(tǒng)就會接收各種各樣的MQ消息或者接口,如果一個個的去開發(fā),就會耗費很大的成本,同時對于后期的拓展也有一定的難度。此時就會希望有一個系統(tǒng)可以配置一下就把外部的MQ接入進行,這些MQ就像上面提到的可能是一些注冊開戶消息、商品下單消息等等。

而適配器的思想方式也恰恰可以運用到這里,并且我想強調(diào)一下,適配器不只是可以適配接口往往還可以適配一些屬性信息。

3.1、場景模擬工程

itstack-demo-design-6-00

└── src

    └── main

        └── java

            └── org.itstack.demo.design

                ├── mq

                │   ├── create_account.java

                │   ├── OrderMq.java

                │   └── POPOrderDelivered.java

                └── service

                    ├── OrderServicejava

                    └── POPOrderService.java

這里模擬了三個不同類型的MQ消息,而在消息體中都有一些必要的字段,比如;用戶ID、時間、業(yè)務(wù)ID,但是每個MQ的字段屬性并不一樣。就像用戶ID在不同的MQ里也有不同的字段:uId、userId等。同時還提供了兩個不同類型的接口,一個用于查詢內(nèi)部訂單訂單下單數(shù)量,一個用于查詢第三方是否首單。后面會把這些不同類型的MQ和接口做適配兼容。

3.2、場景簡述

3.2.1、注冊開戶MQ

public class create_account {

    private String number;      // 開戶編號
    private String address;     // 開戶地
    private Date accountDate;   // 開戶時間
    private String desc;        // 開戶描述

    // ... get/set     
}

3.2.2、內(nèi)部訂單MQ

public class OrderMq {

    private String uid;           // 用戶ID
    private String sku;           // 商品
    private String orderId;       // 訂單ID
    private Date createOrderTime; // 下單時間     

    // ... get/set      
}

3.2.3、第三方訂單MQ

public class POPOrderDelivered {

    private String uId;     // 用戶ID
    private String orderId; // 訂單號
    private Date orderTime; // 下單時間
    private Date sku;       // 商品
    private Date skuName;   // 商品名稱
    private BigDecimal decimal; // 金額

    // ... get/set      
}

3.2.4、查詢用戶內(nèi)部下單數(shù)量接口

public class OrderService {

    private Logger logger = LoggerFactory.getLogger(POPOrderService.class);

    public long queryUserOrderCount(String userId){
        logger.info("自營商家,查詢用戶的訂單是否為首單:{}", userId);
        return 10L;
    }

}

3.2.5、查詢用戶第三方下單首單接口

public class POPOrderService {

    private Logger logger = LoggerFactory.getLogger(POPOrderService.class);

    public boolean isFirstOrder(String uId) {
        logger.info("POP商家,查詢用戶的訂單是否為首單:{}", uId);
        return true;
    }

}

以上這幾項就是不同的MQ以及不同的接口的一個體現(xiàn),后面我們將使用這樣的MQ消息和接口,給它們做相應的適配。

四、代碼實現(xiàn)

其實大部分時候接MQ消息都是創(chuàng)建一個類用于消費,通過轉(zhuǎn)換他的MQ消息屬性給自己的方法。

我們接下來也是先體現(xiàn)一下這種方式的實現(xiàn)模擬,但是這樣的實現(xiàn)有一個很大的問題就是,當MQ消息越來越多后,甚至幾十幾百以后,你作為中臺要怎么優(yōu)化呢?

4.1、工程結(jié)構(gòu)

itstack-demo-design-6-01

└── src

    └── main

        └── java

            └── org.itstack.demo.design

                └── create_accountMqService.java

                └── OrderMqService.java

                └── POPOrderDeliveredService.java

目前需要接收三個MQ消息,所有就有了三個對應的類,和我們平時的代碼幾乎一樣。如果你的MQ量不多,這樣的寫法也沒什么問題,但是隨著數(shù)量的增加,就需要考慮用一些設(shè)計模式來解決。

4.2、Mq接收消息實現(xiàn)

public class create_accountMqService {

    public void onMessage(String message) {

        create_account mq = JSON.parseObject(message, create_account.class);

        mq.getNumber();
        mq.getAccountDate();

        // ... 處理自己的業(yè)務(wù)
    }

}

三組MQ的消息都是一樣模擬使用,就不一一展示了??梢垣@取源碼后學習。

五、適配器模式重構(gòu)代碼

接下來使用適配器模式來進行代碼優(yōu)化,也算是一次很小的重構(gòu)。

適配器模式要解決的主要問題就是多種差異化類型的接口做統(tǒng)一輸出,這在我們學習工廠方法模式中也有所提到不同種類的獎品處理,其實那也是適配器的應用。

在本文中我們還會再另外體現(xiàn)出一個多種MQ接收,使用MQ的場景。來把不同類型的消息做統(tǒng)一的處理,便于減少后續(xù)對MQ接收。

在這里如果你之前沒要開發(fā)過接收MQ消息,可能聽上去會有些不理解這樣的場景。對此,我個人建議先了解下MQ。另外就算不了解也沒關(guān)系,不會影響對思路的體會。

再者,本文所展示的MQ兼容的核心部分,也就是處理適配不同的類型字段。而如果我們接收MQ后,在配置不同的消費類時,如果不希望一個個開發(fā)類,那么可以使用代理類的方式進行處理。

5.1、工程結(jié)構(gòu)

itstack-demo-design-6-02

└── src

    └── main

        └── java

            └── org.itstack.demo.design

                ├── impl

                │   ├── InsideOrderService.java

                │   └── POPOrderAdapterServiceImpl.java

                ├── MQAdapter,java

                ├── OrderAdapterService,java

                └── RebateInfo,java

適配器模型結(jié)構(gòu)

  • 這里包括了兩個類型的適配;接口適配、MQ適配。之所以不只是模擬接口適配,因為很多時候大家都很常見了,所以把適配的思想換一下到MQ消息體上,增加大家多設(shè)計模式的認知。
  • 先是做MQ適配,接收各種各樣的MQ消息。當業(yè)務(wù)發(fā)展的很快,需要對下單用戶首單才給獎勵,在這樣的場景下再增加對接口的適配操作。

5.2、代碼實現(xiàn)(MQ消息適配)

5.2.1、統(tǒng)一的MQ消息體

public class RebateInfo {

    private String userId;  // 用戶ID
    private String bizId;   // 業(yè)務(wù)ID
    private Date bizTime;   // 業(yè)務(wù)時間
    private String desc;    // 業(yè)務(wù)描述
    
    // ... get/set
}
  • MQ消息中會有多種多樣的類型屬性,雖然他們都有同樣的值提供給使用方,但是如果都這樣接入那么當MQ消息特別多時候就會很麻煩。
  • 所以在這個案例中我們定義了通用的MQ消息體,后續(xù)把所有接入進來的消息進行統(tǒng)一的處理。

5.2.2、MQ消息體適配類

public class MQAdapter {

    public static RebateInfo filter(String strJson, Map<String, String> link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        return filter(JSON.parseObject(strJson, Map.class), link);
    }

    public static RebateInfo filter(Map obj, Map<String, String> link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        RebateInfo rebateInfo = new RebateInfo();
        for (String key : link.keySet()) {
            Object val = obj.get(link.get(key));
            RebateInfo.class.getMethod("set" + key.substring(0, 1).toUpperCase() + key.substring(1), String.class).invoke(rebateInfo, val.toString());
        }
        return rebateInfo;
    }

}
  • 這個類里的方法非常重要,主要用于把不同類型MQ種的各種屬性,映射成我們需要的屬性并返回。就像一個屬性中有用戶ID;uId,映射到我們需要的;userId,做統(tǒng)一處理。
  • 而在這個處理過程中需要把映射管理傳遞給Map<String, String> link,也就是準確的描述了,當前MQ中某個屬性名稱,映射為我們的某個屬性名稱。
  • 最終因為我們接收到的mq消息基本都是json格式,可以轉(zhuǎn)換為MAP結(jié)構(gòu)。最后使用反射調(diào)用的方式給我們的類型賦值。

5.2.3、測試適配類

編寫單元測試類

@Test
public void test_MQAdapter() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    create_account create_account = new create_account();
    create_account.setNumber("100001");
    create_account.setAddress("河北省.廊坊市.廣陽區(qū).大學里職業(yè)技術(shù)學院");
    create_account.setAccountDate(new Date());
    create_account.setDesc("在校開戶");          

    HashMap<String, String> link01 = new HashMap<String, String>();
    link01.put("userId", "number");
    link01.put("bizId", "number");
    link01.put("bizTime", "accountDate");
    link01.put("desc", "desc");
    RebateInfo rebateInfo01 = MQAdapter.filter(create_account.toString(), link01);
    System.out.println("mq.create_account(適配前)" + create_account.toString());
    System.out.println("mq.create_account(適配后)" + JSON.toJSONString(rebateInfo01));

    System.out.println("");

    OrderMq orderMq = new OrderMq();
    orderMq.setUid("100001");
    orderMq.setSku("10928092093111123");
    orderMq.setOrderId("100000890193847111");
    orderMq.setCreateOrderTime(new Date()); 

    HashMap<String, String> link02 = new HashMap<String, String>();
    link02.put("userId", "uid");
    link02.put("bizId", "orderId");
    link02.put("bizTime", "createOrderTime");
    RebateInfo rebateInfo02 = MQAdapter.filter(orderMq.toString(), link02);

    System.out.println("mq.orderMq(適配前)" + orderMq.toString());
    System.out.println("mq.orderMq(適配后)" + JSON.toJSONString(rebateInfo02));
}
  • 在這里我們分別模擬傳入了兩個不同的MQ消息,并設(shè)置字段的映射關(guān)系。
  • 等真的業(yè)務(wù)場景開發(fā)中,就可以配這種映射配置關(guān)系交給配置文件或者數(shù)據(jù)庫后臺配置,減少編碼。

測試結(jié)果

mq.create_account(適配前){"accountDate":1591024816000,"address":"河北省.廊坊市.廣陽區(qū).大學里職業(yè)技術(shù)學院","desc":"在校開戶","number":"100001"}

mq.create_account(適配后){"bizId":"100001","bizTime":1591077840669,"desc":"在校開戶","userId":"100001"}

mq.orderMq(適配前){"createOrderTime":1591024816000,"orderId":"100000890193847111","sku":"10928092093111123","uid":"100001"}

mq.orderMq(適配后){"bizId":"100000890193847111","bizTime":1591077840669,"userId":"100001"}

Process finished with exit code 0

  • 從上面可以看到,同樣的字段值在做了適配前后分別有統(tǒng)一的字段屬性,進行處理。這樣業(yè)務(wù)開發(fā)中也就非常簡單了。
  • 另外有一個非常重要的地方,在實際業(yè)務(wù)開發(fā)中,除了反射的使用外,還可以加入代理類把映射的配置交給它。這樣就可以不需要每一個mq都手動創(chuàng)建類了。

5.3、代碼實現(xiàn)(接口使用適配)

就像我們前面提到隨著業(yè)務(wù)的發(fā)展,營銷活動本身要修改,不能只是接了MQ就發(fā)獎勵。因為此時已經(jīng)拉新的越來越多了,需要做一些限制。

因為增加了只有首單用戶才給獎勵,也就是你一年或者新人或者一個月的第一單才給你獎勵,而不是你之前每一次下單都給獎勵。

那么就需要對此種方式進行限制,而此時MQ中并沒有判斷首單的屬性。只能通過接口進行查詢,而拿到的接口如下;

接口 描述
org.itstack.demo.design.service.OrderService.queryUserOrderCount(String userId) 出參long,查詢訂單數(shù)量
org.itstack.demo.design.service.OrderService.POPOrderService.isFirstOrder(String uId) 出參boolean,判斷是否首單
  • 兩個接口的判斷邏輯和使用方式都不同,不同的接口提供方,也有不同的出參。一個是直接判斷是否首單,另外一個需要根據(jù)訂單數(shù)量判斷。
  • 因此這里需要使用到適配器的模式來實現(xiàn),當然如果你去編寫if語句也是可以實現(xiàn)的,但是我們經(jīng)常會提到這樣的代碼很難維護。

5.3.1、定義統(tǒng)一適配接口

public interface OrderAdapterService {

    boolean isFirst(String uId);

}

后面的實現(xiàn)類都需要完成此接口,并把具體的邏輯包裝到指定的類中,滿足單一職責。

5.3.2、分別實現(xiàn)兩個不同的接口

內(nèi)部商品接口

public class InsideOrderService implements OrderAdapterService {

    private OrderService orderService = new OrderService();

    public boolean isFirst(String uId) {
        return orderService.queryUserOrderCount(uId) <= 1;
    }

}

第三方商品接口

public class POPOrderAdapterServiceImpl implements OrderAdapterService {

    private POPOrderService popOrderService = new POPOrderService();

    public boolean isFirst(String uId) {
        return popOrderService.isFirstOrder(uId);
    }

}

在這兩個接口中都實現(xiàn)了各自的判斷方式,尤其像是提供訂單數(shù)量的接口,需要自己判斷當前接到mq時訂單數(shù)量是否<= 1,以此判斷是否為首單。

5.3.3、測試適配類

編寫單元測試類

@Test
public void test_itfAdapter() {
    OrderAdapterService popOrderAdapterService = new POPOrderAdapterServiceImpl();
    System.out.println("判斷首單,接口適配(POP):" + popOrderAdapterService.isFirst("100001"));   

    OrderAdapterService insideOrderService = new InsideOrderService();
    System.out.println("判斷首單,接口適配(自營):" + insideOrderService.isFirst("100001"));
}

測試結(jié)果

23:25:47.076 [main] INFO  o.i.d.design.service.POPOrderService - POP商家,查詢用戶的訂單是否為首單:100001

判斷首單,接口適配(POP):true

23:25:47.079 [main] INFO  o.i.d.design.service.POPOrderService - 自營商家,查詢用戶的訂單是否為首單:100001

判斷首單,接口適配(自營):false

Process finished with exit code 0

從測試結(jié)果上來看,此時已經(jīng)的接口已經(jīng)做了統(tǒng)一的包裝,外部使用時候就不需要關(guān)心內(nèi)部的具體邏輯了。而且在調(diào)用的時候只需要傳入統(tǒng)一的參數(shù)即可,這樣就滿足了適配的作用。

六、總結(jié)

  • 從上文可以看到不使用適配器模式這些功能同樣可以實現(xiàn),但是使用了適配器模式就可以讓代碼:干凈整潔易于維護、減少大量重復的判斷和使用、讓代碼更加易于維護和拓展。
  • 尤其是我們對MQ這樣的多種消息體中不同屬性同類的值,進行適配再加上代理類,就可以使用簡單的配置方式接入對方提供的MQ消息,而不需要大量重復的開發(fā)。非常利于拓展。
  • 設(shè)計模式的學習過程可能會在一些章節(jié)中涉及到其他設(shè)計模式的體現(xiàn),只不過不會重點講解,避免喧賓奪主。但在實際的使用中,往往很多設(shè)計模式是綜合使用的,并不會單一出現(xiàn)。

以上就是詳解Java實踐之適配器模式的詳細內(nèi)容,更多關(guān)于Java適配器模式的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java中Runnable和Thread的區(qū)別分析

    Java中Runnable和Thread的區(qū)別分析

    在java中可有兩種方式實現(xiàn)多線程,一種是繼承Thread類,一種是實現(xiàn)Runnable接口,下面就拉分別介紹一下這兩種方法的優(yōu)缺點
    2013-03-03
  • java-thymeleaf的使用方式

    java-thymeleaf的使用方式

    這篇文章主要介紹了java-thymeleaf的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • 基于ThreadPoolTaskExecutor的使用說明

    基于ThreadPoolTaskExecutor的使用說明

    這篇文章主要介紹了基于ThreadPoolTaskExecutor的使用說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Mybatis-Plus 多表聯(lián)查分頁的實現(xiàn)代碼

    Mybatis-Plus 多表聯(lián)查分頁的實現(xiàn)代碼

    本篇文章主要介紹了Mybatis-Plus 多表聯(lián)查分頁的實現(xiàn)代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06
  • 詳解Java設(shè)計模式編程中的Flyweight享元模式的開發(fā)結(jié)構(gòu)

    詳解Java設(shè)計模式編程中的Flyweight享元模式的開發(fā)結(jié)構(gòu)

    這篇文章主要介紹了Java設(shè)計模式編程中的Flyweight享元模式的開發(fā)結(jié)構(gòu),享元模式能夠最大限度地重用現(xiàn)有的同類對象,需要的朋友可以參考下
    2016-04-04
  • Java實現(xiàn)LRU緩存算法的參考示例

    Java實現(xiàn)LRU緩存算法的參考示例

    這篇文章主要介紹了JAVA實現(xiàn)LRU緩存算法的參考示例,幫助大家根據(jù)需求實現(xiàn)算法,對大家的學習或工作有一定的參考價值,需要的朋友可以參考下
    2023-05-05
  • 基于spring如何實現(xiàn)事件驅(qū)動實例代碼

    基于spring如何實現(xiàn)事件驅(qū)動實例代碼

    這篇文章主要給大家介紹了關(guān)于基于spring如何實現(xiàn)事件驅(qū)動的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用spring具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-04-04
  • Java使用XML與注解方式實現(xiàn)CRUD操作代碼

    Java使用XML與注解方式實現(xiàn)CRUD操作代碼

    MyBatis提供了靈活的配置和使用方式,使得數(shù)據(jù)庫操作更加簡潔和高效,通過本文,我們介紹了如何使用MyBatis框架,通過XML映射文件和注解兩種方式來實現(xiàn)數(shù)據(jù)庫的增刪改查操作,感興趣的朋友跟隨小編一起看看吧
    2024-02-02
  • Java應用程序開發(fā)學習之static關(guān)鍵字應用

    Java應用程序開發(fā)學習之static關(guān)鍵字應用

    今天小編就為大家分享一篇關(guān)于Java應用程序開發(fā)學習之static關(guān)鍵字應用,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • 詳談fastjson將對象格式化成json時的兩個問題

    詳談fastjson將對象格式化成json時的兩個問題

    下面小編就為大家?guī)硪黄斦刦astjson將對象格式化成json時的兩個問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05

最新評論