一般而言,工厂模式分为3种,简单工厂模式,工厂方法模式,抽象工厂模式。这三种工厂模式逐层深入吧。
一,从springWeb.jar包使用抽象工厂模式的一个例子聊起
之前对spring各种痴迷,所以在需要发送http请求时,用了spring自带的http客户端,上代码:
import java.io.InputStream; import java.net.URI; import java.nio.charset.Charset; import org.springframework.http.HttpMethod; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.util.StreamUtils; public class Client { public static void main(String[] args) throws Exception{ URI uri = new URI("https://www.cnblogs.com/"); //新建一个抽象ClientHttpRequest工厂 ClientHttpRequestFactory chrf = new SimpleClientHttpRequestFactory(); //生产一个抽象ClientHttpRequest ClientHttpRequest req = chrf.createRequest(uri, HttpMethod.GET); //ClientHttpRequest执行execute方法 ClientHttpResponse res = req.execute(); InputStream is = res.getBody(); String strBody = StreamUtils.copyToString(is, Charset.forName("UTF-8")); is.close(); System.out.println(strBody); } }
上UML图,首先是工厂类:
产品类,因为产品类有点小复杂,先看产品类接口的定义,看这个产品类的定义,你会觉得spring搞那么复杂干嘛,为啥不直接开一个统一的接口HttpRequest,
把httpOutputMessage里面的getBody放进去就好了。
其实仔细想想,spring之所以这么设计,是遵循“接口隔离原则”。
为啥要遵循这个原则呢?因为看完spring-web-release.jar包后你会发现,httpMessage被三个接口所extends,分别是HttpOutputMessage,HttpInputMessage,HttpRequest。
这三个接口有十多个实现类,如果并在一起,在三个接口中就需要重复写3次。
再聊聊HttpOutputMessage,HttpInputMessage,这两个接口对于springMVC来说是重中之重,是springMVC传输的载体,后面我们聊springMVC框架时还会遇到他们。
再看具体的产品实现类,笔者比较喜欢把方法也放进类图里面,所以稍微显得有点臃肿。前面我们看到,clientHttpRequest接口一共有5个接口方法需要子类去实现。
我猜想spring是这样子想的:
1,先定义几个抽象类implement那个ClientHttpRequest接口,然后在抽象类中对clientHttpRequest中做基本的实现,和之前笔者分析spring.core.io包里面想法一模一样。
这是用到设计模式中的“模版方法”模式,不过模版方法比较简单,就不单独开帖聊了。
2,5个接口方法中,在AbstractClientHttpRequest中实现了getHeaders(),getBody(),execute()3个方法,
然后还不省事地给他的子类添加了两个抽象方法getBodyInternal(HttpHeaders headers),executeInternal(HttpHeaders headers),
再悄悄地告诉你,这两个抽象方法分别有一个抽象类和两个具体实现类实现了该抽象方法,也就是我们的产品实现类的类图还没有画完,下图只是画了冰山一角而已,不过管中窥豹可见一斑,将就看看吧。
3.剩下的两个接口方法,getURI,getMethod()方法在SimpleStreamingClientHttpRequest具体的实现类中实现。
4,最后再分析下SimpleBufferingClientHttpRequest这个最底层的实现类。getURI,getMethod()是在这个SimpleBufferingClientHttpRequest底层类实现的。同时第2点提到的两个不省事的抽象方法在SimpleBufferingClientHttpRequest的父抽象类AbstractBufferingClientHttpRequest已经进行了具体的实现。
5.总结,以上几个类基本上一个接口方法对应着一个@Override,我猜想这是为了符合里氏替换原则(每个父类能用的地方,他的子类替换过去不会有任何影响)。
其实我挺期待父类override祖父类的接口方法,而后,孙子类再override父类的方法的,好像很少有这种用法。
spring用到的这种工厂模式,应该是属于最复杂的抽象工厂模式吧,继承树,产品族什么的,真的好复杂。
回到刚开始的需求,其实如果只是要发起一个简单的http请求,用工厂方法模式或者简单工厂模式就可以了吧。
二:工厂方法模式和简单工厂模式
举个栗子,我们来砍掉上述抽象工厂的产品族等等的一些为了拓展而抽象出的类和接口,类图会变成这个样子。
笔者故意比上面的类图多画了一个工厂实现类,让ClientHttpRequestFactory这个接口不会显得很鸡肋。用工厂方法模式其实已经能很大程度地增加程序的拓展性了。
将OkHttp3ClientHttpRequestFactory这个工厂具体实现类删掉,就变成了简单工厂模式的类图了。
至此,3种工厂模式已经介绍完了,如有错漏,还请各位博友批评指正。