- 设计模式解析一 工厂模式的不同
- 设计模式解析二 结构模式三剑客
- 设计模式解析三 行为模式三剑客
- 设计模式解析四 模板方法模式和外观模式
- 设计模式解析五 观察者模式和桥接模式
- 设计模式解析六 单例模式
一. 前言
作为一个程序开发者,设计模式的学习总是不变的真理,也许在程序员职业生涯的前期学习设计模式还知识死记硬背,不知道如何应用的话,那在工作一段时间以后再回过头来学习,会发现前人的总结包含着无数的智慧。
用百科的话来说,设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
一千个人有一千个哈姆雷特,每个人学习设计模式都会有自己的理解,接下来这个系列就是我对设计模式的理解,记录下来,也作为自己这段时间的学习总结。
二. 工厂模式
接下来的设计模式解析会进行一个分组的总结,把一些结构或者容易搞混的设计模式会合并在一起来进行分析,然后分析其中的不同。
java的是面向对象的编程,我们到处都在同对象打交道,自然少不了创建这样那样的对象,如何得到对象呢,比如这个代码:
Deserializer deserializer = new LocationInfoDeserializer();
有时候需要根据条件来获取不同的实现:
//解码器
Deserializer deserializer;
if (UploadMsgType.LOCATION_INFO.equals(msgType)) {
deserializer = new LocationInfoDeserializer();
} else if (UploadMsgType.MEDIA_INFO.equals(msgType)) {
deserializer = new MediaInfoDeserializer();
}
这样的确是正确的,但这存在一些问题:
- 如果实现类需要替换,我们需要去每一个
new
的地方去修改 - 如果需要根据不同的条件来创建不同的实现,这种
if
判断代码可能充斥的到处都是
回忆一下,我们编写业务代码是不是总是先定义一个Service
的interface
,然后在定义一个ServiceImpl
的实现,那么有没有想过为什么要这么做呢?
设计模式的思想告诉我们要做接口隔离,针对接口编程,而不是针对实现的编程,这样我们只要实现遵循了接口的规范,我们就可以任意的替换实现,而不必担心对上层应用造成影响。
Spring是一个伟大的框架,他为我们隐藏了很多复杂的细节,让我们的开发更加简单,实际上Spring框架中蕴含着丰富的设计模式,比如@Service
注解,它为我们实现了工厂模式的过程,而隐藏了工厂模式的细节,但总有一些时候是需要我们自己去使用设计模式的,这时候思想的积累就尤为重要了。
1. 简单工厂模式
回到解码器这段代码,试想一下,如果这段代码揉进了业务代码里面会有多糟糕,这是一个解码器,需要根据不同的消息类型来得到解码器,消息类型增加的可能性太大了!
我们应该封装这部分,封装变化这是不变的真理:
public class SimpleDeserializerFactory {
public static Deserializer produce(UploadMsgType msgType) {
Deserializer deserializer = null;
if (UploadMsgType.LOCATION_INFO.equals(msgType)) {
deserializer = new LocationInfoDeserializer();
} else if (UploadMsgType.MEDIA_INFO.equals(msgType)) {
deserializer = new MediaInfoDeserializer();
}
return deserializer;
}
}
所有需要解码器的地方只要调用这个方法就好,无论增加多少种消息类型,我们只需要在这里增加一段else if
即可,没错这就是简单工厂模式,也许这都不能称之为一个设计模式,只是一种编码习惯。
下面看类图:
2. 工厂方法模式
上面的简单工厂让我们能够把获取解码器的部分进行了封装,但上面这是一个极简单的例子,每一个解码器的实例化都只是简单的进行一次new
,但现实中总不是这样,对象和对象之间存在太多的依赖关系,一个对象需要依赖其他几个,甚至十几个对象,如果还像上面这样,那我们的代码会变成非常繁杂,大量的set
、new
:
public static Deserializer produce(UploadMsgType msgType) {
if (UploadMsgType.LOCATION.equals(msgType)) {
LocationInfoDeserializer locationInfoDeserializer = new LocationInfoDeserializer();
LocationService locationService = new LocationService();
locationInfoDeserializer.setLocationService(locationService);
return locationInfoDeserializer;
} else if (UploadMsgType.MEDIA.equals(msgType)) {
MediaInfoDeserializer mediaInfoDeserializer = new MediaInfoDeserializer();
MediaInfoService mediaInfoService = new MediaInfoService();
mediaInfoDeserializer.setMediaInfoService(mediaInfoService);
return mediaInfoDeserializer;
}
return null;
}
这上面每个解码器都只有一个依赖,看起来还好,如果有十个呢?
未来随着业务的发展,将会出现改变,如果位置解码器出现改变,增加了依赖,我们将会修改这部分的代码,但面向对象的基本原则告诉我们一个类应当只有一个发生变化的原因。但上面的类出现了两个:
- 位置解码器发生变化
- 多媒体解码器发生变化
那么我们能不能更进一步,把上面的变化继续封装起来呢?
设计模式教我们要面向接口编程,那么我们能不能设计一个接口,接口返回解码器,然后设计不同的实现来返回不同的解码器呢?
public interface DeserializerFactory {
Deserializer create();
}
public class LocationInfoDeserializerFactory implements DeserializerFactory {
@Override
public Deserializer create() {
LocationInfoDeserializer deserializer = new LocationInfoDeserializer();
LocationService service = new LocationService();
deserializer.setLocationService(service);
return deserializer;
}
}
public class MediaInfoDeserializerFactory implements DeserializerFactory {
@Override
public Deserializer create() {
MediaInfoDeserializer deserializer = new MediaInfoDeserializer();
MediaInfoService service = new MediaInfoService();
deserializer.setMediaInfoService(service);
return deserializer;
}
}
你看,现在变化的地方都被我们封装了起来,再回到刚才的方法,让我们对这个方法再进行一些改造:
public class SimpleDeserializerFactory {
public static DeserializerFactory produce(UploadMsgType msgType) {
DeserializerFactory factory = null;
if (UploadMsgType.LOCATION.equals(msgType)) {
factory = new LocationInfoDeserializerFactory();
} else if (UploadMsgType.MEDIA.equals(msgType)) {
factory = new MediaInfoDeserializerFactory();
}
return factory;
}
public static void main(String[] args) {
UploadMsgType msgType = UploadMsgType.LOCATION;
DeserializerFactory factory = SimpleDeserializerFactory.produce(msgType);
Deserializer deserializer = factory.create();
}
}
你看这一次,这个类又只剩下了一个改变的原因,那就是出现新增消息类型的时候。
这就是工厂方法模式,下面看类图:
定义
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。
这也是著名的依赖倒置原则,要依赖抽象,而不是依赖具体的实现类。
3. 抽象工厂模式
接下来,需求又来了,客户希望我们不仅仅能够进行解码,还希望我们能够对解码后的内容进行存储和一些特殊的处理,而不同的内容进行存储的方式也是不同的,所以我们抽象一下,可以知道,不论是位置还是多媒体数据我们都需要一个解码器,一个仓库服务还有一个处理器,这是他们的相同之处,不同的只是实现。
首先我们增加一个仓库服务的接口:
public interface StorageService {
void save(Object o);
}
然后增加一个存储位置和存储多媒体的仓库服务实现:
public class MediaStorageService implements StorageService {
@Override
public void save(Object o) {
System.out.println("save the media");
}
}
public class LocationStorageService implements StorageService {
@Override
public void save(Object o) {
System.out.println("save the location");
}
}
接下来创建一个消息处理工厂的接口:
public interface MsgProcessFactory {
Deserializer createDeserializer();
StorageService createStorage();
}
针对不同的消息类型,创建不同的工厂实现:
public class MediaMsgProcessFactory implements MsgProcessFactory {
@Override
public Deserializer createDeserializer() {
return new MediaInfoDeserializer();
}
@Override
public StorageService createStorage() {
return new MediaStorageService();
}
}
public class LocationMsgProcessFactory implements MsgProcessFactory {
@Override
public Deserializer createDeserializer() {
return new LocationInfoDeserializer();
}
@Override
public StorageService createStorage() {
return new LocationStorageService();
}
}
我们还需要一个消息处理器,来整合这些消息处理的过程:
public interface MsgProcess {
void process(byte[] bytes);
}
针对不同消息的不同处理过程:
public class MediaMsgProcess implements MsgProcess {
private MsgProcessFactory msgProcessFactory;
public MediaMsgProcess(MsgProcessFactory msgProcessFactory) {
this.msgProcessFactory = msgProcessFactory;
}
@Override
public void process(byte[] bytes) {
Deserializer deserializer = msgProcessFactory.createDeserializer();
Object o = deserializer.decode(bytes);
StorageService storageService = msgProcessFactory.createStorage();
storageService.save(o);
// TODO 其他针对多媒体的业务逻辑
}
}
public class LocationMsgProcess implements MsgProcess {
private MsgProcessFactory msgProcessFactory;
public LocationMsgProcess(MsgProcessFactory msgProcessFactory) {
this.msgProcessFactory = msgProcessFactory;
}
@Override
public void process(byte[] bytes) {
Deserializer deserializer = msgProcessFactory.createDeserializer();
Object o = deserializer.decode(bytes);
StorageService storageService = msgProcessFactory.createStorage();
storageService.save(o);
// TODO 其他针对位置的业务逻辑
}
}
然后让我们运行他:
public static void main(String[] args) {
byte[] bytes = new byte[1024];
MsgProcessFactory factory = new LocationMsgProcessFactory();
MsgProcess process = new LocationMsgProcess(factory);
process.process(bytes);
factory = new MediaMsgProcessFactory();
process = new MediaMsgProcess(factory);
process.process(bytes);
}
输出:
save the location
save the media
定义
抽象工厂模式的重点在于提供一个接口,用于创建相关或依赖对象的家族,而不需要明确具体的实现。
在这里,我们的解码器和仓库服务就是相关的对象家族,因为多媒体的解码器应该和多媒体仓库服务一起使用,如果用错了就会出现致命的后果。而无论什么消息需要处理的时候都需要一个解码器和一个仓库服务,所以这才是抽象,实际应用中,抽象自然没有这么简单,是需要仔细思考的。
抽象工厂模式和工厂方法模式的不同
为什么抽象工厂模式和工厂方法模式相比只是在一个工厂接口里多了几个方法却被称为抽象的原因,在我理解,抽象描述的就是抽象出对不同的业务逻辑处理中相同的部分,如果用肉包子来举例,我们可以试着抽象,制作一个鲜肉包子、酱肉包子、牛肉包子的做法是不同的,但是他们相同的地方在哪呢?
- 都需要面粉
- 都需要肉(尽管肉可能是不同的)
- 都需要调料(调料可能也是不同的)
这就是抽象。
抽象工厂模式的类图就不给了,大家试着自己画一下吧。