溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

平臺框架(Framework)開發(fā)的雕龍之技6招

發(fā)布時間:2020-03-22 16:31:49 來源:網絡 閱讀:589 作者:myeit 欄目:移動開發(fā)

前言:客端呼叫抽象類別的TemplateMethod()函數(shù),而此TemplateMethod()函數(shù)轉而呼叫抽象類別的PrimitiveOperation()函數(shù)。由于此PrimitiveOperation()函數(shù)是抽象函數(shù),也就是卡榫函數(shù),于是轉而呼叫具體子類別的PrimitiveOperation()函數(shù)。其中,AbstractClass抽象類別是屬于框架,而ConcreteClass具象子類別則屬于應用程序。

平臺框架(Framework)開發(fā)的雕龍之技6招

ee                                                                        ee

歡迎訪問 ==>高老師的博客網頁

高煥堂:MISOO(大數(shù)據.大思考)聯(lián)盟.臺北中心和東京(日本)分社.總教練


EE                                                                        EE

平臺框架開發(fā)的雕龍之技6

實踐接口:擅用抽象類別(Abstract Class)

◆  實踐框架接口:擅用Template Method設計樣式

◆  誰來誕生應用子類別的對象呢? 答案是:框架

◆  規(guī)劃壁虎的尾巴

◆  設計Callback機制

◆  由基類封裝線程(Thread)的復雜性



1. 雕龍之技#1:實踐接口:擅用抽象類別(Abstract Class)

  • 不要期待一部汽車能在街道上,也能在沙灘上跑。

  • 不要期待一支AP能在WinMobile上跑,也能在Android上跑。

  • 但是,如果能隨時抽換輪胎的話,汽車就有可能。

  • 但是,如果能隨時抽換應用子類別的話,AP就有可能。

接口的來源

  為了隨時能更換輪胎,可以將一部完整的「汽車」看成一顆優(yōu)質的「大理石」,然后學習偉大雕刻師羅丹的雕刻之道:“把不必要的部分去掉”。首先仔細審視一部優(yōu)質的「汽車」(如同大理石),如下圖:

平臺框架(Framework)開發(fā)的雕龍之技6招

圖1  羅丹的雕刻之道:把「不」必要的部分去掉


由于要等客戶出現(xiàn)才能決定輪胎,所以客戶到來之前,先不做輪胎。于是,把輪胎(含輪轂)部分去掉,先不做輪胎,而得出「汽車×××」,如下圖所示:

平臺框架(Framework)開發(fā)的雕龍之技6招

圖2  挖掉輪胎,出現(xiàn)接口(即×××)


軟件接口的實踐:抽象類別

為了隨時能更換子類,可以將一支完整的「軟件類別」看成一顆優(yōu)質的「大理石」,然后學習偉大雕刻師羅丹的雕刻之道:“把不必要的部分去掉”。首先仔細審視一個「類別」(如同大理石),如下圖:

平臺框架(Framework)開發(fā)的雕龍之技6招

圖3  一顆軟件大理石


由于要等客戶出現(xiàn)才能決定輪胎,所以客戶到來之前,先不做輪胎。于是,把輪胎(含輪轂)部分去掉,先不做輪胎,而得出「軟件接口」,如下圖所示:

平臺框架(Framework)開發(fā)的雕龍之技6招

圖4  挖掉小鳥之形,出現(xiàn)軟件接口

抽象類別之內涵:抽象函數(shù)

抽象類別含有抽象函數(shù)(Abstract Function),成為抽象類別與它的具象子類別(Concrete Class 或Subclass)之卡榫函數(shù)(Hook Function)。[歡迎光臨 高煥堂 網頁: http://www.cnblogs.com/myEIT/ ]

平臺框架(Framework)開發(fā)的雕龍之技6招

圖5  挖掉小鳥之形,出現(xiàn)軟件接口


為何叫做卡榫函數(shù)呢? 因為它是讓具體子類別來相銜接的接口處。例如,

平臺框架(Framework)開發(fā)的雕龍之技6招

圖6  挖掉小鳥之形,出現(xiàn)軟件接口


卡榫函數(shù)是抽象類別與具象子類別之間的接口。


2. 雕龍之技#2:實踐框架接口:擅用Template Method設計樣式

卡榫函數(shù)是抽象類別與具象子類別之間的接口。如果再加上抽象類別與客端(Client)之間的接口,就成為大家所熟悉的Template Method設計樣式(Pattern)了。這個樣式就是來自GoF的<<Design Patterns>>一書,此樣式如下圖所示:

平臺框架(Framework)開發(fā)的雕龍之技6招

 圖7  GoF的Template Method樣式圖


   客端呼叫抽象類別的TemplateMethod()函數(shù),而此TemplateMethod()函數(shù)轉而呼叫抽象類別的PrimitiveOperation()函數(shù)。由于此PrimitiveOperation()函數(shù)是抽象函數(shù),也就是卡榫函數(shù),于是轉而呼叫具體子類別的PrimitiveOperation()函數(shù)。其中,AbstractClass抽象類別是屬于框架,而ConcreteClass具象子類別則屬于應用程序。將之對應到上述的「畫鳥」范例,得到下圖:

平臺框架(Framework)開發(fā)的雕龍之技6招

圖8  「畫鳥」范例的Template Method樣式


3. 雕龍之技#3:誰來誕生應用子類別的對象呢? 答案是:框架

   框架開發(fā)在先,應用子類別開發(fā)在后,框架開發(fā)者事先無法預知應用子類別的名稱,那么他又如何去new一個應用子類別的對象(Object)呢?

平臺框架(Framework)開發(fā)的雕龍之技6招

 圖9  框架誕生Bird子類別的對象

如果是Java框架,就可使用Java的CreateInstance()函數(shù)來實際誕生Bird應用子類別的對象。

4. 雕龍之技#4:規(guī)劃壁虎的尾巴

   小框架終究還是要離開母框架而自主運行或移植到其它平臺上,就像一位姑娘終究要離開母親,而自主生活或嫁入婆家的。為了讓小框架擁有獨立自主的求生能力,我們應該幫她規(guī)劃好對外的結合接口。于是,母框架扮演一只惡貓,而小框架扮演一只壁虎,就可以找出壁虎的尾巴了。規(guī)劃壁虎的尾巴一直是框架設計的重要之技之一。例如,Blackjack撲克牌游戲的Android應用程序,其一般架構如下圖:

平臺框架(Framework)開發(fā)的雕龍之技6招

圖10  壁虎在惡貓的嘴巴里

 其中,Blackjack游戲玩法的主要函數(shù)(如bj_f1()、bj_f2()等)都分散于Activity或View的子類別里。茲舉個比喻:

  • Activity、View等基類,如同一只貓。

  • bjActivity、bjView等子類別,如同貓的嘴巴。

  • bjActivity、bjView等子類別里的onCreate()、onDraw()等函數(shù),如同貓的牙齒。

  • Blackjack游戲主要函數(shù)(如bj_f1()、bj_f2()等),如同一只壁虎的身體。


從這個比喻,可以看出來這只壁虎是很可憐的。

平臺框架(Framework)開發(fā)的雕龍之技6招

圖11  棄尾求生術


  這樣的新架構,讓壁虎隨時可以棄尾求生,移植到別的平臺里,繼續(xù)生存下去。甚至可以成為框架的一部分,如下圖:

平臺框架(Framework)開發(fā)的雕龍之技6招

 圖12  移到框架里(一)


平臺框架(Framework)開發(fā)的雕龍之技6招

圖13 移到框架里(二)


5. 雕龍之技#5:設計Callback機制

   前面介紹的Template Method樣式都是將「會變」的部份委托給子類別,當有所變化時,就抽換掉子類別,換上新的子類別就行了。由于該子類別與其抽象類別之間具有「繼承」關系,所以就通稱為:繼承方式的反向控制(IoC, 或稱Callback)。如下圖:

平臺框架(Framework)開發(fā)的雕龍之技6招

圖14  Callback(繼承)


   現(xiàn)在介紹另一種反向控制,則是某一個類別將「會變」的部份委托另一個類別,這兩個類別不必具有繼承關系,而只須具結合(Association)關系。我們稱此為:委托方式的反向控制(IoC, 或稱Callback)。

平臺框架(Framework)開發(fā)的雕龍之技6招

圖15  Callback(接口)


Callback的實踐

// ILoc.java

package com.misoo.ppxx;

publicinterface ILoc {

int getY(int y);

}

//---------------------------------------------

// myView.java

package com.misoo.ppxx;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.view.View;

import android.view.View.OnClickListener;

publicclass myView extends View implements OnClickListener{

private Paint  paint= new Paint();

privateint line_x = 10;

privateint line_y = 30;

private ILoc callback;    

    myView(Context ctx) {

super(ctx);

             setOnClickListener(this);

         }

 @Override

protectedvoid onDraw(Canvas canvas) {

super.onDraw(canvas);

                   canvas.drawColor(Color.WHITE);

                   paint.setColor(Color.GRAY);

                   paint.setStrokeWidth(3);

                   canvas.drawLine(line_x, line_y, line_x+120, line_y, paint);

   paint.setColor(Color.BLACK);

                   paint.setStrokeWidth(2);

        canvas.drawText("click here please", line_x, line_y + 50, paint);

int pos = 70;

                   paint.setColor(Color.RED);

                   canvas.drawRect(pos-5, line_y - 5, pos+5, line_y + 5, paint);

                   paint.setColor(Color.YELLOW);

                   canvas.drawRect(pos-3, line_y - 3, pos+3, line_y + 3, paint);

this.invalidate();

        }

      @Override

publicvoid onClick(View arg0) {

               line_y = callback.getY(line_y);

       }

publicvoid setCallback(ILoc cb){

               callback = cb;

       }

}

//------------------------------------------------------------

// myActivity.java

package com.misoo.ppxx;

import android.app.Activity;

import android.os.Bundle;

import android.widget.LinearLayout;

publicclass myActivity extends Activity{

   @Override

publicvoid onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

    LinearLayout layout_01 = new LinearLayout(this);

           layout_01.setOrientation(LinearLayout.VERTICAL);

           LinearLayout.LayoutParams param =

new LinearLayout.LayoutParams(150, 200);

           param.leftMargin = 1;         param.topMargin = 3;

           myView mv = new myView(this);

           mv.setCallback(callback);

           layout_01.addView(mv,param);

           setContentView(layout_01);

   }

ILoc callback = new ILoc(){

       @Overridepublicint getY(int y) { return y+=10;      }

 };

}


6. 雕龍之技#6:由基類封裝線程(Thread)的復雜性

   線程(Thread)又稱為<執(zhí)行緒>。因為Android主線程(Main Thread)必須迅速照顧UI畫面,盡快處理UI事件,因此常常需要創(chuàng)件小線程(Sub Thread)去執(zhí)行一個特定或較費時的模塊。例如,必須使用小線程去執(zhí)行如下圖1里的BiAdder.java類別時,該如何做呢?  

平臺框架(Framework)開發(fā)的雕龍之技6招

 圖16  由應用子類別誕生小(子)線程


   由于BiAdder的執(zhí)行時間可能超過5秒鐘,所以最好使用子線程去執(zhí)行之。但是這件事情可能會困擾著myActivity類別的開發(fā)者,因為BiAdder開發(fā)者可能不同于myActivity的開發(fā)者。所以myActivity類別的開發(fā)者通常并不知道BiAdder切確的執(zhí)行時間長度。在此種情況下,最佳的解決辦法是:

  • BiAdder開發(fā)者也提供一個基類(Super-class),例如下圖2里的Adder基類,由它來包容線程的考慮,于是myActivity類別開發(fā)者就會開心了,他不必替BiAdder執(zhí)行時間長短而傷腦筋了。

平臺框架(Framework)開發(fā)的雕龍之技6招

圖17  由基類吸收線程的復雜性


  在此圖里,myActivity和myAdder兩個類別都是由主線程執(zhí)行,所以這兩類別的開發(fā)者很開心,不必顧慮BiAdder的執(zhí)行時間長度??烧J為BiAdder類別也是由主線程執(zhí)行的(其實是子線程執(zhí)行的)。于是,在Adder::exec()里以主線程執(zhí)行myAdder子類別的onGetX()和onGetY()兩個函數(shù),并誕生子線程來執(zhí)行BiAdder::execute()函數(shù)。等到執(zhí)BiAdder::execute()行完畢,就透過MQ要求主線程執(zhí)行myListener::callback()函數(shù)。在執(zhí)行myListener::callback()時,子線程已經計算出carry和sum值了。這時主線程可取得正確的carry和sum的值。所以這樣的寫法是對的。如下述的原始程序代碼:

// IListener.java

package com.misoo.adder;

publicinterface IListener {

publicvoid callback();

}

//------------------------------------------

// BiAdder.java

package com.misoo.adder;

publicclass BiAdder {

privateint a, b, carry, sum;

publicvoid set_digits(int ia, int ib){

         a = ia;  b = ib;

   }

publicvoid execute(){

try {

               Thread.sleep(6000);

           } catch (InterruptedException e) {

               e.printStackTrace();

              }

carry = a & b;

           sum = a ^ b;

 }

publicint get_carry(){

return carry;

    }

publicint get_sum(){

return sum;

   }

  }

//------------------------------------------

// Adder.java

package com.misoo.adder;

import android.os.Handler;

import android.os.Looper;

abstractpublicclass Adder {

private BiAdder ba;

privatestatic IListener plis;

public Adder()

                  {

                     ba = new BiAdder();

                  }

publicvoid exec(){

                      ba.set_digits(onGetX(), onGetY());

new Thread(){

publicvoid run() {

                                 ba.execute();

                                 Handler h = new Handler(Looper.getMainLooper());

                                 h.post(new myRun());

                         }

                 }.start();

                }

publicint get_carry(){

return ba.get_carry();

                   }

publicint get_sum(){

return ba.get_sum();

                  }

abstractpublicint onGetX();

abstractpublicint onGetY();

publicstaticvoid setListener(IListener listener){

plis = listener;

                }

class myRun implements Runnable{

publicvoid run() {

plis.callback();

                       }

                }

}

//--------------------------------------------------------

// myAdder.java

package com.misoo.pk01;

import com.misoo.adder.*;

publicclass myAdder extends Adder {

public myAdder(){

super();

    }

              @Override publicint onGetX() {

return 1;

              }

              @Override publicint onGetY() {

return 1;

              }

}

//-----------------------------------------------

// myActivity.java

package com.misoo.pk01;

import com.misoo.adder.IListener;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.LinearLayout;

publicclass myActivity extends Activity implements OnClickListener {

private Button btn, btn2;

private myAdder adder;

publicvoid onCreate(Bundle icicle) {

super.onCreate(icicle);

               LinearLayout layout = new LinearLayout(this);

               layout.setOrientation(LinearLayout.VERTICAL);

             btn = new Button(this);

               btn.setBackgroundResource(R.drawable.heart);

               btn.setId(101);

               btn.setText("run");

               btn.setOnClickListener(this);

               LinearLayout.LayoutParams param =

new LinearLayout.LayoutParams(120, 55);

               param.topMargin = 10;

               layout.addView(btn, param);

               btn2 = new Button(this);

               btn.setBackgroundResource(R.drawable.heart);

               btn2.setId(102);                                   btn2.setText("exit");

               btn2.setOnClickListener(this);                layout.addView(btn2, param);

               setContentView(layout);

               //-----------------------------------

               myAdder.setListener(new myListener());

              }

publicvoid onClick(View v) {

switch(v.getId()){

case 101:

                       adder = new myAdder();

                       adder.exec();

                       setTitle("executing...");

break;

case 102:      finish();break;

               }

              }

class myListener implements IListener {

publicvoid callback() {

                setTitle(Thread.currentThread().getName() + ", sum = "

               + String.valueOf(adder.get_carry())

               + String.valueOf(adder.get_sum()));

              }

  }

}

   如果BiAdder類別與myActivity類別是由不同的人負責開發(fā)的話,上述的范例就很有參考價值了。BiAdder開發(fā)者藉由基類來封裝子線程的誕生,讓myActivity類別的開發(fā)變得簡單了。

EE                                                                  EE

平臺框架(Framework)開發(fā)的雕龍之技6招


向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI