第 4 天

框架开发者学习指南(Day-04)

-- 实例演练

 

学习步骤:

Step 4.1  前言:在昨天学习指南(Day-03)里,已经看过<餐厅的点菜单>的范例了。一旦谈到餐厅里攸关于桌子和锅子的知识,就很容易联想到烹饪的知识,它分散于两种文件里:食谱和点菜单。一般而言,食谱主要记载烹饪师傅的知识,而点菜单则记载买主(客人)的知识;两者相汇合之后,才能端出一道好吃的菜肴。如下图所示:

(图4-1)

  基于上述食谱和点菜单的例子,就很容易对应到软件框架开发了。于买主(客户或用户)出现之前,软件开发者基于他们已具备的知识,来决定接口;然后把其知识(包括领域通用需求和开发技能)写在框架基类里;并且提供<I>接口。

  基于<I>接口,当买主出现了,才把买主的知识(即需求)写入于应用子类里。如下图所示:

 (图4-2)

    在软件开发上,厘清或萃取知识(Knowledge Acquisition)的任务,通称为需求分析(Requirement Analysis)。在框架设计上,我们最关键的是要去厘清领域知识与买主知识的分际,通常不需要对需求细节多下功夫。因此,配合开发框架而做的需求分析,与传统的系统开发需求分析并不完全一样。于是,我们常对前者称之为:框架需求分析。 

Step 4.2  复习:主板模式(Motherboard pattern)。在昨天学习指南(Day-03)里,已经看过主板模式的两种不一样的结构了。例如,典型的结构,如下图所示:

(图4-3)

Step 4.复习:框架需求分析将软件开发分为三个阶段,一般而言,软件开发者都很能区分软件的「开发」与「执行」两个阶段。如果我们再依循上一节所提的『买主来了』时间点,来将开发阶段分为两段,就成为3个阶段了。如下图: 

 (图4-4)

   厘清这3个阶段之后,就可以演练进行一个简单的动作:把领域(和架构师)知识写入基类里,然后把买主需求写入到子类里。此外,还要定义两者之间的<I>接口,让基类可以调用子类的函数,以便执行子类的代码,将执行结果汇合到基类里。如下图:

  (图4-5)


Step 4.4 实操演练:题目(一)

  • 需求分析

          在学习Java编程时,几乎大家都写过一个简单的应用程序,就是求算1+2+3+ … + N的值。依据上一小节的需求时间轴概念,可绘图如下:

(4-6)

   从上图可引导你决定三件事:

1.算法(1+2+3+… +N)必须撰写于框架的基类里。

2.N值必须撰写于应用子类里。

3.基类必须调用子类去取的N值。

 决定了,就可以依据上图4-4到图4-6的思维而绘制出类别设计图,如下所示:

(4-7)

  其中的ICount接口内含抽象函数的定义。如果你想加上一些预设行为(Default Behavior)函数,就可以设计抽象类来取代之。如下图:(图4-8)

   虽然BaseAdpaterICount的架构形式不同,但其都担任接口的角色,让myCounter能顺利装配到Counter上。在BaseAdpater里可以加上其它的具象函数来表达一些预设行为,呈现框架的深度服务。这些预设行为,一方面可用来简化myCounter内容,减轻其开发者的负担。在另一方面,则能用来调整接口本身,让CountermyCounter两者之间更具有独立性,降低两者之间的相依性(Dependency);此时,它扮演调节器(Adapter)的角色。 

(图4-9)

   这BaseAdapter将onCount()接口函数转换成为getCount()函数;其让Clounter与myCounter各使用不同接口函数,有利于双方的稳定而独立成长。 

  • 撰写

   首先建立一个Android的Ex05_01项目(Project),如下:

 

★ 撰写你的框架基类和<I>

// BaseAdapter.java

package myFramework; 

public abstract class BaseAdapter {

       // 其它函数

        public int getCount() {

        int n = this.onCount();

        if( n<0 ) n = 0;

             return n;

        }

        protected abstract int onCount();

}

 

// Counter.java

package myFramework; 

public class Counter {

        private BaseAdapter reference;       

        public int run() {

            int N;

            N = reference.getCount();

            int sum = 0;

            for(int i=1; i<=N; i++) {

                sum += i;

             }

            return sum;

          }

          public void setRef(BaseAdapter co){

              reference = co;

        }

 }

 

★ 把基类和<I>送人,协助别人去开发应用子类 

// myCounter.java

package com.misoo.pk07;

import myFramework.BaseAdapter; 

public class myCounter extends BaseAdapter

   @Override

   public int onCount() {

                   return 10;

    }

 

// myActivity.java

package com.misoo.pk07;

import myFramework.Counter;

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 Button ibtn;

        private Counter counter;

 

        @Override  protected void onCreate(Bundle icicle) {

             super.onCreate(icicle); 

             LinearLayout layout = new LinearLayout(this);

             layout.setOrientation(LinearLayout.VERTICAL);

             ibtn = new Button(this);

             ibtn.setOnClickListener(this);

             ibtn.setText("Exit");

             ibtn.setBackgroundResource(R.drawable.gray);

             LinearLayout.LayoutParams param1 =

                      new LinearLayout.LayoutParams(150, 65);

             param1.topMargin = 10;    

             param1.leftMargin = 5;

             layout.addView(ibtn, param1);

             setContentView(layout);

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

            counter = new Counter();

            counter.setRef(new myCounter());

            int sum = counter.run();

            setTitle("sum = " + String.valueOf(sum));

         }       

        public void onClick(View arg0) {       finish();     }

此程序计算了1+2+3+… +10,输出结果:55。

  • 改变设计图

   在架构层面上,你可以将上图4-9里的CounterBaseAdapter两个类别合并起来。这是框架开发者的自由调解范围,只要不改变onCount()接口函数,就不会影响到myCounter了。如下图:

(图4-10)

 

// Counter.java

package myFramework; 

public abstract class Counter {

    public int run() {

         int N;

         N = this.onCount();

      int sum = 0;

         for(int i=1; i<=N; i++)  

                     sum += i;

          return sum;

        }

      public abstract int onCount();

 } 

// myCounter.java

package com.misoo.pk07;

import myFramework.Counter; 

public class myCounter extends Counter{

      public int onCount() {   return 10;   }  

}

  

Step 4.5 实操演练:题目()

  • 需求分析

    如果时间轴改为:

(图4-11)

   现在可以试试先想想接口设计:基类必须有个抽象函数,来反向调用到子类。在调用该函数时,顺便把基类里的N值传递下去给子类。由子类进行计算工作,然后将计算结果传回给基类。亦即,你可做下述三件事:      

  1. 将N值写在框架基类里。
  2. 替各买主而设计一个子类,将各自采取的算法(如1^1 + 2^2 + 3^3 +… +N^N)撰写于子类里。
  3. 替基类设计一个抽象函数onCal(n:int),让基类能调用子类,取得子类的计算结果。
  • 撰写

请您参考前面Step 4.4的流程,建立一个Android应用开发项目(Project),并将上图4-11的框架需求分析图,实现为AndroidApp代码,并实际执行之。

PS.可参考文章:好莱坞(Hollywood)原则的演练

 

 

Step 4.实操演练:题目()

  • 需求分析   

   如果时间轴改为:

((图4-12)

   现在可以试试先想想接口设计:基类必须有个抽象函数,来反向调用到子类。基类可以重复调用该函数,总共调用N次,每次回传一个Kn值,在由基类把它们累加起来。当然,你也能设计一个新的接口函数,基类只调用它一次,调用时把N值传递下去给子类。由子类回传N项数据,例如从数据库里读取N笔数据并回传给基类。由于接口函数的制定权就掌控于你(框架设计者)的手中,App开发者会配合你的。 

  • 撰写

请您参考前面Step 4.4的流程,建立一个Android应用开发项目(Project),并将上图4-12的框架需求分析图,实现为AndroidApp代码,并实际执行之。

PS.可参考文章:好莱坞(Hollywood)原则的演练

 

Step 4.实操演练:题目()

  • 需求分析

    如果时间轴改为:

 (图4-13)

  依据此图4-13,您可以使用Activity来提供UI上的一个EditText窗口,让User在执行阶段才输入N值。由基类主动去向Activity的 EditText取得N值,然后重复调用接口函数,总共调用N次,每次回传一个Kn值,在由基类把它们累加起来。于是,设计出架构图,如下:

 (图4-14)

  • 撰写

请您参考前面Step 4.4的流程,建立一个Android应用开发项目(Project),依据上图4-14的框架需求分析图,以及图4-15的架构图,开发AndroidApp代码,并实际执行之。

PS.可参考文章:好莱坞(Hollywood)原则的演练

 

Step 4.8 观摩与讨论:

  • 针对Web Shop用例进行分组讨论,提出业务框架的初步设计方案
  • 基于初步方案来进行优化,提出高可用的实践方案

 

~ End ~