前言:
视频教程:狂神说Java之通俗易懂的23种设计模式
什么是设计模式?
- 设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。
它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案==> 一种思维,一种态度,一种进步- 1995年,GoF(Gang of Four,四人组/四人帮)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了23种设计模式,从此树立了软件设计模式领域的里程碑,人称 【GoF设计模式】
设计模式分类 具体模式 创建型模式:
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。⌛单例模式、⌛工厂模式、⌛抽象工厂模式、⌛建造者模式、⌛原型模式 结构型模式:
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。⌛适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式 行为型模式:
这些设计模式特别关注对象之间的通信。模板方法模、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式
适配器模式:
-
将一个类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的那些类,可以在一起工作。
- 举个例子:
- 举个例子:
-
角色分析
刘伟大佬讲设计模式的文章,值得一看:不兼容结构的协调——适配器模式(一)
-
在对象适配器模式结构图中包含如下几个角色:
- Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
- Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
- Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
-
根据上述角色分析,结合例子可得:
电脑要用过USB接口上网:- 电脑:Target(目标抽象类)
- USB转接器:Adapter(适配器类)
- 网线: Adaptee(适配者类)
-
-
实现模式:
- 类适配器:通过继承
- 对象适配器:通过类作为属性进行组合
代码实例:
依旧是笔记本通过usb转接器上网的例子:
类适配器:
简单来说就是买了个自带网线的转接头:
//要被适配的类:网线类
public class Cable {
public void surfing(){
System.out.println("成功连接网线-->上网冲浪~");
}
}
//接口转换器的抽象实现
public interface CableToUSB {
//作用:处理请求,转接网线
public void handleRequest();
}
//真正的网线适配器,比如华为牌子的适配器
//此处的类适配器,相当于买了个插着网线的转换器
public class Adapter extends Cable implements CableToUSB {
@Override
public void handleRequest() {
super.surfing();//此时可以上网了
}
}
有自带网线的转换器,这时自己买的网线用不上:
//客户端:想上网的具体实现
public class Computer {
//usb需要一个转接器才能插上网线上网
public void net(CableToUSB cableToUSB){
//上网的具体实现,需要一个转接头:
cableToUSB.handleRequest();
}
public static void main(String[] args) {
//电脑:
Computer computer = new Computer();
//适配器:
Adapter adapter = new Adapter();
//网线:
Cable cable = new Cable();//自备的网线用不上
//真正用电脑上网,此时usb插上了一个自带网线的转换器
computer.net(adapter);
//成功上网:
//成功连接网线-->上网冲浪~
}
}
对象适配器:
相当于买了个 可拔插式的转接器,需要用自己的网线
//要被适配的类:网线类
public class Cable {
public void surfing(){
System.out.println("成功连接网线-->上网冲浪~");
}
}
//接口转换器的抽象实现
public interface CableToUSB {
//作用:处理请求,转接网线
public void handleRequest();
}
网线与转换器接口都不变👆
//真正的网线适配器,比如华为牌子的适配器
public class Adapter implements CableToUSB {
//此处进行组合
private Cable cable;
public Adapter(Cable cable) {
this.cable = cable;
}
@Override
public void handleRequest() {
cable.surfing();//此时可以上网了
}
}
//客户端:想上网的具体实现
public class Computer {
//usb需要一个转接器才能插上网线上网
public void net(CableToUSB cableToUSB){
//上网的具体实现,需要一个转接头:
cableToUSB.handleRequest();
}
public static void main(String[] args) {
//电脑:
Computer computer = new Computer();
//网线:
Cable cable = new Cable();
//适配器:
//此时的适配器需要插上一个网线才能用:
Adapter adapter = new Adapter(cable);
//真正用电脑上网,
computer.net(adapter);
//成功上网:
//成功连接网线-->上网冲浪~
}
}
小结:
-
对象适配器优点
- 一个对象适配器可以把多个不同的适配者适配到同一个目标
- 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配.
-
类适配器缺点
- 对于Java,C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;
- 在Java,C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。
-
适用场景
-
系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源
代码. -
想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作.
-
因此适配器模式又称 弥补形模式
怪不得在平时的二开工作中看到老大用过
-
应用案例:
-
io流中的InputStreamReader 和 OutputStreamWriter
-
java.io.InputStreamReader(InputStream)
将输入的字节流转换为字符流
public InputStreamReader(InputStream in) { super(in); try { sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object } catch (UnsupportedEncodingException e) { // The default encoding should always be available throw new Error(e); } }
-
java.io.OutputStreamWriter(OutputStream)
-
-
在SpringMVC中的DispatcherServlet 中的doDispatch() 有使用
适配器模式在 SpringMVC 中的经典使用体现在它的核心方法 doDispatch 方法中,再来看一个 Spring MVC 中的 HandlerAdapter 类,它也有多个子类,类图如下。
位于org.springframework.web.servlet包中的DispatcherServlet是servlet接口的实现类,作用是处理请求并返回结果。在servlet容器接收到一个请求时,servlet容器会针对这个请求创建一个servletRequest和servletRespones对象,相应的处理方法会通过servletRequest中携带的参数对应处理请求,再通过servletRespones对象商城请求的响应结果。
DispatcherServlet类的doDispatch方法是处理请求的核心逻辑,截取部分内容如下
try { //对请求做类型转换 processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 根据请求信息找到相应的Handler mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 根据handleru想你找相应的HandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); }
-
Collections 的 enumeration 方法
public static <T> Enumeration<T> enumeration(final Collection<T> c) { return new Enumeration<T>() { private final Iterator<T> i = c.iterator(); public boolean hasMoreElements() { return i.hasNext(); } public T nextElement() { return i.next(); } }; }