java接口和实现分离_谈谈Java接口与实现的分离以及隐藏实现

一. what ?

对于一个框架来说, 用户只需要知道这个框架的关键组件和接口就行了, 不要对外公布太多的细节. 因为用户看到的东西太多反而导致了迷惑. 对于用户来说, 只要调用一个方法就帮我完成我想要的那些复杂功能, 这样最好不过了. 接口和实现分开或者说只对外公布用户要使用的接口, 而其实现则对用户隐藏起来. 这是一个框架应该做的事情, 也是Java的一个重要特性 ------ 封装. 简单的来说接口和实现的分离就是把接口已实现分开, 尽量减少两者之间的依赖, 以方便移植和修改. 那么隐藏实现又怎么说呢? 前面已经说了, 一个框架要做到的是尽量不要公布实现, 只公布接口. 因此就需要对实现进行封装并隐藏. 这样说有些抽象, 你可能有些不知所云. 下面我将说说为什要进行接口和实现的分离、对实现方式进行隐藏 以及怎么实现它们.

二. why ?

在《在Android上使用SPI机制》一文中已经说过关于接口和实现的分离和动态更换实现的问题, 但是接口的实现并未对外隐藏, 用户可以直接调用接口的实现, 而不使用接口. 这样编写的代码并不是面向接口编程, 而是硬编码. 如果要更换实现, 这将非常麻烦. 为了不让用户看到实现, 只需要将实现类变成包私有的类用反射初始化. 分离接口和实现以及隐藏实现细节好处很多, 下面列举几个:

面向接口编程, 方便更换实现.

隐藏实现细节, 减少对外接口和类.

减少接口和实现直接的相互依赖.

封装, 高内聚.

......

三. how ?

下面看看如何实现:

(1) 定义接口

public interface ImageLoader {

/**

* 初始化ImageLoader

* @param appContext ApplicatonContext

*/

void init(@NonNull Context appContext);

/**

* 展示图片

* @param targetView

* @param uri

* @param listener

*/

void displayImage(@NonNull ImageView targetView, @NonNull Uri uri, @Nullable LoadListener listener);

/**

* 取消图片展示

* @param targetView

*/

void cancelDisplay(ImageView targetView);

/**

* 销毁ImageLoader, 回收资源

*/

void destroy();

}

(2) 实现接口 (实现类要定义成包私有的, 即没有修饰符)

FrescoImageLoader.java文件:

class FrescoImageLoader implements ImageLoader {

private Context mAppContext;

@Override

public void init(@NonNull Context appContext) {

if(Fresco.hasBeenInitialized()) return;

// hold appContext

mAppContext = appContext;

// init fresco

OkHttpClient client = new OkHttpClient.Builder()

.addNetworkInterceptor(chain -> {

DevUtil.d("ImageLoader", "request-url: " + chain.request().url().toString());

return chain.proceed(chain.request());

})

.build();

ImagePipelineConfig config = OkHttpImagePipelineConfigFactory

.newBuilder(appContext, client)

.build();

Fresco.initialize(appContext, config);

}

@Override

public void displayImage(@NonNull ImageView targetView, @NonNull Uri uri, @Nullable LoadListener listener) {

// Fresco

if (targetView instanceof DraweeView) {

DraweeView realView = (DraweeView) targetView;

realView.setController(getDraweeController(realView, uri, listener));

return;

}

// Generic ImageView

targetView.setImageURI(uri);

}

private DraweeController getDraweeController(DraweeView targetView, Uri uri, LoadListener listener) {

DraweeController controller = Fresco.newDraweeControllerBuilder()

.setOldController(targetView.getController())

.setUri(uri)

.setControllerListener(listener == null ? null : new BaseControllerListener() {

@Override

public void onFailure(String id, Throwable throwable) {

super.onFailure(id, throwable);

if (listener != null) {

listener.onFailed(targetView);

}

}

@Override

public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) {

super.onFinalImageSet(id, imageInfo, animatable);

if (imageInfo instanceof CloseableBitmap) {

CloseableBitmap image = (CloseableBitmap) imageInfo;

Bitmap resultBitmap = image.getUnderlyingBitmap();

if (listener != null) {

listener.onSuccess(targetView, resultBitmap);

}

}

}

})

.build();

return controller;

}

@Override

public void cancelDisplay(ImageView targetView) {

}

@Override

public void destroy() {

Fresco.shutDown();

}

}

UILImageLoader.java文件

class UILImageLoader implements ImageLoader {

private com.nostra13.universalimageloader.core.ImageLoader mImpl;

@Override

public void init(@NonNull Context appContext) {

mImpl = com.nostra13.universalimageloader.core.ImageLoader.getInstance();

}

@Override

public void displayImage(@NonNull ImageView targetView, @NonNull Uri uri, @Nullable LoadListener listener) {

com.nostra13.universalimageloader.core.ImageLoader.getInstance().displayImage(uri.toString(), targetView);

}

@Override

public void cancelDisplay(ImageView targetView) {

mImpl.cancelDisplayTask(targetView);

}

@Override

public void destroy() {

mImpl.destroy();

}

}

其余省略................

(3) 为实现类定义初始化工厂类

class ImageLoaderFactory {

private static ImageLoader mImageLoader;

private ImageLoaderFactory() {

//no instance

}

/**

* @return

*/

public static ImageLoader createImageLoader(String implClass) {

if (mImageLoader == null) {

mImageLoader = createImageLoaderWithClassName(implClass);

}

return mImageLoader;

}

/**

* 此处使用类反射, 所有implClass都不能混淆, 类名必须keep: {@code -keep class a.b.c.ImplClass}

* @param implClass 实现类有: FrescoImageLoader, GlideImageLoader, PicassoImageLoader, UILImageLoader

* @return

*/

private static ImageLoader createImageLoaderWithClassName(String implClass) {

try {

Class klass = Class.forName(implClass);

Constructor constructor = klass.getDeclaredConstructor();

if (constructor == null) {

throw new RuntimeException(implClass + " 的实现类必须有一个无参构造方法 !");

}

boolean isAccessible = constructor.isAccessible();

constructor.setAccessible(true);

Object obj = constructor.newInstance();

constructor.setAccessible(isAccessible);

if ( !(obj instanceof ImageLoader) ) {

throw new RuntimeException(implClass + "必须实现" + ImageLoader.class.getName() + "接口");

}

ImageLoader imageLoader = (ImageLoader) obj;

return imageLoader;

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (NoSuchMethodException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

} catch (InstantiationException e) {

e.printStackTrace();

} catch (InvocationTargetException e) {

e.printStackTrace();

}

return null;

}

}

(4) 要使用ImageLoader, 直接用工厂类创建即可, 我们只需要知道实现类的名称, 其它细节都不需要知道. 实现类都是高度内聚, 外部完全不知道其内部的逻辑. 外部只需要调用接口的方法实现业务逻辑即可. 下面是ImageLoader及其相关实现的整体结构:

ede85f9f60b7

接口与实现分离, 隐藏实现.png

(5) 下面是使用工厂类创建ImageLoader实现的代码

import android.content.Context;

public final class ImageManager {

private static String TAG = "ImageManager";

private static ImageLoader sImageLoader;

private ImageManager() {

//no instance

}

public static void init(Context appContext) {

if (sImageLoader != null) {

throw new IllegalStateException(TAG + " already initalized");

}

sImageLoader = ImageLoaderFactory.createImageLoader("com.stone.app.manager.imageloader.internal.FrescoImageLoader");

sImageLoader.init(appContext);

}

public static ImageLoader getImageLoader() {

return sImageLoader;

}

}

总结:

将接口和实现类分离, 接口和实现分别放在单独的包中, 并且实现类定义为包私有的 (即类没有修饰符).

定义工厂类, 使用反射初始化实现类.

注意混淆的时候不能混淆实现类的类名, 因为其初始化使用了反射.

有一个设计模式也是分离接口和实现并使其各自独立变化, 那就是桥接模式. 具体可以参考Android中的Window和Context的设计. 关于动态扩展和分离接口和实现, 可以参考《在Android上使用SPI机制》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值