礼拜5下午,有一些小伙伴在讨论关于“我对MVP的理解啊”,“我对RxJava,RxAndroid的理解啊”等等。在交流中发现,其实我们往往在实际开发中有意无意的都可能出现某个类特别的繁杂,代码特别的多,而且其实很多都是重复的,但是又没有办法,诸如一大堆回调。
可能在项目构建之初,想着我要如何如何去实现,如何如何优化代码结构,逻辑等等,可是因为理解或者业务繁重等各个因素,导致到后来还是挤成一坨龙猫,像这样(麦麦胖的脚都看不见了,臃肿,不过可爱!!)
这里不说RxJava的那些内容就简单的从MVP再来解释一遍,理解一遍,什么是抽象类,什么是接口。
很多人其实对抽象类和接口觉得他们都差不多,他们都不是实体类,他们都可以被实现,都可以被继承,那么区别又在哪?
我记得以前在一开始进入软件行业的时候,面试官都问的很基础最突出的是以下几个问题
相信大家对着3个问题一定记忆犹新,都快成了java程序员面试的必考题了。
这里只解释第一个问题
抽象类:
在OOP中,万物皆对象(这句话印象深刻),同时所有的对象都是通过类来描述的,但是并不是所有的类都是来描述对象的。如果一个类没有足够的信息来描述一个具体的对象,而需要其他具体的类来支撑它,那么这样的类我们称它为抽象类。
接口:
接口是一种比抽象类更加抽象的“类”。接口是用来建立类与类之间的协议,它所提供的只是一种形式,而没有具体的实现,接口是抽象类的延伸。
区别:
网上有一大堆说区别啊,概念的,这些都比较具体,但是我认为,最确切,最重要的一点是 “抽象类是对类抽象,而接口是对行为的抽象。”
Ok,理解了吗?不理解?那我们来看今天的例子,这个例子是上次MVP Demo的延伸,先看一下运行的效果。
这边就是一个简单的例子,逻辑大致如此:
我们有一个EditText,有一个Button,当Button被点击的时候获取EditText的文字内容做一个判断,大于5显示一些字,小于5显示一些字,根据判断的结果,打印依据Log,Log如下:
我们再来贴下包目录,这次请仔细看结构,因为这也是讲解的一部分:
我们现在所看到的这个类是MainActivity这个类,BaseActivity是他的父类,LoginActivity是之前的例子,想了解的可以看:http://blog.csdn.net/ddwhan0123/article/details/51009279
我们还有一个Common目录,里面有一个接口,DialogInterface看名字就知道是跟Dialog相关的一个接口(取名规范不规范我们另议,大家理解他干嘛的比较重要)
Login目录无视
Main目录下有1个类和2个接口。
MainResult这个接口是用于输入内容判断所用的接口。
MainButtonClick这个接口是用户点击按钮所触发的一些行为所用的接口。
MainCLickImp这个类用于实现之前的接口。
从头开始看,我们先看父类BaseActivity
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayout());
findId();
logic();
}
abstract void findId();
abstract int getLayout();
abstract void logic();
protected void showToast(Context context, String msg) {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}
我们的BaseActivity是一个抽象类,他有3个抽象方法供子类实现,分别是findId(),getLayout(),logic() 看名字我们就理解,分别是 用于获取控件对象,用于获取布局ID,用于逻辑处理。
BaseActivity有一个带protected的方法showToast(Context context, String msg)
,一目了然,这是给与子类调用的。
为什么这里用一个抽象类去做这些事情,也可以写个接口来做,像这样
//没想到什么好名字,你理解就好
public interface BaseXXXXX{
abstract void findId();
abstract int getLayout();
abstract void logic();
}
我们之前所说的 对象都是通过类来描述的。因为我们接下来的那些子类可能都有初始化的操作,可能都要获取布局,可能都要有简单的逻辑。这些行为都是这个类本身就应该具有的,只是可能具体的行为我们描述不清,所以我们用一个抽象类来描述这么个“不具体”的对象(所以,继承了他的子类一定是具体的)这里再提一提 抽象类是对类抽象,这么看来没错吧,父类描述了个大题的样子,让具体的子类去实现。
接下来我们再来看下DialogInterface这个接口
/**
* 接口是对行为的抽象,作用于模拟进度条
**/
public interface DialogInterface {
void show();
void hide();
}
这个接口有2个方法,一个用来模拟showDialog,那另外个就是hideDialog了。
那么为什么,没有把这2个方法放到我们的BaseActivity中呢?
不是所有的子类都需要Dialog,那么相关于Dialog的行为并不是这些Activity所固有的,所以我们用一个接口来给需要Dialog行为的子类来实现!因为接口是对行为的抽象,接口跟实现类本身不存在概念本质上的一致,而抽象类必须是
上面2者的关系解释完,后面的东西就好理解了
MainButtonClick只负责用户点击行为的传递,而它本身不在意他做了什么
public interface MainButtonClick {
//按钮的行为,传入作为比较用的接口 MainResult,并把判断的结果返回
boolean MainButtonClickListener(int value, MainResult mainResult);
//根据判断的结果决定是否显示Dialog,传入作为dialog的接口 DialogInterface
void ShowDialog(boolean value, DialogInterface dialogInterface);
}
MainResult只负责用户结果的比对,而它本身不在意他做了什么
public interface MainResult {
void Greater();
void Less();
}
OK,再之后就是我们最在意的2个实体类,先从“冲做 MVP模式里”Presenter的MainClickImp说起。
public class MainClickImp implements MainButtonClick {
@Override
public boolean MainButtonClickListener(int value, MainResult mainResult) {
if (value > 5) {
mainResult.Greater();
return true;
} else {
mainResult.Less();
return false;
}
}
@Override
public void ShowDialog(boolean value, DialogInterface dialogInterface) {
if (value) {
dialogInterface.show();
} else {
dialogInterface.hide();
}
}
}
MainClickImp实现了我们用户点击的实现,同时我们的业务流程全部都在这里完成。因为我们的MainActivity这个类是有Dialog行为的所以我把点击事件引发的Dialog行为也放到实现类里一起实现。
最后我们来看下我们原本可能相对比较繁杂的MainActivity
public class MainActivity extends BaseActivity implements MainResult, DialogInterface, View.OnClickListener {
EditText edit;
Button button;
Toolbar toolbar;
MainButtonClick mainButtonClick;
@Override
void findId() {
toolbar = (Toolbar) findViewById(R.id.toolbar);
edit = (EditText) findViewById(R.id.edit);
button = (Button) findViewById(R.id.button);
}
@Override
int getLayout() {
return R.layout.activity_main;
}
@Override
void logic() {
StatusBarUtil.setColor(this, getResources().getColor(R.color.colorAccent), 0);
setSupportActionBar(toolbar);
button.setOnClickListener(this);
mainButtonClick = new MainClickImp();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public static void LogicSuccess(Activity activity) {
Intent intent = new Intent(activity, MainActivity.class);
activity.startActivity(intent);
}
@Override
public void Greater() {
showToast(this, "大于5");
}
@Override
public void Less() {
showToast(this, "小于5");
}
@Override
public void show() {
LogUtils.d("--->show");
}
@Override
public void hide() {
LogUtils.d("--->hide");
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.button) {
int value = Integer.parseInt(edit.getText().toString().trim());
//没写值空不空的判断了,别没事找事了,随便填数字就行!!!
boolean flag = mainButtonClick.MainButtonClickListener(value, MainActivity.this);
mainButtonClick.ShowDialog(flag, MainActivity.this);
}
}
}
我们发现,我们的Activity实现了父类的3个初始化方法,点击行为引发的判断方法以及Dialog方法。
此时观众朋友们肯定会心存疑虑,我们整了那么多接口啊,实现类啊怎么代码没有想象中的少,感觉还多了?
这边来解释,首先你的代码逻辑变得更清晰,点击发起点 找
onClick(View v)
Dialog显示找
show()
Dialog消失找
hide()
大于预设值找
Greater()
小于预设值找
Less()
我们的代码不再是在 onCreate()里有“一卡车代码”,就算要再加功能,加逻辑只要添加接口,实现业务逻辑就行,我们的Activity更纯粹,更干净。
下面给基础差的小伙伴们讲解下:
因为接口是不可以new的所以我们要跑接口中的方法怎么办?
例子中,我new 了一个实现类的实例,虽然调用的时候还是mainButtonClick.MainButtonClickListener(value, MainActivity.this);
但是这些事情都是让MainClickImp去做了
这里我们把MainActivity.this自己传入了MainClickImp并不是作为Context之类的需要,而是因为我们的MainActivity实现了MainResult, DialogInterface
,我们才能在Activity这里享受到MainClickImp里执行的逻辑。
简单的说他就是形成了一个环
1.Activity的调用
2.MainClickImp去实现逻辑
3.又因为Activity参与了实现过程,所以在Activity里的方法才能被执行,不然就是 null Object。
好累,一不小心又礼拜6下午了。。。周末还没休息。。。。大家周末愉快
因为这台电脑没有git相关环境,就不丢git了。。CSDN下载地址吧,礼拜1再丢git
源码地址:http://download.csdn.net/detail/ddwhan0123/9493126
这里再安利下,我的整合库,让大家找资源更方便,谢谢支持:http://blog.csdn.net/ddwhan0123/article/details/51145547