前倆天老媽說從家裏抽屜裏找到個耳機但是用不了,我讓她拍個照片給我看,原來是因爲手機是Type-C接口,耳機是3.5mm頭的。於是我買了個轉接口給她。如下圖:
本能反應的我,立馬開始聯想了起來…
適配器
情景1 沒有耳機
mom的手機
public class MomPhone {
private TypeC earphone;
public void play(){
if (earphone == null) {
System.out.println("loudspeaker play");
} else {
System.out.println(earphone.getType() + " earphone play");
}
}
public void connect(TypeC earphone){
this.earphone = earphone;
}
}
播放狀態是揚聲器外放
MomPhone momPhone = new MomPhone();
momPhone.play(); // loudspeaker play
情景2 連接Type-C接口的華爲耳機
public interface TypeC {
String getType();
}
public class TypeCHW implements TypeC {
@Override
public String getType() {
return "Huawei";
}
}
播放狀態是華爲耳機播放
momPhone.connect(new TypeCHW());
momPhone.play(); // Huawei earphone play
情景3 連接3.5mm的JBL耳機
public interface M35 {
String getType();
}
public class M35JBL implements M35 {
@Override
public String getType() {
return "JBL";
}
}
接口不對應是不能使用的,所以需要一個如圖的轉接頭
public class Adapter implements TypeC{
private M35 m35;
public Adapter(M35 m35) {
this.m35 = m35;
}
@Override
public String getType() {
return m35.getType();
}
}
轉接頭需要連接手機,所以必須實現Type-C接口,並且暴露一個3.5mm接口,讓3.5mm的設備連接自己,就是Adapter的構造方法。
有了轉接口就可以播放了
momPhone.connect(new Adapter(new M35JBL()));
momPhone.play(); // JBL earphone play
適配器模式
情景3中將3.5mm設備通過轉接口連接上Type-C接口的設備的過程,就是使用了設計模式中適配器模式,那個轉接頭就是適配器。
適配器模式可以分爲類適配、對象適配、接口適配,上面用的就是對象適配。
類適配
public class M35JBLAdapter extends M35JBL implements TypeC {
@Override
public String getType() {
return super.getType();
}
}
要描述上面這個類,可以理解JBL的廠商將原來的JBL耳機升級爲Type-C接口一樣,是專門針對M35JBL這款設備做了適配一樣。
momPhone.connect(new M35JBLAdapter());
momPhone.play(); // JBL earphone play
接口適配
這種模式在業務代碼中應該很少遇到。大致描述就是說,有一個接口中定義了很多方法,如果想只實現其中部分的話,可以在接口和實現類中增加一層抽象類,抽象類實現接口所有方法,但是都是空實現,自定義類繼承抽象類可以重寫其中部分方法。
public interface A {
void a();
void b();
void c();
void d();
void e();
void f();
void g();
void h();
}
public abstract class AbstractA implements A {
public void a() {}
public void b() {}
public void c() {}
public void d() {}
public void e() {}
public void f() {}
public void g() {}
public void h() {}
}
public class AImpl extends AbstractA {
public void b() {
System.out.println();
}
public void e() {
System.out.println();
}
}
其中的AbstractA類就是適配器。
策略模式
策略模式大多情況用在根據不同角色(或類型)處理不同業務的場景中(比如遊客、VIP、活動送的VIP)。舉個例子:
if ("tourists".equals(type)) {
// 業務a
} else if ("vip".equals(type)) {
// 業務a
} else if ("presenterVip".equals(type)) {
// 業務a
} else {
// 業務a
}
隨着業務代碼行數比較多的時候,將每個業務代碼提成一個單獨的方法,可讀性很高。直到增加了一個爲 SVIP 的類型,原來的業務方法可能也需要拆分,於是決定將每個方法都獨立成一個類。
public interface Role {
void run();
}
public class VIP implements Role {
@Override
public void run() {
// vip
}
}
public class SVIP implements Role {
@Override
public void run() {
// 金主爸爸,什麼權限都開
}
}
public class Tourists implements Role {
@Override
public void run() {
// 窮b,不需要操作
return;
}
}
public class PVIP implements Role{
@Override
public void run() {
// 活動送的vip,只設置頭像掛飾
}
}
另外定義一個業務類,將業務方法抽取出來
public class RoleService {
private Role role;
public RoleService(Role role){
this.role = role;
}
public void run(){
role.run();
}
}
最初的調用方式變成了下面這樣:
Role role = null;
if ("tourists".equals(type)) {
role = new Tourists();
} else if ("vip".equals(type)) {
role = new VIP();
} else if ("presenterVip".equals(type)) {
role = new PVIP();
} else if ("svip".equals(type)){
role = new SVIP();
}
RoleService roleService = new RoleService(role);
roleService.run(); // 業務a
如果再次增加一種類型,同樣執行業務a,那麼擴展Role接口的實現類即可(程序設計開閉原則:對擴展開放,對修改關閉),以上改進流程就是運用了策略設計模式。每種角色有單獨的實現類後,其中只包含該角色應該要做的業務,不和其他角色業務混在一起(單一職能原則),降低業務間的耦合。
但是貌似這麼多if-else還是沒有刪除,還是要修改if-else內容。其實這塊代碼可以抽象成工廠方法,需要根據type獲取對應角色實現類,用在程序其他地方。
簡單工廠模式
public class RoleFactory {
public static Role create(String type) {
if ("tourists".equals(type)) {
return new Tourists();
} else if ("vip".equals(type)) {
return new VIP();
} else if ("presenterVip".equals(type)) {
return new PVIP();
} else if ("svip".equals(type)){
return new SVIP();
} else {
throw new RuntimeException("Type Error");
}
}
}
使用簡單工廠
Role role = RoleFactory.create(type);
RoleService roleService = new RoleService(role);
roleService.run(); // 業務a
最後
剛開始寫代碼的時候並不知道設計模式有什麼用,感覺花裏胡哨還把自己繞暈了,直到在多個項目中由於需求的增加,發現自己寫的代碼改起來越來越費勁,慢慢的重構改進後,有一天發現自己原來用了設計模式(第一個是策略模式)。那麼下次遇到這種業務場景,開始就按一種模式設計後,不論是後期優化還是擴展都很方便,而且代碼的可讀性也很好。
每個人對設計模式態度是不一樣的,站的高度不同,看法也不同。“看山是山,看水是水;看山不是山,看水不是水;看山還是山,看水還是水”。不知道自己何時能修煉到那個境界。