类协作简介
类之间的协作是软件设计中非常重要的内容,可以说,任何好的架构设计和精妙的设计模式,都是因为具有好的类协作。
类协作在不同的场景差异性很大,要想把类协作系统性的分门别类,具有很大的难度,我也不具备那个能力。如果说选一个重要的维度,针对类协作在Android中的常见场景,我有一个简单实用的分法,以耦合关系为维度,分为直接协作、接口协作和协议协作。
所谓直接协作是指协作类间互相持有引用,直接调用,类之间直接耦合在一块;接口协作是指所有参与协作的类,都将自身的输入和输出定义在接口中,然后由协作类们的父模块实现并初始化接口,从而建立协作关系;而协议协作是指协作类们全部依赖同一套协议,通过这一套协议进行协作,协议协作比较复杂,下文中会详细说明。
基于高内聚、低耦合原则,我们假定(事实上应该)需要协作的类之间互相没有依赖,比如,Teacher类和Student类。
class Teacher {
void teach(String message) {
}
void answer(String question) {
}
}
class Student{
void learn(String message) {
}
void ask(String question) {
}
}
找到Teacher的teach方法,此方法表示Teacher具有Teach这个行为,但这个行为需要一个被Teach的对象,其实就是Student类的对象。反过来,Student类的ask方法同样需要一个Teacher类的对象。这两个类在这两个行为上的协作,下文中将分别用三种协作方式进行分析。
直接协作
直接协作代码如下:
class Teacher {
private Student mStudent; //直接引用Student对象
void setStudent(Student student) {
mStudent = student;//初始化协作对象
}
void teach(String message) {
mStudent.learn(message);//产生协作行为
}
void answer(String question) {
Log.w("",question);//响应协作行为
}
}
class Student{
private Teacher mTeacher; //直接引用Teacher对象
void setTeacher(Teacher teacher) {
mTeacher = teacher;//初始化协作对象
}
void learn(String message) {
Log.w("",message);//响应协作行为
}
void ask(String question) {
mTeacher.answer(question);//产生协作行为
}
}
class ClassRoom{
ClassRoom() {
Teacher teacher = new Teacher();//创建对象
Student student = new Student();//创建对象
teacher.setStudent(student); //初始化协作关系
student.setTeacher(teacher); //初始化协作关系
teacher.teach("直接协作教学");//引发协作
student.ask("什么情况下选择直接协作?");//引发协作
}
}
直接协作是最简单粗暴的协作方式,其优点是简单,缺点是各个协作类的移植性较差;所以直接耦合往往用于密切协作、非常特定(变数少)、不可拆分的协作场景。切记,不要试图把所有的类都设计成移植性很高的类,越是组合了很多类的大类,其使用场景越特定,其使用直接耦合的情况应该越多。
接口协作
协作代码如下:
static class Teacher {
// 定义协作接口
interface ITeacherCallback {
void onTeach(String message);
}
private ITeacherCallback mCallback; //直接引用Student对象
void setCallback(ITeacherCallback callback) {
mCallback = callback;//初始化协作对象
}
void teach(String message) {
mCallback.onTeach(message);//产生协作行为
}
void answer(String question) {
Log.w("",question);//响应协作行为
}
}
static class Student {
// 定义协作接口
interface IStudentCallback {
void onAsk(String message);
}
private IStudentCallback mCallback; //直接引用Teacher对象
void setCallback(IStudentCallback callback) {
mCallback = callback;//初始化协作对象
}
void learn(String message) {
Log.w("",message);//响应协作行为
}
void ask(String question) {
mCallback.onAsk(question);//产生协作行为
}
}
static class ClassRoom {
ClassRoom() {
final Teacher teacher = new Teacher(); //创建对象
final Student student = new Student(); //创建对象
//初始化协作关系
teacher.setCallback(new Teacher.ITeacherCallback(){
@Override
public void onTeach(String message) {
student.learn(message);
}
});
//初始化协作关系
student.setCallback(new Student.IStudentCallback(){
@Override
public void onAsk(String message) {
teacher.answer(message);
}
});
teacher.teach("直接协作教学");//引发协作
student.ask("什么情况下选择直接协作?");//引发协作
}
}
接口协作是耦合最低的协作方式,可以看出Teacher和Student之间没有任何耦合,它们之间的协作关系是由父模块ClassRoom类建立和初始化的。其优点是无耦合,缺点是定义接口和维护接口需要成本、协作关系没有那么简单明了。接口协作非常适用于模块间的协作,因为模块间本身要解耦,同时模块都是高内聚的、需要协作的行为不会很多。再次强调高内聚的模块内部,应该使用直接协作,简单明了。
协议协作
协议协作是协作类之间依赖同一套协议,那么是什么协议呢?协议可以理解为类之间协作的规范和途径,体现的形式为共同依赖相同的类、接口、数据等,只要协作类们按照协议规范去操作,就能互相协作。协议协作的类互相之间没有耦合,但它们都与协议耦合,从依赖的层次上看,协议处于协作类们的下层,协议不依赖任何协作类,但协作类们全部都要依赖协议。例如,Activity之间可以通过Bundle协作,Bundle就是协议;可以通过Broadcast协作,Broadcast就是协议;可以通过EventBus协作,EventBus就是协议。
协议协作的代码并不难编写,以下代码为上文所举例子的协议协作实现:
// 定义协议接口
interface ITeacher {
void answer(String question);
}
// 定义协议接口
interface IStudent {
void learn(String message);
}
// 定义协议管理类
static class Manager {
private static ITeacher sTeacher;
private static IStudent sStudent;
// 初始化协作类
static void init(ITeacher teacher) {
sTeacher = teacher;
}
// 初始化协作类
static void init(IStudent student) {
sStudent = student;
}
// 代理协作
static void teach(String message) {
sStudent.learn(message);
}
// 代理协作
static void ask(String question) {
sTeacher.answer(question);
}
}
static class Teacher implements ITeacher {
Teacher() {
// 向协议注册自身
Manager.init(this);
}
void teach(String message) {
Manager.teach(message); //产生协作行为
}
@Override
public void answer(String question) {
Log.w("",question);//响应协作行为
}
}
static class Student implements IStudent {
Student() {
// 想协议注册自身
Manager.init(this);
}
@Override
public void learn(String message) {
Log.w("",message);//响应协作行为
}
void ask(String question) {
Manager.ask(question); //产生协作行为
}
}
static class ClassRoom {
ClassRoom() {
final Teacher teacher = new Teacher(); //创建对象
final Student student = new Student(); //创建对象
teacher.teach("直接协作教学");//引发协作
student.ask("什么情况下选择直接协作?");//引发协作
}
}
协议协作的好处是协作类之间可以非常分散的进行协作,因为它们之间既不需要彼此引用(直接协作)、也不需要一起组合到父模块中(接口协作),只需要访问协议对象就能进行协作。例如,很多同学喜欢定义单例协议,这样只要是同一个进程内的类对象都能通过协议进行协作,并以此作为进程内的消息机制,可见协议协作在难以获取引用、难以设置引用的场景下,能让协作类之间分散的进行协作,非常便利。
协议协作的协议往往都包含类似注册监听和执行协作两部分内容,协作类之间各自注册自身和调用更新的方法,即可完成“分散型”的协作。由于协作类之间都依赖协议,其耦合比较大,适用于模块内部合作密切的类之间使用。定义整个应用内都能使用的又大又通用的协议,会让整个应用都被协议污染,代码不好移植。