by 高煥堂  2013/06/20

[請指教:高老師的免費on-line教學視頻]

 

一、EIT造形的涵意

    自从1996年Java问世之后,接口(Interface)成为Java语言的关键词(Key Word)。于是,<接口>的位阶已经提升了,其与<类>是同位阶了,而不再隐藏于类造形里。这意味着,我们可以设计一个更大的代码造形来包容类和接口两种元素。为了凸显接口角色,就得考虑两项特性:

  • 为了清楚地定义一个接口(主角),需要两个类来当配角。
  • 此外,接口以能实现为类(造形)。

于是,高焕堂老师将3个<类造形>组合起来,成为一个更大的造形;就像生物DNA的螺旋结构,组合如下图:

      

     在上图里,为于中间的类就是接口实现类。高老师将其命名为EIT造形:

      

    EIT造形也不难理解,它只是对类造形加以扩大;也就是以类为基础(保留了类的各项功能),将3个类结合在一起,各扮演不同角色;让开发者拥有更大的视野,具有更好的整体观。例如,Java语言的程序: 

class Tasks implements Runnable{

      public void run() {
              int sum = 0;
              for (int i = 0; i <= 100; i++)
                     sum += i;
                     System.out.println("Result: " + sum);
      }

}
// ----------------------------------------------------------
public class JMain {
      public static void main(String[] args) {
               Thread t = new Thread( new Tasks());
               t.start();
               System.out.println("Waiting...");
      }
}

       其主要意图是:凸显出<接口>元素与类同位阶的角色。为了清楚地定义一个接口(主角),需要两个类来当配角。例如,为了凸显Runnable接口,而且要精确地表述它,就需要Thread和Tasks两个类来陪衬,如下图:

    

      由于接口定义是架构师的主要职责,所以EIT可以协助架构师清晰地定义接口,非常有助于清晰表达架构。

 

二、EIT造形的重复组合

     EIT造形也内部结构简单,也能透过内含的类的组合关系,将EIT造形组合起来,轻易地组合出大型而复杂的系统。例如,EIT造形能像DNA螺旋结构一样,组合起来:

               

        EIT造形提供更宏大的整体观,更易于重构,迅速从简单组合出复杂系统。 

      

         这是由两个EIT造形(即Thread造形和View造形)所组成的。   

  

     在游戏软件应用上,这个Thread造形里的小线程(由UI线程所诞生的)扮演一个特殊的角色:成为游戏的主控循环(Game Loop),而UI线程则专注于响应UI 的事件,创造出两个线程完美分工。由于这个线程专注于游戏主控循环,所以又称为游戏线程(Game Thread)。游戏线程调用postInvalidate()函数,间接触发UI线程去调用invalidate()函数了,也触发View重新调用App子类的onDraw()去重新绘图了。现在就将上图落实为Android程序码,如下: 

// GameLoop.java
package com.misoo.pk001;

public class GameLoop implements Runnable {
     myView mView;
     GameLoop(myView v){
               mView = v;
         }
     public void run() {
               mView.doUpdate();
               mView.postInvalidateDelayed(1000);
         } 
}

// myView.java
package com.misoo.pk001;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;

public class myView extends View {
      private Paint paint= new Paint();
      private int x, y;
      private int line_x = 100;
      private int line_y = 100;
      private float count = 0;

      myView(Context ctx)
           { super(ctx); }
      public void doUpdate(){
             if( count > 12) count = 0;
             x = (int) (75.0 * Math.cos(2*Math.PI * count/12.0));
             y = (int) (75.0 * Math.sin(2*Math.PI * count/12.0));
             count++;
           }
      @Override protected void onDraw(Canvas canvas) {
             super.onDraw(canvas);
             canvas.drawColor(Color.WHITE);
             paint.setColor(Color.BLUE); paint.setStrokeWidth(3);
             canvas.drawLine(line_x, line_y, line_x+x, line_y+y, paint);
             paint.setStrokeWidth(2); paint.setColor(Color.RED);
             canvas.drawRect(line_x-5, line_y - 5, line_x+5, line_y + 5, paint);
             paint.setColor(Color.CYAN);
             canvas.drawRect(line_x-3, line_y - 3, line_x+3, line_y + 3, paint);
             Thread gt = new Thread(new GameThread(this));
             gt.start();
        }

}

// myActivity.java
package com.misoo.pk001;
import com.misoo.pk001.R;
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;

public class myActivity extends Activity implements OnClickListener {
       private myView mv = null;
       private Button ibtn;

      @Override protected void onCreate(Bundle icicle) {
              super.onCreate(icicle);
              show_layout_01();
       }
       public void show_layout_01(){
               LinearLayout layout = new LinearLayout(this);
               layout.setOrientation(LinearLayout.VERTICAL);
               mv = new myView(this);
               LinearLayout.LayoutParams param = 
                        new LinearLayout.LayoutParams(200, 200);
               param.topMargin = 10; 
               param.leftMargin = 10;
               layout.addView(mv, param);
               //----------------------------------------------
               ibtn = new Button(this); 
               ibtn.setOnClickListener(this);
               ibtn.setText("Exit"); 
               ibtn.setBackgroundResource(R.drawable.gray);
               LinearLayout.LayoutParams param1 = 
                              new LinearLayout.LayoutParams(200, 65);
               param1.topMargin = 10; param1.leftMargin = 10;
               layout.addView(ibtn, param1);
               //-----------------------------------------------
               setContentView(layout);
            }
       public void onClick(View v) 
               { finish(); }
  }

       由于EIT造形只是对类造形加以扩大;也就是以类为基础(保留了类的各项功能),将3个类结合在一起,各扮演不同角色。所以,只要利用类造形既有的组合机制,就能将EIT造形组合起来,成为复杂的系统了。例如,也能组合如下图:

  

    这通称为:双层EIT造形的架构设计。

 

三、为什么需要要有两个配角呢?

     虽然从代码造形来看,<E>、<I>和<T>三者是同位阶的,但从架构师角度上,<I>属于主角,而<E>和<T>是配角。搭配两个配角,才能将<I>表述的完整而清晰。

     

     搭配两个配角,就能将<I>表述的更完整而清晰。 

拿英语来比喻

   就如同英语,搭配了主词(Subject)和受词(Object),就能够将动词(Verb)表述得更完整而清晰。例如,         

  • play  -- 没有主词和受词,动词<play>就显得意义不够清晰。
  • 猫 玩(play) 绣球 -- 有了主词和受词,动词<play>就显得意义很清晰。
  • 老师 弹(play) 钢琴 -- 有了主词和受词,动词<play>就显得意义很清晰。 

拿厕所来比喻

   依据传统的类造形,架构师会表述为:设计一个厕所类(对映到实际的厕所),其提供接口(实际厕所的入口)给男生或女生使用。这很可能引导开发者先建置厕所,然后才提供入口给用户使用;经常是先类而后接口。

   反之,如果使用EIT造形,则架构师就表述为:(女生<E>、入口<I>、厕所<T>),以及<男生<E>、入口<I>、厕所<T>)。架构师考虑到女生和厕所,而定设计入口<Iw>,然后交给开发者去撰写<Tw>代码。同样地,架构师考虑到男生和厕所,而定设计入口<Im>,然后交给开发者去撰写<Tm>代码。架构师设计出来的<Iw>和<Im>是不一样的;同理,开发者撰写的<Tw>和<Tm>也是不一样的。架构师与开发者的沟通变得清晰而完整。这有效引导架构师先定义明确的<I>,然后提醒架构师搭配<E>和<T>来表述<I>。

[關於EIT代碼造形的相關文章]