工厂模式、适配器模式、代理模式、模板方法模式、责任链模式

微信图片_20211009085252

设计模式

设计模式

工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

主要解决:主要解决接口选择的问题。

何时使用:我们明确地计划不同条件下创建不同实例时。

如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。

关键代码:创建过程在其子类执行。

应用实例: 1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。

优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。

缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

使用场景: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,”POP3”、”IMAP”、”HTTP”,可以把这三个作为产品类,共同实现一个接口。

注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

案例

  • Computer
1
2
3
4
5
6
7
8
/**
* @author xiaobo
* @date 2021/10/8 - 13:32
* 接口
*/
public interface Computer {
public void createComputer();
}
  • ComputerA
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author xiaobo
* @date 2021/10/8 - 13:32
* 计算机A类
*/
public class ComputerA implements Computer {

@Override
public void createComputer() {
System.out.println("计算机A生产完成");
}
}
  • ComputerB
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author xiaobo
* @date 2021/10/8 - 13:32
* 计算机B类
*/
public class ComputerB implements Computer {

@Override
public void createComputer() {
System.out.println("计算机B生产完成");
}
}
  • ComputerFactory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* @author xiaobo
* @date 2021/10/8 - 13:33
* 计算机工厂类
*/
public class ComputerFactory {

//注入进来
private Computer computer;

/**
* 创建计算机的方法
* @param name
* @return
*/
public Computer createComputer(String name){

//当传入的名字为a时、创建计算机A类实例化对象
if (name.equals("a")) {
computer = new ComputerA();
}

//当传入的名字为b时、创建计算机B类实例化对象
if (name.equals("b")) {
computer = new ComputerB();
}

//返回计算机对象
return computer;
}
}
  • main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* @author xiaobo
* @date 2021/10/8 - 13:35
* 测试类
*/
public class Test {

/**
* 主函数
* @param args
*/
public static void main(String[] args) {
//创建计算机工厂实例化对象
ComputerFactory computerFactory = new ComputerFactory();

//创建计算机
Computer computerA = computerFactory.createComputer("a");

computerA.createComputer();

//创建计算机
Computer computerB = computerFactory.createComputer("b");

computerB.createComputer();
}
}

工厂模式结果

适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

我们通过下面的实例来演示适配器模式的使用。其中,音频播放器设备只能播放 mp3 文件,通过使用一个更高级的音频播放器来播放 vlc 和 mp4 文件。

意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

主要解决:主要解决在软件系统中,常常要将一些”现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。

何时使用: 1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。 3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)

如何解决:继承或依赖(推荐)。

关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。

应用实例: 1、美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。 2、JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式。 3、在 LINUX 上运行 WINDOWS 程序。 4、JAVA 中的 jdbc。

优点: 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。

缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。

注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。

案例

  • MusicPlayer
1
2
3
4
5
public interface MusicPlayer {

public void play(String type, String fileName);

}
  • MyPlay
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @author xiaobo
* @date 2021/10/8 - 14:05
* 播放器类
*/
public class MyPlayer {

/**
* MP3方法
* @param fileName
*/
public void playerMp3(String fileName){
System.out.println("Player mp3:" + fileName + ".mp3");
}

/**
* wma方法
* @param fileName
*/
public void playerWma(String fileName){
System.out.println("Player wma:" + fileName + ".wma");
}
}
  • PlayerAdapter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* @author xiaobo
* @date 2021/10/8 - 14:10
* 播放器适配器类
*/
public class PlayerAdapter implements MusicPlayer {

//注入播放器类
private MyPlayer myPlayer;

/**
* 构造方法、创建播放器实例化对象
*/
public PlayerAdapter() {
this.myPlayer = new MyPlayer();
}

/**
* 适配方法
* @param type
* @param fileName
*/
@Override
public void play(String type, String fileName) {

//当类型为mp3类型时、适配mp3
if (type.equals("mp3")) {
this.myPlayer.playerMp3(fileName);
}

//当类型为wma类型时、适配wma
if (type.equals("wma")){
this.myPlayer.playerWma(fileName);
}
}
}
  • main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @author xiaobo
* @date 2021/10/8 - 14:12
* 测试类
*/
public class Test {

/**
* 主函数
* @param args
*/
public static void main(String[] args) {

//创建适配器实例化对象
PlayerAdapter playterAdapter = new PlayerAdapter();

//调用适配方法
playterAdapter.play("mp3","起风了");

//调用适配方法
playterAdapter.play("wma","遇见");
}
}

适配器模式结果

代理模式

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

意图:为其他对象提供一种代理以控制对这个对象的访问。

主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

何时使用:想在访问一个类时做一些控制。

如何解决:增加中间层。

关键代码:实现与被代理类组合。

应用实例: 1、Windows 里面的快捷方式。 2、猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。 3、买火车票不一定在火车站买,也可以去代售点。 4、一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。 5、spring aop。

优点: 1、职责清晰。 2、高扩展性。 3、智能化。

缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

使用场景:按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。

注意事项: 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。

案例

  • House
1
2
3
4
5
6
7
8
9
10
/**
* @author xiaobo
* @date 2021/10/8 - 14:44
* 找房子接口
*/
public interface House {

public void findHoues();

}
  • XiaoBO
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author xiaobo
* @date 2021/10/8 - 14:44
* 小博找房子类
*/
public class XiaoBo implements House {

@Override
public void findHoues() {
System.out.println("小博在找房子");
}
}
  • HouseProxy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* @author xiaobo
* @date 2021/10/8 - 14:44
* 代理找房子类
*/
public class HouseProxy implements House{

//注入房子接口
private House house;

/**
* 构造方法
* @param house
*/
public HouseProxy(House house) {
this.house = house;
}

/**
* 代理找房子方法
*/
@Override
public void findHoues() {

//这块可以写业务逻辑
System.out.println("代理再给我找房子");

this.house.findHoues();
}
}
  • main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @author xiaobo
* @date 2021/10/8 - 14:46
* 测试类
*/
public class Test {

/**
* 主方法
* @param args
*/
public static void main(String[] args) {

House house = new XiaoBo();

HouseProxy houseProxy = new HouseProxy(house);

houseProxy.findHoues();

}
}

代理模式结果

模板方法模式

在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。

意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

主要解决:一些方法通用,却在每一个子类都重新写了这一方法。

何时使用:有一些通用的方法。

如何解决:将这些通用算法抽象出来。

关键代码:在抽象类实现,其他步骤在子类实现。

应用实例: 1、在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。 2、西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。 3、spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。

优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。

缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。

注意事项:为防止恶意操作,一般模板方法都加上 final 关键词。

案例

  • Cook
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* @author xiaobo
* @date 2021/10/8 - 15:12
* 模板类
*/
public abstract class Cook {

public void open(){
System.out.println("打开抽油烟机");
}

public void fire(){
System.out.println("开火");
}

public abstract void doCook();

public void outFire(){
System.out.println("关火");
}

public void close(){
System.out.println("关闭抽油烟机");
}

public void cook(){
this.open();
this.fire();
this.doCook();
this.outFire();
this.close();
}
}
  • CookTomato
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @author xiaobo
* @date 2021/10/8 - 15:15
* 工作类
*/
public class CookTomato extends Cook {

@Override
public void doCook() {
System.out.println("炒西红柿");
}
}

  • CookPotato
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author xiaobo
* @date 2021/10/8 - 15:16
* 工作类
*/
public class CookPotato extends Cook{

@Override
public void doCook() {
System.out.println("炒土豆");
}
}
  • main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author xiaobo
* @date 2021/10/8 - 15:17
* 测试类
*/
public class Test {

public static void main(String[] args) {
Cook cook = new CookTomato();
cook.cook();
System.out.println("****************");
cook = new CookPotato();
cook.cook();
}
}

模板方式模式结果

责任链模式

顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

何时使用:在处理消息的时候以过滤很多道。

如何解决:拦截的类都实现统一接口。

关键代码:Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。

应用实例: 1、红楼梦中的”击鼓传花”。 2、JS 中的事件冒泡。 3、JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。

优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。

缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。

使用场景: 1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。

注意事项:在 JAVA WEB 中遇到很多应用。

案例

  • Post
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @author xiaobo
* @date 2021/10/9 - 8:12
* 实体类
*/
public class Post {

private String context;

public String getContext() {
return context;
}

public void setContext(String context) {
this.context = context;
}
}
  • PostHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @author xiaobo
* @date 2021/10/9 - 8:13
* 抽象父类
*/
public abstract class PostHandler {

private PostHandler postHandler;

public void setPostHandler(PostHandler postHandler) {
this.postHandler = postHandler;
}

/**
* 抽象方法、使其子类重写
* @param post
*/
public abstract void handlerRequest(Post post);

/**
* 下一节点
* @param post
*/
protected final void next(Post post){
if (this.postHandler != null) {
this.postHandler.handlerRequest(post);
}
}
}
  • AHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* @author xiaobo
* @date 2021/10/9 - 8:23
* A子类
*/
public class AHandler extends PostHandler{

@Override
public void handlerRequest(Post post) {

//获得传入的内容
String context = post.getContext();

//将传入的内容中个关键字过滤
context = context.replace("sb","**");

//将过滤后的内容存回去
post.setContext(context);

System.out.println("过滤敏感词");

//继续下一节点
this.next(post);

}
}
  • BHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* @author xiaobo
* @date 2021/10/9 - 8:25
* B子类
*/
public class BHandler extends PostHandler {

@Override
public void handlerRequest(Post post) {

//获得传入的内容
String context = post.getContext();

//将传入的内容中个关键字过滤
context = context.replace("垃圾","**");

//将过滤后的内容存回去
post.setContext(context);

//继续下一节点
this.next(post);

}
}
  • main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @author xiaobo
* @date 2021/10/9 - 8:28
*/
public class Test {

public static void main(String[] args) {

//创建A子类和B子类实例化对象
PostHandler aHandler = new AHandler();
PostHandler bHandler = new BHandler();

//将B子类放入到A子类中、使其B称为A的下一节点
aHandler.setPostHandler(bHandler);

//实例化post实体类对象
Post post = new Post();

//存入内容
post.setContext("正常内容,垃圾,sb");

System.out.println("过滤前的内容:" + post.getContext());

//将post对象放进抽象父类中
aHandler.handlerRequest(post);

System.out.println("过滤后的内容:" + post.getContext());
}
}

责任链模式结果

正确的开始、微小的长进、然后持续、嘿、我是小博、带你一起看我目之所及的世界……

-------------本文结束 感谢您的阅读-------------

本文标题:工厂模式、适配器模式、代理模式、模板方法模式、责任链模式

文章作者:小博

发布时间:2021年10月09日 - 08:59

最后更新:2021年10月09日 - 09:01

原始链接:https://codexiaobo.github.io/posts/1976879072/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。