適配器模式在咱們開發中使用率極高,從最先的 ListView、GridView 到如今的 RecyclerView 都須要使用 Adapter ,而且在開發中咱們遇到的優化問題,出錯機率較大的地方也基本都出在 Adapter,這是一個讓人又愛又恨的角色。html
說到底,適配器是將兩個不兼容的類融合到一塊兒,它有點像粘合劑,將不一樣的東西經過一種轉換使得它們可以協做起來。例如,常常碰到要兩個沒有關係的類型之間進行交互,第一個解決方案是修改各自類的接口,可是若是沒有源代碼或者咱們不肯意爲了一個應用而修改各自的接口,此時怎麼辦?這種狀況咱們每每會使用一個 Adapter,在這兩種接口之間建立一個 「混血兒」 接口,這個 Adapter 會將這兩個接口進行兼容,在不修改原有代碼的狀況下知足需求。java
適配器模分爲兩種,即類適配器和對象適配器,咱們先來看類適配器。git
業務背景: 用電源接口作例子,筆記本電腦的電源通常是 5V 電壓,可是咱們生活中的電線電壓通常都是 220V 。這個時候就出現了不匹配的情況,在軟件開發中咱們稱爲接口不兼容,此時就須要適配器來進行一個接口轉換。在軟件開發中有一句話正好體現了這點:任何問題均可以加一箇中間層來解決。這個層咱們能夠理解爲這裏的 Apapter 層,經過這層來進行一個接口轉換就達到了兼容的目的。github
在上述電源接口這個示例中, 5V 電壓就是 Target 接口,220V 電壓就是 Adapter 類,而將電壓從 220V 轉換到 5V 就是 Adapter。設計模式
類適配器:緩存
target:架構
public interface FiveVolt {
int getVolt5();
}
複製代碼
Adapter 角色,須要被轉換的對象併發
public class Volt220 {
public int getVolt220(){
return 220;
}
}
複製代碼
Adapter 角色,將 220 -> 5 V 的電壓:dom
public class VoltAdapter extends Volt220 implements FiveVolt {
@Override
public int getVolt5() {
return 5;
}
}
複製代碼
test:ide
@Test
public void testAdapter1(){
VoltAdapter voltAdapter = new VoltAdapter();
System.out.println("voltAdapter:"+voltAdapter.getVolt5());
}
複製代碼
output:
voltAdapter:5
複製代碼
Target 角色給出了須要的目標接口,而 Adapter 類則是須要被轉換的對象。Adapter 則是將 Volt220 轉換成 Target 的接口。對應的 Target 的目標是要獲取 5V 的輸出電壓,而 Adapter 真正輸出電壓是 220V ,此時就須要電源適配器類將 220V 電壓轉換爲 5V 電壓,解決接口不兼容的問題。
對象適配器:
咱們只須要變換下 Adapter 對象,以下:
public class VoltAdapter extends Volt220 implements FiveVolt {
private Volt220 mVolt220;
public VoltAdapter(Volt220 mVolt220) {
this.mVolt220 = mVolt220;
}
@Override
public int getVolt220() {
return mVolt220.getVolt220();
}
@Override
public int getVolt5() {
return 5;
}
}
複製代碼
test / output:
VoltAdapter voltAdapter = new VoltAdapter(new Volt220());
System.out.println("voltAdapter:"+voltAdapter.getVolt5());
voltAdapter:5
複製代碼
這種實現方式直接將要被適配的對象傳遞到 Adapter 中,使用組合的形式是吸納接口兼容的效果。這比類適配器方式更爲靈活,它的另外一個好處是被適配的對象中的方法不會暴露出來,而類適配因爲繼承了被適配對象,所以,被適配對象類的函數在 Adapter 類中也都含有,這使得 Adapter 類出現了一些奇怪的接口,用戶使用成本較高,所以,對象適配器模式更加靈活、實用。
Adapter 模式的經典實如今於將本來不兼容的接口融合在一塊兒,使之可以很好的進行合做。可是,在實際開發中,Adapter 模式也有一些靈活的實現。例如 ListView 中的隔離變化,使得整個 UI 架構變得更靈活,可以擁抱變化。Adapter 模式在開發中運用很是普遍,所以,掌握 Adapter 模式是很是必要的。
優勢:
缺點:
裝飾模式 (也稱爲 Decorator Pattern) 也稱爲包裝模式,屬於結構型模式之一,其使用一種對客戶端透明的方式來動態的擴展對象的功能,同時它也是繼承關係的一種替代方案之一。在現實生活中你也看見不少裝飾模式的例子,或者能夠大膽地說裝飾模式無處不在,就拿人來講,人須要各式各樣的衣着,無論你穿着怎麼,可是,對於我的的本質來講是不變的,充其量只是在外面披上一層 「遮羞物」 而已,這就是裝飾模式。
動態的給一個對象田愛軍一些額外的職責。就是增長功能來講,裝飾模式生成子類更爲靈活。
須要透明且動態地擴展類的功能時。
業務背景: 爲 boy 穿衣
定義一個抽象的穿衣行爲:
public abstract class Person {
/** * Person 下有一個穿着的抽象方法 */
public abstract void dressed();
}
複製代碼
聲明一個具體行爲實現:
public class Boy extends Person {
@Override
public void dressed() {
System.out.println("男孩穿着內褲");
}
}
複製代碼
定義一個用來裝飾具體行爲的抽象:
public abstract class PersonCloth extends Person {
protected Person person
;
public PersonCloth(Person person) {
this.person = person;
}
@Override
public void dressed() {
person.dressed();
}
}
複製代碼
具體裝飾實現:
public class ExpensiveCloth extends PersonCloth {
public ExpensiveCloth(Person person) {
super(person);
}
@Override
public void dressed() {
super.dressed();
//穿短袖
dressShirt();
//穿皮衣
dressLeather();
//穿牛仔褲
dressJean();
}
private void dressShirt() {
System.out.println("穿上短袖");
}
private void dressLeather() {
System.out.println("穿上皮衣");
}
private void dressJean() {
System.out.println("穿上牛仔褲");
}
}
複製代碼
public class CheapCloth extends PersonCloth {
public CheapCloth(Person person) {
super(person);
}
@Override
public void dressed() {
super.dressed();
dressShorts();
}
private void dressShorts() {
System.out.println("穿條短褲");
}
}
複製代碼
test:
@Test
public void testDecorator(){
//首先得有一個男孩
Person person = new Boy();
//先穿上便宜的衣服
PersonCloth cheapCloth = new CheapCloth(person);
cheapCloth.dressed();
//或者在穿上有點檔次的衣服
PersonCloth personCloth = new ExpensiveCloth(person);
personCloth.dressed();
}
複製代碼
output:
男孩穿着內褲
穿條短褲
男孩穿着內褲
穿上短袖
穿上皮衣
穿上牛仔褲
複製代碼
裝飾模式和咱們前面講的 代理模式 有點相似,有時候甚至容易混淆,倒不是說會把代理當成裝飾,而是經常會是將裝飾看做代理,裝飾模式是以對客戶端透明的方式擴展對象的功能,是繼承關係的一個替代方案;而代理模式則是給一個對象提供一個代理對象,並有代理對象來控制對原有的對象的引用。裝飾模式應該爲所裝飾的對象加強功能;代理模式對代理的對象施加控制,但不對對象自己的功能進行加強。
享元模式是對象池的一種實現,享元模式用來儘量減小內存使用量,它適合用於可能存在大量重複對象的場景,來緩存可共享的對象,達到對象共享、避免建立過多對象的效果,這樣一來就能夠提高性能、避免內存移除等。
使用共享對象能夠有效地支持大量的細粒度的對象。
需求背景: 過年回家買車票,若是在併發 1W 人次同時 http 請求數據,若是後臺每次都從新建立一個查詢的車票結果,那麼必然會形成大量重複對象的建立、銷燬、使得 GC 任務繁重、內存高居不下。
展現車票信息接口
public interface Ticket {
public void showTicketInfo(String info);
}
複製代碼
展現車票具體實現:
public class TrainTicket implements Ticket {
public String from;
public String to;
public String bunk;
public int price;
public TrainTicket(String from, String to) {
this.from = from;
this.to = to;
}
@Override
public void showTicketInfo(String info) {
bunk = info;
price = new Random().nextInt(300);
System.out.println("購買從" + from + " -> " + to + "的 " + bunk + " 火車票 ,價格:" + price);
}
}
複製代碼
車票信息管理:
public class TicketFactory {
static Map<String, Ticket> sTicketMap = new ConcurrentHashMap<>();
public static Ticket getTicket(String from, String to) {
String key = from + "-" + to;
if (sTicketMap.containsKey(key)) {
//使用已經存在的對象
System.out.println("使用存在的對象 = [" + from + "], to = [" + to + "]");
return sTicketMap.get(key);
} else {
System.out.println("建立對象 = [" + from + "], to = [" + to + "]");
TrainTicket trainTicket = new TrainTicket(from, to);
sTicketMap.put(key, trainTicket);
return trainTicket;
}
}
}
複製代碼
test:
@Test
public void testFlaweiget(){
Ticket ticket1 = TicketFactory.getTicket("北京", "上海");
ticket1.showTicketInfo("上鋪");
Ticket ticket7 = TicketFactory.getTicket("北京", "上海");
ticket7.showTicketInfo("下鋪");
Ticket ticket2 = TicketFactory.getTicket("北京", "上海");
ticket2.showTicketInfo("上鋪");
Ticket ticket3 = TicketFactory.getTicket("北京", "上海");
ticket3.showTicketInfo("上鋪");
Ticket ticket4 = TicketFactory.getTicket("北京", "成都");
ticket4.showTicketInfo("下鋪");
Ticket ticket5 = TicketFactory.getTicket("北京", "上海");
ticket5.showTicketInfo("上鋪");
Ticket ticket6 = TicketFactory.getTicket("北京", "上海");
ticket6.showTicketInfo("上鋪");
}
複製代碼
output:
建立對象 = [北京], to = [上海]
購買從北京 -> 上海的 上鋪 火車票 ,價格:36
使用存在的對象 = [北京], to = [上海]
購買從北京 -> 上海的 下鋪 火車票 ,價格:261
使用存在的對象 = [北京], to = [上海]
購買從北京 -> 上海的 上鋪 火車票 ,價格:100
使用存在的對象 = [北京], to = [上海]
購買從北京 -> 上海的 上鋪 火車票 ,價格:247
建立對象 = [北京], to = [成都]
購買從北京 -> 成都的 下鋪 火車票 ,價格:224
使用存在的對象 = [北京], to = [上海]
購買從北京 -> 上海的 上鋪 火車票 ,價格:262
使用存在的對象 = [北京], to = [上海]
購買從北京 -> 上海的 上鋪 火車票 ,價格:114
複製代碼
從上面的查詢結果得知,若是已經查詢了就使用緩存,沒有就建立對象。
享元模式是實現比較簡單,可是它的做用在某些場景確實極其重要的。它能夠大大減小應用程序建立的對象,下降程序內存的佔用,加強程序的性能,但它同時也提升了系統的複雜性,須要分離出外部狀態和內部狀態,並且外部狀態具備固化特性,不該該隨內部狀態改變而改變,不然致使系統的邏輯混亂。
享元模式的有點在於大幅度地下降內存中對象的數量。可是,它作到這一點所付出的代價也是很高的。
感謝你的閱讀,謝謝!