谈谈Java接口与实现的分离以及隐藏实现
2017-03-03 00:00
232 查看
学习Java的同学注意了!!!
学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:183993990 我们一起学Java!
一. what ?
对于一个框架来说, 用户只需要知道这个框架的关键组件和接口就行了, 不要对外公布太多的细节. 因为用户看到的东西太多反而导致了迷惑. 对于用户来说, 只要调用一个方法就帮我完成我想要的那些复杂功能, 这样最好不过了. 接口和实现分开或者说只对外公布用户要使用的接口, 而其实现则对用户隐藏起来. 这是一个框架应该做的事情, 也是Java的一个重要特性 ------ 封装. 简单的来说接口和实现的分离就是把接口已实现分开, 尽量减少两者之间的依赖, 以方便移植和修改. 那么隐藏实现又怎么说呢? 前面已经说了, 一个框架要做到的是尽量不要公布实现, 只公布接口. 因此就需要对实现进行封装并隐藏. 这样说有些抽象, 你可能有些不知所云. 下面我将说说为什要进行
二. why ?
在《在Android上使用SPI机制》一文中已经说过关于接口和实现的分离和动态更换实现的问题, 但是接口的实现并未对外隐藏, 用户可以直接调用接口的实现, 而不使用接口. 这样编写的代码并不是面向接口编程, 而是硬编码. 如果要更换实现, 这将非常麻烦. 为了不让用户看到实现, 只需要将实现类变成包私有的类用反射初始化. 分离接口和实现以及隐藏实现细节好处很多, 下面列举几个:
面向接口编程, 方便更换实现.
隐藏实现细节, 减少对外接口和类.
减少接口和实现直接的相互依赖.
封装, 高内聚.
......
三. how ?
下面看看如何实现:
(1) 定义接口
(2) 实现接口 (实现类要定义成包私有的, 即没有修饰符)
FrescoImageLoader.java文件:
UILImageLoader.java文件
其余省略................
(3) 为实现类定义初始化工厂类
(4) 要使用ImageLoader, 直接用工厂类创建即可, 我们只需要知道实现类的名称, 其它细节都不需要知道. 实现类都是高度内聚, 外部完全不知道其内部的逻辑. 外部只需要调用接口的方法实现业务逻辑即可. 下面是ImageLoader及其相关实现的整体结构:
接口与实现分离, 隐藏实现.png
(5) 下面是使用工厂类创建ImageLoader实现的代码
总结:
将接口和实现类分离, 接口和实现分别放在单独的包中, 并且实现类定义为包私有的 (即类没有修饰符).
定义工厂类, 使用反射初始化实现类.
注意混淆的时候不能混淆实现类的类名, 因为其初始化使用了反射.
学习Java的同学注意了!!!
学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:183993990 我们一起学Java!
学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:183993990 我们一起学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<ImageInfo>() { @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及其相关实现的整体结构:
接口与实现分离, 隐藏实现.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; } }
总结:
将接口和实现类分离, 接口和实现分别放在单独的包中, 并且实现类定义为包私有的 (即类没有修饰符).
定义工厂类, 使用反射初始化实现类.
注意混淆的时候不能混淆实现类的类名, 因为其初始化使用了反射.
学习Java的同学注意了!!!
学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:183993990 我们一起学Java!
相关文章推荐
- Java常用类集接口以及实现方式总结
- Java微信公众平台开发(九)——关键字回复以及客服接口实现(该公众号暂时无法提供服务解决方案)
- 今天的问题:一个简单的例子,请帮我解开“接口实现Java‘隐藏实现细目’”的迷惑。
- Java微信公众平台开发(九)——关键字回复以及客服接口实现(该公众号暂时无法提供服务解决方案)
- Android(java)学习笔记229:服务(service)之绑定服务调用服务里面的方法 (采用接口隐藏代码内部实现)
- Java中list接口的方法和list接口的实现类LinkedList,Vecotr以及队列和栈结构
- java 内部类与接口结合实现隐藏具体的方法的实现
- Java 接口示例以及实现
- Java微信公众平台开发(9) 关键字回复以及客服接口实现
- Java中方法多态以及多接口实现
- 2018/01/07JAVA 基础 / 接口与继承:重写【子类继承并覆盖父类的对象方法】、隐藏【子类继承并覆盖父类的类方法】与实现类实现接口方法の区别
- Java学习:增量接口的设计以及实现
- java多线程实现(thread和runnable接口两种方式以及其区别)
- java中的线程-继承thread-实现runnable接口以及线程同步
- java多线程(继承Thread以及实现Runnable接口)
- Java集合源码学习(13)_Queue接口以及基础实现AbstractQueue
- 从信息隐藏的一个需求看C++接口与实现的分离
- java中的接口的定义以及实现关系
- web打印,巧妙实现隐藏不想打印的页面元素,以及页眉和页脚
- 用java读写ini配置文件的原因以及实现 示例