如果这个世界没有了项目经理,事情的发展可能并不会如同想象中一样美好,相反,对于开发人员来说可能是噩梦的开始。
比如:
客户因为几个需求的具体实现大发雷霆,甚至开始恶语相向,一通含ma量极高的“斯伯坦语”下来,瞬间让性情耿直的开发人员集体开团,场面一度难以控制。因为对于大多数技术部门的人员来说,这种过度“世俗”的沟通方式并不能被接受。
但如果此时有一个经验丰富的项目经理作为缓冲夹在中间,将客户的“斯伯坦语”过滤处理成纯粹的需求,传达给技术人员,再将技术人员的解决方案加以“怀柔的”修饰传递给客户。世界是不是就变得美好了呢?
语言是门艺术,沟通充满了技巧。
一言
适配器模式即:将某个类的接口转换成客户端期望的另一个接口表示,让原本不匹配的两个对象可以协同工作。
概述
在刚刚的例子中,项目经理实际上就是充当了适配器的角色,让原本水火不容的客户和技术人员能够携起手来,共同完成项目。其核心在于适配器的兼容性。
在软件设计领域,通常将适配器模式划分为结构型模式的一种。从用户的角度来看,是看不到被适配者的,就比如客户通常不会关心技术人员的存在,从这个角度来说,整体的结构的耦合度是很低的。用户调用适配器,适配器再调用被适配者。
在适配器的具体实现上,通常有:类适配器、对象适配器、接口适配器三种。
类适配器
项目经理: 客户就是我的爸爸!
参照我们此前描述的情景,项目经理(适配器类)需要将客户类(被适配对象)当作父亲,通过继承获得客户的初始需求,同时还要掌握一定管理能力(实现管理接口),通过管理能力将客户的需求进行处理,交付给开发人员。
图解
实例
我们假定客户的每一个需求都带有一个愤怒值,当这个愤怒值高于10时,开发人员将认位自己受到了冒犯并拒绝工作,而项目经理则需要即时的将客户的需求通过处理交付给开发人员,以期工作的正常开展。
客户
public class Customer {
public int communicate(){
int anger = 10000;
System.out.println("沟通需求,愤怒指数:"+anger);
return anger;
}
}
管理能力
public interface Manage {
public int skill();
}
项目经理
public class ProjectManager extends Customer implements Manage{
@Override
public int skill() {
int anger = communicate();
return anger/1000;
}
}
开发人员
public class Devops {
public void work(Manage manage){
if (manage.skill()>10)
System.out.println("你的态度让我无法工作");
else
System.out.println("沟通方式很尊重我,工作包在我身上");
}
}
分析
Java是单继承机制,所以类适配器需要继承客户类这一点算是一个缺点,因为这要求管理能力必须是接口,有一定局限性;
客户类的方法在Adapter(项目经理)中都会暴露出来,也增加了使用的成本。由于其继承了客户类,所以它可以根据需求重写客户类的方法,使得Adapter的灵活性增强了。
对象适配器
项目经理:客户不是我的爸爸,他是我的家人。我们彼此尊重、方向一致。
实际上我们已经清楚的看到了类适配器存在的问题:项目经理的膝盖太软了,一直把客户(被适配对象)当作自己的父亲,这使得整个结构存在着很大的局限性。
对象适配的思路上就是让项目经理站起来,不是直接继承客户,而是通过持有客户的对象来解决兼容问题。
图解
实例核心
public class ProjectManager implements Manage {
private Customer customer;
public ProjectManager(Customer customer){
this.customer = customer;
}
@Override
public int skill() {
int anger = customer.communicate();
return anger/1000;
}
}
分析
对象适配器和类适配器其实算是同一种思想,只不过实现方式不同根据合成复用原则,使用组合替代继承, 所以它解决了类适配器必须继承被适配对象的局限性问题,也不再要求适配能力必须是接口。
使用成本更低,结构上也更灵活。
接口适配器
有很多资料也将这种方式称为“缺省适配器模式”,顾名思义,当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现 (空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求。
这其实非常适用于一个接口不想使用其所有方法的情况。
这种方式很容易理解,这里不过多赘述。
适配器模式在SpringMVC源码的应用
相信有的朋友看到SpringMVC再结合我们上文讲到的适配器模式(Adapter)就已经恍然大悟了。SpringMVC框架中一个非常重要的角色就是HandlerAdapter。
我们可以回想一下SpringMVC的工作过程(这里不做展开,忘记了的同学可以查阅下相关资料),其中有一个重要环节就是DispatchServlet 会根据 收到的Handler 选择一个合适的HandlerAdapter。这里就是适配器模式的关键应用。
下面我将相关源码主体剥离了一部分出来,大家可以体会下。
部分源码
Controller
public interface Controller {
}
class HttpController implements Controller {
public void doHttpHandler() {
System.out.println("http...");
}
}
class SimpleController implements Controller {
public void doSimplerHandler() {
System.out.println("simple...");
}
}
class AnnotationController implements Controller {
public void doAnnotationHandler() {
System.out.println("annotation...");
}
}
HandlerAdapter
public interface HandlerAdapter {
public boolean supports(Object handler);
public void handle(Object handler);
}
class SimpleHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((SimpleController) handler).doSimplerHandler();
}
public boolean supports(Object handler) {
return (handler instanceof SimpleController);
}
}
class HttpHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((HttpController) handler).doHttpHandler();
}
public boolean supports(Object handler) {
return (handler instanceof HttpController);
}
}
class AnnotationHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((AnnotationController) handler).doAnnotationHandler();
}
public boolean supports(Object handler) {
return (handler instanceof AnnotationController);
}
}
DispatchServlet
public class DispatchServlet {
public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();
public DispatchServlet() {
handlerAdapters.add(new AnnotationHandlerAdapter());
handlerAdapters.add(new HttpHandlerAdapter());
handlerAdapters.add(new SimpleHandlerAdapter());
}
public void doDispatch() {
HttpController controller = new HttpController();
// AnnotationController controller = new AnnotationController();
//SimpleController controller = new SimpleController();
HandlerAdapter adapter = getHandler(controller);
adapter.handle(controller);
}
public HandlerAdapter getHandler(Controller controller) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(controller)) {
return adapter;
}
}
return null;
}
public static void main(String[] args) {
new DispatchServlet().doDispatch(); // http...
}
}
图解
分析
对于多种Controller,应该有对应的适配器对其做处理。
在DispatchServlet从request中取到handler对象后,适配器可以得到自己希望的Controller进而通过getHandler方法得到对应的适配器,在通过适配器执行handle,也就能执行对应controller的对应方法了。
好啦,希望通过此文能让大家对适配器模式有更深一步的认识,也期待获得大家的指正或共鸣。
关注我,共同进步,每周至少一更。——Wayne