前俩天老妈说从家里抽屉里找到个耳机但是用不了,我让她拍个照片给我看,原来是因为手机是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 {@Overridepublic 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 {@Overridepublic String getType() {return "JBL";}
}
接口不对应是不能使用的,所以需要一个如图的转接头
public class Adapter implements TypeC{private M35 m35;public Adapter(M35 m35) {this.m35 = m35;}@Overridepublic 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 {@Overridepublic 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 {@Overridepublic void run() {// vip}
}public class SVIP implements Role {@Overridepublic void run() {// 金主爸爸,什么权限都开}
}public class Tourists implements Role {@Overridepublic void run() {// 穷b,不需要操作return;}
}public class PVIP implements Role{@Overridepublic 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
最后
刚开始写代码的时候并不知道设计模式有什么用,感觉花里胡哨还把自己绕晕了,直到在多个项目中由于需求的增加,发现自己写的代码改起来越来越费劲,慢慢的重构改进后,有一天发现自己原来用了设计模式(第一个是策略模式)。那么下次遇到这种业务场景,开始就按一种模式设计后,不论是后期优化还是扩展都很方便,而且代码的可读性也很好。
每个人对设计模式态度是不一样的,站的高度不同,看法也不同。“看山是山,看水是水;看山不是山,看水不是水;看山还是山,看水还是水”。不知道自己何时能修炼到那个境界。