java解耦利器之万能接口

  • 前言

java中的接口(interface)想必大家都用过,但是如果有些刚入行的萌新问起,为什么要用接口?有什么作用?说实话,一时半会儿我也答不上来。这时候看上去稳如老狗,其实心里慌的一批的我会说,这么简单,去度娘里查下你就清楚了。然后自己就偷偷的打开电脑在浏览器里面输入www.bai...,后面的大家都懂的。

这里我就不做科普了,用过接口的老司机都说好,谁用谁知道。最几年都有人主张java要面向接口编程了,可见接口的重要性。那么今天楼主要说的这个万能接口跟java接口有啥关联了?接下来大家坐稳,我们就要开始开车了。

  • 传统接口使用

我们自定义一个view用来做演示,在view中点击,然后在activity中需要响应点击事件。使用姿势和步骤:

1、在自定义view中定义一个接口

2、在自定义view中定义一个接口类型的变量

3、暴露一个set方法用来传入实现接口的实现类

4、在activity中implements在自定义view中的接口,并实现方法

5、在activity中初始化自定义view,然后通过暴露的set方法传入实现体

6、在自定义view中调用接口

//这里为了简化流程就只用了button,实际可能是自定义容器view,或者更为复杂的场景需要用到接口

public class NormalButton extends AppCompatButton {
    public NormalButton(Context context) {
        super(context);
        initEvent();
    }

    public NormalButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        initEvent();
    }

    public NormalButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initEvent();
    }
    
    //第一步
    public interface Callback{
        void callback();
    }
    //第二步
    private Callback callback;
    //第三步
    public void setCallback(Callback callback){
        this.callback = callback;
    }

    private void initEvent(){
        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                //第六步
                if(callback != null){
                    callback.callback();
                }
            }
        });
    }
}


activity中使用

public class MainActivity extends AppCompatActivity implements NormalButton.Callback{
    private NormalButton nb;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }
    
    //第五步,暴露实现体
    private void initView(){
        nb = findViewById(R.id.nb);
        nb.setCallback(this);
    }

    private void showToast(String msg){
        Toast.makeText(this,msg,Toast.LENGTH_LONG).show();
    }


    //第四步,实现接口中的方法
    @Override
    public void callback() {
        showToast("activity 中调用普通接口");
    }
}

xml很简单,我这里就不贴了。当点击界面上的button的时候,就能调用activity中的方法,并且弹出信息。

扫描二维码关注公众号,回复: 9370183 查看本文章
  • 存在的不足和缺点

看到上一点,一切看上去似乎都非常的美好,代码简洁,逻辑清晰。你要这么想,说明你还是太年轻。试想一下某个场景,项目中需要针对不同的业务和场景设计大量的接口,这样你就需要定义许多个接口,然后并且要实现他们。随之而来的问题就是接口取名字难,一个类可能大量的implements(android系统源码中就有很多这样的例子),导致代码冗余,而且由于需要持有实现类的引用,增加了项目的耦合度。

总结下缺点和不足主要为两点:

1、代码冗余

2、耦合度高

  • 实现

我们的万能接口其实还是借鉴了传统接口的思路,利用面向对象的思想,对传统接口来做抽象,以达到减少代码冗余和低耦合的目标。

首先我们看下接口都有哪些特征,然后我们给它抽象出来。

1、接口名(所有接口必须都有的)

2、未实现函数体(可以为空,个人感觉为空意义不大,只是语法可以这样允许)

3、函数体参数(可选)

4、函数体返回值(可选)

所以我们的基类必须要有一个属性,就是接口名字,定义如下:

//基类
public abstract class Function {
    private String mFunctionName;

    public String getmFunctionName() {
        return mFunctionName;
    }

    public Function(String mFunctionName){
        this.mFunctionName = mFunctionName;
    }
}

针对上面总结的特征,我们可以组合成多种形式,比如没有参数和没有返回值的,有参数没有返回值的等等类型,接下来我们来一一抽象出来,能不能看懂暂且后面再说。

没有参数和没有返回值


public abstract class FunctionNoParmNoResult extends Function {
    public FunctionNoParmNoResult(String mFunctionName) {
        super(mFunctionName);
    }

    //没有实现的函数体,具体实现交给子类
    public abstract void function();
}

没有参数有返回值

public abstract class FunctionNoParmWithResult<Result> extends Function{
    public FunctionNoParmWithResult(String mFunctionName) {
        super(mFunctionName);
    }

    //返回值类型使用泛型
    public abstract Result function();
}

有多个参数没有返回值

public abstract class FunctionWithMultiParmNoResult extends Function{
    public FunctionWithMultiParmNoResult(String mFunctionName) {
        super(mFunctionName);
    }

    //多个参数用可变数组,由于多个参数类型可能不统一,所以用object类型
    public abstract void function(Object ... param);
}

有多个参数有返回值

public abstract class FunctionWithMultiParmWithResult<Result> extends Function{
    public FunctionWithMultiParmWithResult(String mFunctionName) {
        super(mFunctionName);
    }
    
    //返回值用泛型,可变参数
    public abstract Result function(Object ... param);
}

有单个参数没有返回值

public abstract class FunctionWithParmNoResult<Param> extends Function{
    public FunctionWithParmNoResult(String mFunctionName) {
        super(mFunctionName);
    }

    //单个参数用泛型传入
    public abstract void function(Param param);
}

有单个参数有返回值

public abstract class FunctionWithParmWithResult<Param,Result> extends Function{
    public FunctionWithParmWithResult(String mFunctionName) {
        super(mFunctionName);
    }

    //泛型参数和泛型返回结果
    public abstract Result function(Param param);
}

可能看到这里,很多同学还是不太明白为什么要这样子写,看不明白没关系,我们先放着,看完下面的再回过头来看就清楚了。

很显然,我们需要一个管理类来统一管理上面的这个不同类型的“接口”。那管理类应该怎么设计了?

1、针对不同的类型,需要用不同的容器来盛放

2、提供添加“接口”到管理类里的方法

3、提供调用不同类型“接口”的方法

4、最好能支持链式调用

综上,管理类如下:

//function 管理类
public class FunctionsManger {
    private static FunctionsManger _instance;
    //容器用来存放函数不同类型的接口,key是函数名
    private HashMap<String,FunctionNoParmNoResult> mFunctionNoParmNoResult;
    private HashMap<String,FunctionNoParmWithResult> mFunctionNoParmWithResult;
    private HashMap<String,FunctionWithParmNoResult> mFunctionWithParmNoResult;
    private HashMap<String,FunctionWithParmWithResult> mFunctionWithParmWithResult;
    private HashMap<String,FunctionWithMultiParmNoResult> mFunctionWithMultiParmNoResult;
    private HashMap<String,FunctionWithMultiParmWithResult> mFunctionWithMultiParmWithResult;
    private FunctionsManger(){
        //初始化
        mFunctionNoParmNoResult = new HashMap<>();
        mFunctionNoParmWithResult = new HashMap<>();
        mFunctionWithParmNoResult = new HashMap<>();
        mFunctionWithParmWithResult = new HashMap<>();
        mFunctionWithMultiParmNoResult = new HashMap<>();
        mFunctionWithMultiParmWithResult  = new HashMap<>();
    }

    //单例模式
    public static FunctionsManger getInstance(){
        if(_instance == null){
            _instance = new FunctionsManger();
        }
        return _instance;
    }

    //无参数无返回值,添加
    public FunctionsManger addFunction(FunctionNoParmNoResult func){
        mFunctionNoParmNoResult.put(func.getmFunctionName(),func);
        return this;
    }
    //无参数无返回值,调用
    public void invokeFunction(String functionName){
        if(TextUtils.isEmpty(functionName)){
            throw new RuntimeException("Function name can not be empty");
        }
        if(mFunctionNoParmNoResult != null){
            FunctionNoParmNoResult func = mFunctionNoParmNoResult.get(functionName);
            if(func != null){
                func.function();
            }else{
                throw new RuntimeException("Function "+functionName+" is not exits");
            }
        }
    }

    //无参数有返回值,添加
    public FunctionsManger addFunction(FunctionNoParmWithResult func){
        mFunctionNoParmWithResult.put(func.getmFunctionName(),func);
        return this;
    }
    //无参数有返回值,调用
    public <Result> Result invokeFunction(String functionName,Class<Result> clazz){
        if(TextUtils.isEmpty(functionName)){
            throw new RuntimeException("Function name can not be empty");
        }
        if(mFunctionNoParmWithResult != null){
            FunctionNoParmWithResult func = mFunctionNoParmWithResult.get(functionName);
            if(func != null){
                if(clazz != null){
                    return clazz.cast(func.function());
                }else{
                    return (Result)func.function();
                }
            }else{
                throw new RuntimeException("Function "+functionName+" is not exits");
            }
        }
        return null;
    }


    //有参数无返回值,添加
    public FunctionsManger addFunction(FunctionWithParmNoResult func){
        mFunctionWithParmNoResult.put(func.getmFunctionName(),func);
        return this;
    }
    //有参数无返回值,调用
    public <Param> void invokeFunction(String functionName,Param param){
        if(TextUtils.isEmpty(functionName)){
            throw new RuntimeException("Function name can not be empty");
        }
        if(mFunctionWithParmNoResult != null){
            FunctionWithParmNoResult func = mFunctionWithParmNoResult.get(functionName);
            if(func != null){
               func.function(param);
            }else{
                throw new RuntimeException("Function "+functionName+" is not exits");
            }
        }
    }

    //有参数有返回值,添加
    public FunctionsManger addFunction(FunctionWithParmWithResult func){
        mFunctionWithParmWithResult.put(func.getmFunctionName(),func);
        return this;
    }
    //有参数有返回值,调用
    public <Param,Result> Result invokeFunction(String functionName,Param param,Class<Result> clazz){
        if(TextUtils.isEmpty(functionName)){
            throw new RuntimeException("Function name can not be empty");
        }
        if(mFunctionWithParmWithResult != null){
            FunctionWithParmWithResult func = mFunctionWithParmWithResult.get(functionName);
            if(func != null){
                if(clazz != null){
                    return clazz.cast(func.function(param));
                }else{
                    return (Result) func.function(param);
                }
            }else{
                throw new RuntimeException("Function "+functionName+" is not exits");
            }
        }
        return null;
    }


    //有多个参数无返回值,添加
    public FunctionsManger addFunction(FunctionWithMultiParmNoResult func){
        mFunctionWithMultiParmNoResult.put(func.getmFunctionName(),func);
        return this;
    }
    //有多个参数无返回值,调用
    public void invokeFunction(String functionName,Object ... param){
        if(TextUtils.isEmpty(functionName)){
            throw new RuntimeException("Function name can not be empty");
        }
        if(mFunctionWithMultiParmNoResult != null){
            FunctionWithMultiParmNoResult func = mFunctionWithMultiParmNoResult.get(functionName);
            if(func != null){
                func.function(param);
            }else{
                throw new RuntimeException("Function "+functionName+" is not exits");
            }
        }
    }

    //有多个参数有返回值,添加
    public FunctionsManger addFunction(FunctionWithMultiParmWithResult func){
        mFunctionWithMultiParmWithResult.put(func.getmFunctionName(),func);
        return this;
    }
    //有多个参数有返回值,调用
    public <Result> Result invokeFunction(String functionName,Class<Result> clazz,Object ... param){
        if(TextUtils.isEmpty(functionName)){
            throw new RuntimeException("Function name can not be empty");
        }
        if(mFunctionWithMultiParmWithResult != null){
            FunctionWithMultiParmWithResult func = mFunctionWithMultiParmWithResult.get(functionName);
            if(func != null){
                if(clazz != null){
                    return clazz.cast(func.function(param));
                }else{
                    return (Result) func.function(param);
                }
            }else{
                throw new RuntimeException("Function "+functionName+" is not exits");
            }
        }
        return null;
    }

    //退出应用时调用
    public void clear(){
        mFunctionNoParmNoResult.clear();
        mFunctionNoParmWithResult.clear();
        mFunctionWithParmNoResult.clear();
        mFunctionWithParmWithResult.clear();
        mFunctionWithMultiParmNoResult.clear();
        mFunctionWithMultiParmWithResult.clear();
    }
}

代码可能有点多,但是逻辑一点都不复杂,注释也写的很清楚,那接下来就是如何使用了。

定义一个自定义view模仿文章开始时提到的需要使用接口的场景

public class NoParamNoResultButton extends AppCompatButton {
    //定义接口名字
    public static final String NAME_NPNR = NoParamNoResultButton.class.getSimpleName()+"_npnr";
    public NoParamNoResultButton(Context context) {
        super(context);
        initEvent();
    }

    public NoParamNoResultButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        initEvent();
    }

    public NoParamNoResultButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initEvent();
    }

    private void initEvent(){
        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                //通过名字调用接口
                FunctionsManger fm = FunctionsManger.getInstance();
                fm.invokeFunction(NAME_NPNR);
            }
        });
    }
}

然后activity使用

public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFunction();
    }

    private void initFunction(){
        //将指定名字的接口类型添加到管理类中
        FunctionsManger fm = FunctionsManger.getInstance();
        fm.addFunction(new FunctionNoParmNoResult(NoParamNoResultButton.NAME_NPNR) {
            @Override
            public void function() {
                showToast("activity 中调用无参无返回值接口 :"+NoParamNoResultButton.NAME_NPNR);
            }
        });
    }

    private void showToast(String msg){
        Toast.makeText(this,msg,Toast.LENGTH_LONG).show();
    }

}

怎么样,是不是达到了和文章开始说的一样的效果,但是你会发现,我们少了很多步骤,根本不需要定义interface,不需要implements,代码减少了很多,而且不需要持有实现类的引用,耦合度非常低,只需要指定一个唯一的名字就ok了。

接下来我们再看下一个稍微复杂点的例子,有多个参数和返回值

public class WithMultiParamWithResultView extends AppCompatButton {

    public static final String TAG = WithMultiParamWithResultView.class.getName()+"_wmpwr";

    public WithMultiParamWithResultView(Context context) {
        super(context);
        initEvent();
    }

    public WithMultiParamWithResultView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initEvent();
    }

    public WithMultiParamWithResultView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initEvent();
    }

    private void initEvent(){
        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                //传入返回值类型和不同类型参数,并接收结果显示到界面上
                String result = FunctionsManger.getInstance().invokeFunction(TAG,String.class,"hello world",2);
                setText(result);
            }
        });
    }
}

activity使用

public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFunction();
    }

    private void initFunction(){
        FunctionsManger fm = FunctionsManger.getInstance();
        fm.addFunction(new FunctionNoParmNoResult(NoParamNoResultButton.NAME_NPNR) {
            @Override
            public void function() {
                showToast("activity 中调用无参无返回值接口 :"+NoParamNoResultButton.NAME_NPNR);
            }
        }).addFunction(new FunctionWithMultiParmWithResult<String>(WithMultiParamWithResultButton.NAME_WMPWR) {
            @Override
            public String function(Object... param) {
                String param1 = param[0].toString();
                int param2 = (Integer) param[1];
                return param1 + param2;
            }
        });
    }

    private void showToast(String msg){
        Toast.makeText(this,msg,Toast.LENGTH_LONG).show();
    }

}

可以看到其实相比上面,只是加了一个对多参数和有返回值类型的"接口"的链式添加,这里由于篇幅有限,其他的各种类型就不一一演示了,原理跟这个差不多。其他类型的"接口"调用,请参考我的github:https://github.com/mrcoderzou/CommInterface,欢迎star。

  • 尾巴

虽然文章中代码看上去有点多,其实逻辑都不复杂。如果有问题,欢迎留言,周末愉快!

发布了32 篇原创文章 · 获赞 59 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/abs625/article/details/84376883