天天看点

Android架构设计之类协作

类协作简介

类之间的协作是软件设计中非常重要的内容,可以说,任何好的架构设计和精妙的设计模式,都是因为具有好的类协作。

类协作在不同的场景差异性很大,要想把类协作系统性的分门别类,具有很大的难度,我也不具备那个能力。如果说选一个重要的维度,针对类协作在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("什么情况下选择直接协作?");//引发协作

    }

}

协议协作的好处是协作类之间可以非常分散的进行协作,因为它们之间既不需要彼此引用(直接协作)、也不需要一起组合到父模块中(接口协作),只需要访问协议对象就能进行协作。例如,很多同学喜欢定义单例协议,这样只要是同一个进程内的类对象都能通过协议进行协作,并以此作为进程内的消息机制,可见协议协作在难以获取引用、难以设置引用的场景下,能让协作类之间分散的进行协作,非常便利。

协议协作的协议往往都包含类似注册监听和执行协作两部分内容,协作类之间各自注册自身和调用更新的方法,即可完成“分散型”的协作。由于协作类之间都依赖协议,其耦合比较大,适用于模块内部合作密切的类之间使用。定义整个应用内都能使用的又大又通用的协议,会让整个应用都被协议污染,代码不好移植。