[TOC]
创立型规划形式(简略工厂,工厂办法,笼统工厂)
工厂形式首要是用于对目标实例化的一种管理形式,一般情况下,咱们都需求运用new关键字来创立一个目标,那么咱们就需求用工厂形式来统一管理咱们目标的创立进程,把目标的创立交给该形式去处理,这样咱们就不必手动的去new目标了,工厂形式首要是将创立目标的详细进程屏蔽阻隔起来。
首先咱们先来看一个Android里边开发常用的比方SharedPreferences。这是布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".simple1.MainActivity">
<TextView
android:id="@+id/infoTv"
android:onClick="getInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
咱们一般都这么写,惯例写法
public class MainActivity extends AppCompatActivity {
TextView textView;
private SharedPreferences preferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.infoTv);
//模拟用SP存入数据
SPUtils.getInstance().putString("username", "Colin").putString("userAge", "33").commit();
}
//这儿是模拟从SP取出咱们想要的数据展现在UI上
public void getInfo(View view) {
String username = SPUtils.getInstance().getString("username", "");
String userAge = SPUtils.getInstance().getString("userAge", "");
textView.setText("username=" + username + "userAge=" + userAge);
}
}
SP东西类
public class SPUtils {
private SharedPreferences.Editor editor;
private SharedPreferences preferences;
private volatile static SPUtils mInstance;
private SPUtils() {
}
public static SPUtils getInstance() {
if (mInstance == null) {
synchronized (SPUtils.class) {
if (mInstance == null) {
mInstance = new SPUtils();
}
}
}
return mInstance;
}
public void init(Context context) {
preferences = context.getSharedPreferences("Cache", Context.MODE_PRIVATE);
editor = preferences.edit();
}
public SPUtils putString(String key, String value) {
editor.putString(key, value);
return this;
}
public void commit() {
editor.commit();
}
public String getString(String key, String defaultStr) {
return preferences.getString(key, defaultStr);
}
}
初始化
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
SPUtils.getInstance().init(this);
}
}
小结:
长处:经过SPUtils东西类来优化代码,就不必每次都在Activity里边重复写SP的代码了,做到了统一管理。
缺陷:不能灵敏改变获取数据的办法。数据如果更换成内存,或者数据库,MMKV的办法存取,那么就要将上面用到SP的当地都换掉,换成内存换成,数据库的办法存储的话,那么咱们需求改动的代码就许多。
一.简略工厂形式
其实我觉得简略工厂形式是一种书写的思维习惯,不太像实在的软件规划形式。
由上面的比方引出下面这幅图。假定咱们有4中办法操作数据,那么咱们复写4份不同的代码,那就很无语了;那么咱们能够界说一套规矩,有写入数据,取出数据的操作,然后再交给不同办法的不同目标去处理就好。
/**
界说好写入数据和读取数据的接口,类似于一种规矩,
都是咱们常见的数据类型,
*/
public interface IOHandler {
//写数据
void put(String key, String value);
void put(String key, int value);
void put(String key, long value);
void put(String key, boolean value);
void put(String key, Object value);
void put(String key, double value);
void put(String key, float value);
//读取数据
String getString(String key);
Double getDouble(String key);
boolean getBoolean(String key);
float getFloat(String key);
int getInt(String key);
Object getObject(String key);
long getLong(String key);
}
咱们这儿搞一个SP的完成类,完成上面咱们写的接口
/**
这个类的意义是经过SP的办法对数据进行读取
**/
public class SPIOHandler implements IOHandler {
@Override
public void put(String key, String value) {
//写数据,经过上面咱们封装好的SPUtils东西类,相当于又加了一层封装
//到时分咱们直接调用SPIOHandler的put办法就行,其他都不必管了
SPUtils.getInstance().putString(key, value).commit();
}
@Override
public void put(String key, int value) {
}
@Override
public void put(String key, long value) {
}
@Override
public void put(String key, boolean value) {
}
@Override
public void put(String key, Object value) {
}
@Override
public void put(String key, double value) {
}
@Override
public void put(String key, float value) {
}
@Override
public String getString(String key) {
//获取数据也是一样
return SPUtils.getInstance().getString(key, "");
}
@Override
public Double getDouble(String key) {
return null;
}
@Override
public boolean getBoolean(String key) {
return false;
}
@Override
public float getFloat(String key) {
return 0;
}
@Override
public int getInt(String key) {
return 0;
}
@Override
public Object getObject(String key) {
return null;
}
@Override
public long getLong(String key) {
return 0;
}
}
这儿咱们模拟两种办法对数据的存取,一个是SP,一个是下面这个内存的形式。
/**
* IOHandler的完成类,运用内存的办法对数据进行写入和读取
*/
public class MemoryIOHandler implements IOHandler {
//这儿是一个缓存目标,能够对图片数据进行缓存,下次咱们取图片的时分就不必再去恳求网络了
//能够直接从缓存拿
LruCache<String, Object> mCache = new LruCache<>(10 * 1024 * 1024);
@Override
public void put(String key, String value) {
mCache.put(key, value);
}
@Override
public void put(String key, int value) {
mCache.put(key, value);
}
@Override
public void put(String key, long value) {
mCache.put(key, value);
}
@Override
public void put(String key, boolean value) {
mCache.put(key, value);
}
@Override
public void put(String key, Object value) {
mCache.put(key, value);
}
@Override
public void put(String key, double value) {
mCache.put(key, value);
}
@Override
public void put(String key, float value) {
mCache.put(key, value);
}
@Override
public String getString(String key) {
return (String) mCache.get(key);
}
@Override
public Double getDouble(String key) {
return (Double) mCache.get(key);
}
@Override
public boolean getBoolean(String key) {
return (boolean) mCache.get(key);
}
@Override
public float getFloat(String key) {
return 0;
}
@Override
public int getInt(String key) {
return 0;
}
@Override
public Object getObject(String key) {
return null;
}
@Override
public long getLong(String key) {
return 0;
}
}
在一开始咱们是这样写入数据的。(写入数据举例,读取数据亦是如此)
SPUtils.getInstance().putString("username", "Colin").putString("userAge", "33").commit();
现在咱们能够改成这样,经过咱们封装的接口引用指向对应完成的子类目标
private IOHandler ioHandler;
ioHandler = new SPIOHandler();
ioHandler.put("username", "Colin");
ioHandler.put("userAge", "33");
咱们这儿切换也是十分便利的,前面说的咱们获取数据也有或许用其他办法获取,比方上面说的内存办法,此刻咱们能够直接切换完成类即可。
ioHandler = new MemoryIOHandler();
ioHandler.put("username", "Colin");
ioHandler.put("userAge", "33");
这种写法:
长处:用不同的办法去写入读取数据都不需求重视它的详细完成,咱们只需求切换对应的完成类就能够完成相同的逻辑。
缺陷:在Activity上需求运用到这个类的时分都要手动去new目标。
引出简略工厂形式
从上面的代码能够看出,咱们的UI界面每次都要在用到的当地new目标,对创立目标的依靠性太强,为了削减Activity对创立目标的依靠,咱们界说一个工厂类,咱们需求什么目标就传入对应的类型匹配即可。
/**
* IOHandler工厂,协助咱们创立详细的完成了IOHandler的类
*/
public class IOHandlerFactory {
public enum IOType {
PREFERENCES, MEMORY
}
//创立详细的IOHandler子类目标
public static IOHandler createIOHandle(IOType ioType) {
IOHandler ioHandler = null;
switch (ioType) {
case PREFERENCES:
ioHandler = new SPIOHandler();
break;
case MEMORY:
ioHandler = new MemoryIOHandler();
break;
}
return ioHandler;
}
}
来看下咱们调用:
跟咱们上面的其实迥然不同,都是获取对应的目标,可是这儿的长处是躲藏了目标的创立,解除了Activity和new实例的耦合,把创立目标的动作交给了咱们界说好的工厂类,这便是简略工厂形式。
ioHandler = IOHandlerFactory.createIOHandle(IOHandlerFactory.IOType.PREFERENCES);
ioHandler.put("username", "Colin");
ioHandler.put("userAge", "33");
简略总结
长处:咱们能够对创立的目标进行一些 “加工” ,并且调用方并不知道,由于工厂躲藏了这些细节,没有工厂的话,那咱们就得自己在UI上写这些代码
缺陷:每次添加子类或者删去子类目标的创立都需求翻开这简略工厂类来进行修正,违反了咱们的开闭准则,咱们尽量不要修正已封装好的基类,简略来说便是不便利代码扩展,要修正的当地太多。
二.工厂办法形式
依据上面的前提咱们优化一下代码,尽量保证面向接口编程。工厂办法形式是对简略工厂形式进一步的解耦,在工厂办法形式中是一个子类对应一个工厂类,而这些工厂类都完成于一个笼统接口IOFactory,这样就避免了简略工厂形式,代码都在一个工厂类里边,拆分成了一个个的工厂小类,用到哪个类就运用它对应的工厂类就行。
//界说工厂出产IOHandle需求遵守的规矩
public interface IOFactory {
IOHandler createIOHandler();
}
//SP的目标创立进程咱们放到一个工厂类里边
public class SPIOFactory implements IOFactory {
@Override
public IOHandler createIOHandler() {
return new SPIOHandler();
}
}
//内存办法的目标也是如此
public class MemoryIOFactory implements IOFactory {
@Override
public IOHandler createIOHandler() {
return new MemoryIOHandler();
}
}
对比一下调用办法:
private IOHandler ioHandler;
/**这儿咱们经过实例化对应的SP工厂类,再经过工厂ioFactory目标调用createIOHandler回来的便是咱们所需求的
IOHandler的子类的目标*/
IOFactory ioFactory = new SPIOFactory();
//IOFactory ioFactory = new MemoryIOFactory();当咱们需求某个类的目标时分,直接替换它所对应的小工厂类即可
ioHandler = ioFactory.createIOHandler();
ioHandler.put("username", "Colin");
ioHandler.put("userAge", "33");
长处:各个不同功用的实例目标的创立代码,也没有耦合在同一个工厂类里,而是笼统了一个工厂接口作为扩展点,这也是工厂办法形式对简略工厂形式解耦的一个表现。
缺陷:工厂办法形式的缺陷是每添加一个java类,就需求添加一个对应的工厂类,当咱们的类许多的时分,那么对应的工厂类也会许多。
三.笼统工厂形式
依据工厂办法的进一步优化的办法。
public class IOHandlerFactory {
//协助咱们创立目标经过传入的.class判断咱们需求的类进而创立对应的目标
public static <T extends IOHandler> IOHandler createIOHandle(Class<T> tClass) {
IOHandler ioHandler = null;
try {
ioHandler = tClass.newInstance();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return ioHandler;
}
//这儿举个SP比方
public static IOHandler createSharePreferences() {
return createIOHandle(SPIOHandler.class);
}
//...往下咱们需求什么类的目标,就封装一个它对应的办法,外部直接调用即可。
}
调用:
private IOHandler ioHandler;
ioHandler = IOHandlerFactory.createSharePreferences();
ioHandler.put("username", "Colin");
ioHandler.put("userAge", "33");
长处:比较于工厂办法形式不必一直创立新的对应的类的小工厂,扩展性更加,不会使代码越来越多,越杂乱,把出产目标的进程笼统化,这样就能够和业务逻辑解耦,如果有新扩展,能够在IOHandlerFactory中添加对应的办法。
缺陷:暂无。
Android源码中用到的工厂形式举例
一.BitmapFactory 源码工厂形式详解(简略工厂)
咱们先看一下BitmapFactory 这个类里边具有的一些办法,快捷键ctrl+F12可查看当时类里边的办法,看到这咱们就知道首要都是一些解码操作的办法,终究都是经过解析传入的图片的资源,终究转化成Bitmap目标。
接下来咱们看一下BitmapFactory 生成 Bitmap的调用的进程吧,一开始咱们调用的是这个办法传入资源的途径,然后往下走decodeFile(pathName, null);终究回来的是Bitmap,所以咱们往下走bm = decodeStream(stream, null, opts);
public static Bitmap decodeFile(String pathName) {
return decodeFile(pathName, null);
}
public static Bitmap decodeFile(String pathName, Options opts) {
validate(opts);
Bitmap bm = null;
InputStream stream = null;
try {
stream = new FileInputStream(pathName);
bm = decodeStream(stream, null, opts);
} catch (Exception e) {
/* do nothing.
If the exception happened on open, bm will be null.
*/
Log.e("BitmapFactory", "Unable to decode stream: " + e);
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// do nothing here
}
}
}
return bm;
}
进到decodeStream(stream, null, opts);办法里边看看,
@Nullable
public static Bitmap decodeStream(@Nullable InputStream is, @Nullable Rect outPadding,
@Nullable Options opts) {
// we don't throw in this case, thus allowing the caller to only check
// the cache, and not force the image to be decoded.
if (is == null) {
return null;
}
validate(opts);
Bitmap bm = null;
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
try {
if (is instanceof AssetManager.AssetInputStream) {
final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
bm = nativeDecodeAsset(asset, outPadding, opts, Options.nativeInBitmap(opts),
Options.nativeColorSpace(opts));
} else {
bm = decodeStreamInternal(is, outPadding, opts);
}
if (bm == null && opts != null && opts.inBitmap != null) {
throw new IllegalArgumentException("Problem decoding into existing bitmap");
}
setDensityFromOptions(bm, opts);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
}
return bm;
}
咱们看看这行代码,很明显它的经过JNI调用了底层的办法来进行解码,这儿咱们暂时不深究,终究它是回来一个Bitmap目标,
bm = nativeDecodeAsset(asset, outPadding, opts, Options.nativeInBitmap(opts),
Options.nativeColorSpace(opts));
生成Bitmap的大致的流程咱们都过了一边,其实咱们不必深究太多源码,咱们调用者只需求知道咱们传入图片的途径会回来一个Bitmap就行,而正是由于它运用了工厂形式,把这些细节都屏蔽了,所以咱们不必操心,甭管它生成的,我给你一个图片途径你给我生成一个 Bitmap 就好了。
提到这儿,或许咱们还不理解这玩玩意儿哪里表现工厂形式了,不慌,接下来持续剖析。BitmapFactory,经过传入不同的条件,得到相同的bitmap,回顾咱们上面说的简略工厂形式,是不是传入咱们想要的type类型就能够从工厂里边出产出咱们想要的实例目标,这是不是很类似呢。看下面的代码,终究都是为的是生成Bitmap目标可是咱们能够传入不同的条件参数进行获取,咱们都能够理解为在同一个工厂用不同的条件去出产相同的产品。
简略工厂形式的本质是由一个工厂类依据传入的参数,动态决定应该创立哪一个产品类(这些产品类继承自一个父类或接口)的实例
可是这样的简略工厂形式缺陷很明显,比方咱们有别的一种出产Bitmap的办法出现的时分,咱们就要去修正工厂类BitmapFactory,违反了开闭准则,咱们尽量不要去修正咱们的基类。
/**
* Creates Bitmap objects from various sources, including files, streams,
* and byte-arrays.
*/
public class BitmapFactory {
public static Bitmap decodeFile(String pathName, Options opts){......}
public static Bitmap decodeFile(String pathName) {......}
public static Bitmap decodeResourceStream(Resources res, TypedValue value,
InputStream is, Rect pad, Options opts) {......}
public static Bitmap decodeResource(Resources res, int id, Options opts) {......}
public static Bitmap decodeResource(Resources res, int id) {......}
public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts){......}
public static Bitmap decodeByteArray(byte[] data, int offset, int length) {......}
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {......}
public static Bitmap decodeStream(InputStream is) {......}
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {......}
public static Bitmap decodeFileDescriptor(FileDescriptor fd) {......}
}
二.Retrofit的工厂形式(笼统工厂)
由于Retrofit触及的源码比较庞大,在这儿只是举例介绍这个结构里边所应用到的工厂形式吗,下面是Retrofit的简略运用办法,咱们简略剖析一下GsonConverterFactory.create()和RxJavaCallAdapterFactory.create(),很明显从姓名上就能够看出是工厂形式。两者创立的形式都是笼统工厂形式,迥然不同,拿GsonConverterFactory.create()举例说明,请看下文。
new Retrofit.Builder()
.baseUrl("https://www.baidu.com/")
.client(new OkHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
咱们点击create()办法来到源码中,
public final class GsonConverterFactory extends Converter.Factory {
public static GsonConverterFactory create() {
return create(new Gson());
}
点击这儿进入到笼统工厂中看看,
Converter.Factory
能够看到Factory是abstract润饰,那么就能够明确它是运用的是笼统工厂。
//这是个数据的转化器的笼统类,泛型F表示输入的参数,T是输出的参数,也便是咱们转化完成之后的数据类型。
public interface Converter<F, T> {
//这是一个接口里边的办法,首要做的是转化的操作
@Nullable T convert(F value) throws IOException;
abstract class Factory {
//恳求响应
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;
}
//该工厂用于转化字符串类型的转化器,
public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
protected static Type getParameterUpperBound(int index, ParameterizedType type) {
return Utils.getParameterUpperBound(index, type);
}
protected static Class<?> getRawType(Type type) {
return Utils.getRawType(type);
}
}
}
接下来咱们看看它的详细完成类,经过封装一个 create 办法,来创立工厂目标,外部调用者就不需求联系工厂目标是怎么创立的。再一个经过responseBodyConverter、requestBodyConverter 办法分别创立了恳求响应和恳求建议这两种产品的目标。
//这儿便是创立了它的工厂目标
public static GsonConverterFactory create() {
return create(new Gson());
}
//这儿是经过封装了一个create办法传入一个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);
}
总结:工厂形式属于创立型的规划形式,首要规划的中心在创立咱们的目标上面,使得创立目标和第三方调用者相阻隔,这样目标的提供方和调用方的耦合联系就会减小。