设计模式之工厂方法模式
工厂方法模式,顾名思义,调用工厂里的方法的模式。
一、定义
定义一个用于创建对象的接口,让子类决定实例化哪个类。主要用于生成复杂对象的地方。
二、UML类图
工厂模式类图.png三、解读类图
1.抽象产品类--类图上的Product接口
public abstract class Product {
public abstract void product();
}
2.具体的产品类-类图上的ConcreteProduct
public class ConcreteProduct extends Product{
@Override
public void product() {
System.out.println("生产出一个产品");
}
}
3.抽象的工厂类-类图上的Factory接口
public abstract class Factory {
public abstract Product create();
}
4.具体的工厂类-类图上的ConcreteFactory
public class ConcreteFactory extends Factory{
@Override
public Product create() {
return new ConcreteProduct();
}
}
5.使用工厂类生产产品
public class Client {
public static void main(String[] args) {
Factory factory=new ConcreteFactory();
Product p=factory.create();
p.product();
}
}
输出结果:
生产出一个产品
以上是基本的使用方法。在你需要生产对象的时候可以直接使用具体工厂类的create方法产出你要的对象。在Client代码中是看不到具体的产品类ConcreteProduct,因为我们不需要知道内部的实现细节,不需要知道是怎么生产,这就是使用接口(抽象类)的好处,只需要调用暴露的接口方法就行了。
与此同时代码的可拓展性也变强了。比如我想生产另外的对象,那么只需要换一个工厂就行了。
比如换成2号工厂
Factory factory=new ConcreteFactory2();
2号工厂用于生产2号产品
public class ConcreteFactory2 extends Factory{
@Override
public Product create() {
return new ConcreteProduct2();
}
}
public class ConcreteProduct2 extends Product{
@Override
public void product() {
System.out.println("生产出一个2号产品");
}
}
四、实现工厂方法的第二种方式
有一种方式也比较常用,使用反射的方式去获取产品的对象
1.定义一个工厂抽象类
public abstract class ReflectFactory {
public abstract<T extends Product> T createProduct(Class<T> clz);
}
2.具体工厂类
public class ReflectConcreteFactory extends ReflectFactory{
@Override
public <T extends Product> T createProduct(Class<T> clz) {
Product p=null;
try {
p=(Product) Class.forName(clz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) p;
}
}
3.使用工厂类生产
public class ReflectClient {
public static void main(String[] args) {
ReflectFactory factory=new ReflectConcreteFactory();
Product p=factory.createProduct(ConcreteProduct.class);
p.product();
}
}
这种方法和基本使用方法不同的是我在使用工厂生产对象的时候,要传入我想生产的那个具体的对象的类。表面看上去还不如之前的方法好,代码变复杂。
但是这有个好处。只要这个具体产品是Product的子类,这些产品都可通过这一个工厂去实现。而之前的方式只能一个工厂对应一个具体的产品。
简单的举个栗子:基本方法就是-可口可乐只能生产可乐,要生产芬达的饮料要芬达工厂去生产。反射的方法-可口可乐和芬达都是刺激性饮料,现在有个工厂专门生产刺激性饮料,但是你要什么可乐还是芬达饮料,你要先和工厂说,它会根据你的要求给你(也就对应传入的参数)
总的来说,两种方式各有所长,在合适的时候选择合适的,才是最好的。
五、Retrofit源码上工厂方法的应用
1.RequestFactory
(1)调用RequestFactory的create方法生产Request的对象。这个Request就是封装了网络请求信息的对象
class RequestFactory {
okhttp3.Request create(Object[] args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
headers, contentType, hasBody, isFormEncoded, isMultipart);
List<Object> argumentList = new ArrayList<>(argumentCount);
for (int p = 0; p < argumentCount; p++) {
argumentList.add(args[p]);
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.get() //建造者模式得到Request的对象
.tag(Invocation.class, new Invocation(method, argumentList))
.build();
}
}
(2)在OkHttpCall中调用这个工厂生产对象
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
2.Converter.Factory
在创建retrofit对象的时候,经常会添加了一个转换工厂,比如下面代码添加的GsonConverterFactory,是用于把数据转换成gson数据类型
Retrofit retrofit=new Retrofit.Builder()
.baseUrl("http://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
那么它是怎么使用工厂方法模式的呢?
(1)抽象工厂类
public interface Converter<F, T> {
T convert(F value) throws IOException;
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
Annotation[] annotations, Retrofit retrofit) {
return null;
}、
public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
}
(2)具体的工厂类
public final class GsonConverterFactory extends Converter.Factory {
public static GsonConverterFactory create() {
return create(new Gson());
}
public static GsonConverterFactory create(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
return new GsonConverterFactory(gson);
}
//将响应体类型数据转换成?类型数据
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
//将?类型的数据转换成请求体类型的数据
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonRequestBodyConverter<>(gson, adapter);
}
}
咋一看看不出它生产的是什么对象。其实在重写的两个方法里,它通过不同的方法生产不同的对象,深入其中的一个requestBodyConverter方法去看
class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
//...
public RequestBody convert(T value) throws IOException {
Buffer buffer = new Buffer();
Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
JsonWriter jsonWriter = gson.newJsonWriter(writer);
adapter.write(jsonWriter, value);
jsonWriter.close();
return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
}
}
最后一行可以看到它返回了一个RequestBody的对象。这也符合了它的目的,就是将现有的类型的数据转换成请求体RequestBody类型的数据。
六、总结
总说看源码不能过于拘泥于细节实现,否则会看得满脸懵逼。但当你真正看的时候,特别是对应初学者,很难区分什么是细节实现,我刚在看Retrofit这部分源码的时候,看到这部分代码,emmn......这到底在干啥。最后我发现原因就是作者使用了大量的设计模式,在你不懂这些设计模式的时候,可不就是看天书一样。但当我去了解一些常用的设计模式基本实现之后,在去看这些代码,莫名其妙的就知道哪些是我不用关心的,比如上面的大多数代码,就为了创建一个对象,我只需要知道创建的对象是什么就行了。
额外说一点
Retrofit是一个很优秀的源码框架。里面用了很多设计模式,很适合花时间去学习。既可以学到Retrofit的源码实现,又能学到多种设计模式,想想就血赚啊~~
当然之后也会有在Retrofit上使用的其他设计模式的文章。