Android应用框架魅力的泉源--反向沟通

今天看到一篇特别好的文章,深受启发,对之前的应用框架的认识有了颠覆性的改变,非常感谢高焕堂大神的倾力奉献!

对于文章标题是转载还是原创,我纠结了一下。想到我只是在他的书上节选一段,进行编辑和细微修改,就转载好了~

下载地址:http://download.csdn.net/download/zhoujiamurong/1470442

文章名为:Android 应用框架原理与程序设计36 技

2.2.1正向沟通

传统的链接库(FunctionLibrary)已含有许多现成的函數(前辈),您的程序(晚辈)可呼叫之。例如,

publicmyFunction() {

int x =abs( y );

……

}

abs()是您已很熟悉的库存函數,它诞生在先,是前辈;您的程序(晚辈)诞生在后,是晚辈。这是传统呼叫法:晚辈呼叫前辈。一般類别库(ClassLibrary)含有现成的類别,这些類别含有函數,可供新類别的函數來呼叫之。例如,先有个Person 父類别如下:

publicclass Person {

privateString name;

publicvoid SetName(String na) { name = na; }

publicvoid Display()

{System.out.println("Name: " + name ); }

}

接着,您可以写个子類别Customer去继承它,并且呼叫它的函數,如下:

publicclass Customer extends Person {

publicvoid Init() { super.SetName(“Tom”); }

publicvoid Show() { super.Display(); }

}

上述的Init()呼叫了晚辈SetName()函數。或者,写个JMain 類别:

publicclass JMain {

privatep;

publicvoid Init() {

p = newCustomer();

p.SetName(“Tom”);

}

publicvoid Show() { p.Display(); }

}

这也是晚辈呼叫前辈的情形。由于大家已习惯了这种晚辈呼叫前辈的用法, 就通称为「正向」(Forward)呼叫法。由于晚辈拥有主控权,所以这种机制

又称为「正向控制」。再來看看本书的主角:Android 框架,以下就是Android 的应用程式码:

//Android 程序

publicclass MyActivity extends Activity {

@Override

publicvoid onCreate(Bundle icicle)

{super.onCreate(icicle);

setContentView(R.layout.main);

}

上述晚辈(即MyActivity)的onCreate()呼叫了晚辈(即Activity)的onCreate()和setContentView()函數。而Activity 就是Android 框架裡的重要抽象類别。

2.2.2反向沟通

当子類别继承父類别时,父類别之函數可以呼叫子類别之函數。虽然父類别(前辈)诞生时,子類别(晚辈)常常尚未诞生﹔但是前辈有时候可预知晚辈中的函數,就可呼叫它。框架裡的抽象類别就是扮演父類别的角色,只是含有一些阳春型的類别,其提供很通用,但不完整的函數,是设计师刻意留给应用程序的子類别來补充的。一旦补充完成,框架裡的父類别的函數就可以「反向呼叫」子類别裡的函數了。

2.2.2.1以一般Java 程序为例

例如:有了一个绘图的Shape 父類别:

//Shape.java

package_framework;

publicclass Shape {

publicvoid Paint() { this.Draw(); }

publicabstract void Draw();

}

设计者预期子類别将会定义一个Draw()函數,于是让Paint()呼叫子類别的Draw()函數。于是子類别(晚辈)提供Draw()给父類别(前辈)來呼叫之,如下:

ˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉ

// Circle.java

package _objects;

import java.awt.Color;

import java.awt.Graphics;

import _framework.*;

public class Circle extends Shape {

private Graphics m_gr;

private int x, y, radius;

public Circle(Graphics gr) { m_gr = gr; }

public void SetValue(int x0, int y0, int rad){

x = x0; y = y0;

radius = rad;

}

public void Draw(){ //画圆

m_gr.setColor(Color.BLACK);

m_gr.drawOval(x-radius, y-radius, 2*radius, 2*radius);

}}

接者,写个JMain 類别:

// JMain.java

import java.awt.*;

import javax.swing.*;

import _framework.Shape;

import _objects.*;

class JP extends JPanel {

public void paintComponent(Graphics gr) {

super.paintComponents(gr);

Circle cir = new Circle(gr);

cir.SetValue(160, 100, 45);

Shape sp = cir;

sp.Paint();

}}

public class JMain extends JFrame {

public JMain() { setTitle(""); setSize(400,300); }

public static void main(String[] args)

{ JMain frm = new JMain();

JP panel = new JP();

frm.add(panel);

frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frm.setVisible(true);

38 Android 应用框架原理与程序设计36

ˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉ

}}

此程序执行到JP 類别的sp.Paint()指令时,就呼叫到Shape 父類别的Paint()函數,接着就呼叫到Circle 子類别裡的Draw()函數了。这种前辈呼叫晚辈的用法, 就通称为「反向」(Inversion) 呼叫法。由于前辈拥有主控权,所以这种机制又称为「反向控制」(Inversion ofControl)。

2.2. Android 程序为例

例如想在Android 上画出一个长方形,可写程序如下:

// Android 程序

public class MyView extends View {

private Paint paint;

public MyView(Context context) {

super(context);

private Paint paint= new Paint();

}

public void ReDraw() { this.invalidate(); }

@Override

protected void onDraw(Canvas canvas) { // 画长方形

paint.setAntiAlias(true);

paint.setColor(Color.YELLOW);

canvas.clipRect(30, 30, 100, 100);

}}

程序执行到ReDraw()函數时, 就正向呼叫到Android 框架裡的invalidate()函數了。接着,Android 框架会反过來呼叫MyView 子類别的onDraw()函數。这就是「反向沟通」了。如果你没有定义onDraw()函數的话,会执行View 父類别预设的onDraw()函數,而依据框架预设之惯例而行了。

ˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉ

2.3 主控者是框架,而不是应用程序

前面說过,传统的链接库及類别库只提供正向沟通,而应用框架则提供正向和反向兼具的双向沟通。本节针对应用框架的双向沟通,做进一步的阐述双向沟通机制让框架成为主控者,而应用程序只是配角而已。首先看个例子,假设已经设计了Person 及Customer 兩類别并存于应用框架中,如下图所示:

Android应用框架魅力的泉源--反向沟通_第1张图片

图2-2 一个简单的框架

兹以Java 程序表达如下:

// Person.java

package _abstract_classes;

public abstract class Person {

protected String name;

public void setName(String na) { name = na; }

public abstract void display();

}

// Customer.java

package _abstract_classes;

public class Customer extends Person {

public void display()

{ System.out.println("Customer: " +super.name); }

}

现在,基于这个框架而衍生出VIP 子類别,如下图:

Android应用框架魅力的泉源--反向沟通_第2张图片

图2-3 衍生出应用程序的類别

其Java 程序代码如下:

// VIP.java

package _concrete_classes;

import _abstract_classes.*;

public class VIP extends Customer {

private String tel;

public VIP(String na, String t) {

super.setName(na);

tel = t;

}

public void display()

{ super.display();

System.out.println("TEL: " + tel);

}}

Customer

ˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉ

建构式VIP() 呼叫父類别的setName() 函數, 且display() 呼叫Customer::display()函數,这兩者皆为正向沟通。亦即,程序中的函數呼叫框架中的函數。现在继续增添反向沟通机制。例如,于框架中增加了Product 類别,此时,框架共含三个類别。基于这框架,您可衍生出子類别如下图所示:

Android应用框架魅力的泉源--反向沟通_第3张图片

图2-4 框架掌握更多主控权

其Java 程序代码如下:

// Product.java

package _abstract_classes;

public abstract class Product {

protected int pno;

protected Customer cust;

public Product(int no) { pno = no; }

public void soldTo(Customer cobj) { cust = cobj; }

public void inquire() {

this.print();

System.out.println("sold to ...");

cust.display();

}

ˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉ

public abstract void print();

}

// TV.java

package _concrete_classes;

import _abstract_classes.*;

public class TV extends Product {

private double price;

public TV(int no, double pr) {

super(no); price = pr;

}

public void print()

{ System.out.println("TV No: " + pno);

System.out.println("Price: " + price);

}}

其反向沟通机制如下图:

public class Product {

....

public void inquire() {

public class Person {

....

}

(反向沟通)

}

....

this.print();

cust.display();

(反向沟通)

public class Customer

extends Person {

....

}

}

public class TV extends Product {

....

public void print()

....

}}

public class VIP

extends Customer {

....

public void display() {

....

继续看个主函數,会更清晰地理解框架的主控地位,如下程序代码:

// JMain.java

import _abstract_classes.*;

ˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉ

import _concrete_classes.*;

public class JMain {

public static void main(String[] args)

{ TV t = new TV(1100, 1800.5);

VIP vp = new VIP("Peter","666-8899");

t.soldTo(vp);

t.inquire();

}}

Product 父類别设计在先,然后才衍生TV 子類别,而且常由不同人所设计。那么,何以Product 類别的inquire() 敢大胆地呼叫TV 類别的print()

函數呢﹖万一TV 類别并无print()时,怎么办呢﹖答案很简单:

☆ TV 類别必须定义print()函數,才能成为具体類

别。因为Product 裡的print()是抽象函數,内容

从缺:

public abstract void print();

其中的abstract 字眼,指示子類别必须补充之,才能成为具体類别。

☆ TV 類别成为具体類别,才能诞生对象。

☆ 有了对象才能呼叫inquire()函數,

☆ 既然TV 類别已覆写print()函數,inquire() 可大胆地呼叫

之。于是,必须替TV 類别添增print()函數如下:

public void print()

{ System.out.println("TV No: " + pno);

System.out.println("Price: " + price);

}

执行时,__________就产生反向呼叫的现象了。此外,Product 類别的inquire() 呼叫VIP 類别的display()函數。Product類别与VIP 類别并非同一个類别体系。此时,VIP 類别必须是具体類别才能诞生对象。cust 变數必须參考到刚诞生的对象。由 cust 所參考之物件來执行其display()函數。inquire()就透过cust 而成功地呼叫到VIP 類别的display()了。

ˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉ

这过程有个先决条件──VIP 類别必须覆写display()函數才行。否则将会呼叫到Customer 類别预设的display(),而不是呼叫到VIP 類别的display()函數。也许,您还会问一个问题:何不将cust 变數改宣告为VIP 型态之參考呢﹖答案是: 别忘了,抽象類别通常设计在先,而具体類别产生在后。因之,设计Product 類别时,VIP 類别尚未诞生呢﹗ 这程序展现了应用框架的重要现象:

☉程序执行时,主控权在框架手上。

虽然main()函數仍为程序的启动者,但主要的处理过程皆摆在Product類别内。例如,

● soldTo()负责搭配产品与顾客之关系。

● inquire() 负责呼叫TV 的print() 输出产品资料,并呼叫VIP

display() 输出顾客资料。

☉程序裡的類别,其成员函數,主要是供框架呼叫之。

例如,TV 類别的print()供inquire()呼叫之,而VIP 類别的display()供inquire()呼叫之。

☉由于框架掌握主控权,复杂的指令皆摆在框架中,大幅简化应用程序。因之,优良的应用框架常让程序员如虎添翼。

☉框架裡的inquire() 进行反向沟通,它呼叫子類别的print() 函數。这是同体系内的反向呼叫。

☉框架裡的inquire() 反向呼叫VIP display() 函數。因Product 与VIP 分属不同的類别体系。这是跨越体系的反向沟通。

 

2016.5.10 北京天气晴朗,早晚穿长袖,其他时间半袖就OK了。今天周二,看到这篇文章让我很兴奋,所以分享给大家~


你可能感兴趣的:(Android应用框架魅力的泉源--反向沟通)