代码实录一
日常编程中,在阅读一些开源库的时候,总会看到一些比较优秀的代码风格/设计,雷布斯说:以前还是程序员的时候,自己不会写诗,但是同事读他的代码的时候,感觉写的像诗一样优雅。。
不积跬步,无以至千里。
以下,这些就当做是一个笔记,一份积累吧。
一.静态内部类单例模式
先看下代码:
public class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
return Builder.instance;
}
private static class Builder {
private static final Singleton instance = new Singleton();
}
}
JVM虚拟机的类加载机制,当第一次加载Singleton类的时候并不会初始化instance,只有在调用getInstance()的时候,JVM才会加载Builder类,同时初始化instance。这种方式不仅能够确保线程安全,也能够保证单例对象的唯一性,同时还延迟了单例的实例化,所以在很多开源库中,发现很多人都使用这种单例模式。
二.面对接口编程
这个有点老生常谈了,但是这个太经典了,Java语言的多态性就是为了这个而设计的。但是有时候道理都懂,而实际敲代码的过程中,往往不一定能够运用自如,即使用了也乱七八糟。多的就不想继续写了,这篇文章不是讨论设计模式的,仅仅是记录一些优秀的代码,吸为己用。
就举一个最近看到的例子吧。是做软键盘监听的辅助工具:
主类:KeyboardWatcher,监听软键盘的弹出和关闭
接口:SoftKeyboardStateListener,根据弹出和关闭回调对应的函数
这基本是常规操作了,在需要使用的地方实现具体的回调函数及后续操作
但是作者还在该接口的基础上额外加了另一个
接口:KeyboardHelperCallback:该接口目前也只有2个方法,弹出和关闭回调对应的函数
然后写了一个抽象类实现了以上2个接口,代码如下:
public abstract class KeyboardHelper implements KeyboardWatcher.SoftKeyboardStateListener, KeyboardHelperCallback {
@Override
public void onSoftKeyboardOpened(int keyboardHeightInPx) {
onOpened(keyboardHeightInPx);
}
@Override
public void onSoftKeyboardClosed() {
onClosed();
}
}
这样做的好处是SoftKeyboardStateListener作为KeyboardWatcher的一部分,保持功能单一性(即只监听弹出和关闭)。额外加上KeyboardHelperCallback接口,一是能降低对工具类代码的侵入性。二是可以提升代码的可扩展性,比如我需要在回调函数中额外加上某个参数,这样就不需要去修改 SoftKeyboardStateListener的回调函数了。
以上,个人觉得这段代码还是挺优雅的。
三.对第三方库的封装
很多时候,项目中都会用到第三方的库,很多时候直接使用,感觉一时很爽,直到遇到了坑..
之前的某个项目,用了一个第三方的工具库,里面有个方法是ToastUtils,代码里面需要用到的弹出toast的地方都是直接使用ToastUtils.xxx,直到有一天测试说,在某个红米手机上,toast的显示异常,一查果然,该ToastUtils代码有一部分和部分红米手机的系统(怀疑是厂商对SDK定制的原因)不兼容。
由于技术的迭代和第三库可能存在bug等原因,在使用前,对第三方库进行封装是很有必须要的。
举一个对图片加载库的封装
//单例,调度第三方的图片加载框库
public class ImageLoaderManager {
public static final int GLIDE = 1;
public static final int PICASSO = 2;
private static final int IMAGE_ENGINE_DEFAULT = PICASSO;
private ImageLoaderManager() {
}
public static ImageLoaderManager getInstance() {
return Build.INSTANCE;
}
public ImageLoader initEngine() {
return initEngine(IMAGE_ENGINE_DEFAULT);
}
public ImageLoader initEngine(int engineType) {
ImageLoader imageLoad;
switch (engineType) {
case GLIDE:
imageLoad = GlideLoader.getInstance();
break;
case PICASSO:
imageLoad = PicassoLoder.getInstance();
break;
default:
imageLoad = GlideLoader.getInstance();
}
return imageLoad;
}
private static class Build {
private static final ImageLoaderManager INSTANCE = new ImageLoaderManager();
}
}
//图片加载抽象类
public abstract class ImageLoader implements ImageLoadInterface<ImageView> {
@Override
public ImageView createImage(Context context) {
ImageView imageView = new ImageView(context);
return imageView;
}
}
//图片加载接口
public interface ImageLoadInterface<T extends View> extends Serializable {
void displayImage(Context context, Object path, T imageView);
T createImage(Context context);
}
//实现类,具体的第三方库Glide
public class GlideLoader extends ImageLoader {
@Override
public void displayImage(Context context, Object path, ImageView imageView) {
Glide.with(context.getApplicationContext())
.load(path)
.placeholder(R.mipmap.df_icon)
.into(imageView);
}
public static GlideLoader getInstance() {
return GlideLoader.Builder.glideLoader;
}
static class Builder {
private static GlideLoader glideLoader = new GlideLoader();
}
}
//Picasso同上,就不写了
当然,上面的封装还是有些不足,比如图片的具体加载方法写的太死,不方便特定情况的重写,当然这里只是记录怎么封装第三方库这种思想,不做代码的精化处理。
四.对”代码三”的修改
突然上面的写法有些瑕疵,写了个demo,完善了一下,主要增加了获取引擎实体的接口,简化了调用图片加载库的方法,下面的 ImageLoadHelper=ImageLoaderManager(换了个名字)
public class ImageLoadHelper implements ImageLoaderInf<ImageView> {
private AbstracImageLoader imageLoader;
public static final int ENGINE_GLIDE = 1;
private ImageLoadHelper() {
}
public static ImageLoadHelper getInstance() {
return Builder.INSTANCE;
}
public void initEngine() {
initEngine(ENGINE_GLIDE);
}
public void initEngine(int engine) {
switch (engine) {
case ENGINE_GLIDE:
imageLoader = GlideLoader.getInstance();
break;
default:
}
}
@Override
public void display(Context context, Object file, ImageView view) {
if (imageLoader != null) {
imageLoader.display(context, file, view);
}
}
private static class Builder {
private static final ImageLoadHelper INSTANCE = new ImageLoadHelper();
}
}
GlideLoader类实现EngineInf接口,通过getEngine()获取图片加载引擎,主要可以定制化个别情况的图片加载需求,比如不同的占位图等
public interface EngineInf<T> {
T getEngine(Context context);
}
public class GlideLoader extends AbstracImageLoader implements EngineInf<RequestManager> {
@Override
public void display(Context context, Object file, ImageView view) {
Glide.with(context).load(file).into(view);
}
private GlideLoader() {
}
public static GlideLoader getInstance() {
return Builder.GLIDELOADER;
}
@Override
public RequestManager getEngine(Context context) {
return Glide.with(context);
}
private static class Builder {
private static final GlideLoader GLIDELOADER = new GlideLoader();
}
}
这样调用的时候就是:
ImageLoadHelper.getInstance().display(App.sContext, R.mipmap.ic_launcher, imageView);
可能这种写法还存在需要完善的地方吧,等到发现了问题再跟进修改..
五.Comparator接口
对某个实体类进行排序的时候,可以通过实现Comparator接口进行处理,比如,文章实体类,需要根据文章的日期进行排序,
public class ComparatorDate implements Comparator<ArchiveBean> {
public int compare(ArchiveBean obj1, ArchiveBean obj2) {
Date d1, d2;
try {
d1 = DateUtils.string2Date(obj1.getPostTime());
d2 = DateUtils.string2Date(obj2.getPostTime());
if (d1 == null) {
return 1;
}
if (d2 == null) {
return -1;
}
if (d1.before(d2)) {
return 1;
} else {
return -1;
}
} catch (Exception e) {
return 0;
}
}
}\n
List<ArchiveBean> list = new ArrayList<>();
list.addAll(...);
Collections.sort(list, new ComparatorDate());
六.接口回调的复用实例
看下这样一段代码:
public class HttpCallback<T> implements OnHttpListener<T> {
private final OnHttpListener mListener;
public HttpCallback(OnHttpListener listener) {
mListener = listener;
}
@Override
public void onSucceed(T result) {
if (mListener == null) {
return;
}
mListener.onSucceed(result);
}
@Override
public void onFail(Exception e) {
if (mListener == null) {
return;
}
mListener.onFail(e);
}
}
为啥HttpCallback实现了OnHttpListener接口,还要给HttpCallback的构造函数传递一个OnHttpListener接口呢?通常在需要回调的地方,直接new一个HttpCallback对象不就行了吗。答案是统一处理回调函数。看如下代码:
public class A implements OnHttpListener {
@Override
public void onSucceed(Object result) {
// TODO onSucceed...
}
@Override
public void onFail(Exception e) {
// TODO onFail...
}
}
A实现了了OnHttpListener接口,可以在onSucceed和onFail函数中统一处理某些逻辑
B是A的子类:
public class B extends A {
private void fun1() {
new HttpCallback(this);
}
private void fun2() {
new HttpCallback(this) {
@Override
public void onSucceed(Object result) {
super.onSucceed(result);
}
};
}
}
B不再需要继续处理onSucceed和onFail的公共逻辑部分,且可以选择性实现自己的onSucceed和onFail方法。
还有一个问题,在HttpCallback中直接统一处理onSucceed和onFail不可以吗?
答案是,当然可以。
但是像上面那种方式有什么好处呢:
1.解耦,onSucceed和onFail处理的公共逻辑部分,大部分情况下和A的属性(变量等..)息息相关,
2.不同基类处理的公共逻辑不同,比如现在来了个C类,也要处理统一处理onSucceed和onFail方法,但是C和A的处理方式完全不一样,此时直接在HttpCallback中统一处理并不友好。
未完待续..