JAVA设计模式之Observer

一. Observer樣式應用於Android框架設計
来自: http://blog.csdn.net/liranke/archive/2009/11/05/4771589.aspx

 

1.      Framework的反向控制

反向控制(Inversion of Control)是應用框架(Application Framework,簡稱AF)魅力的泉源。其常見的實現機制有      二:

  1) 繼承(Inheritance)

---- 這與Template Method樣式密切相關。

  2)委託(Delegation)

---- 這與Observer樣式密切相關。

  Android是個完全的AF,處處可見到反向控制的機制。當你熟悉上述的兩種實現機制後,就會將之對應到Template Method和Observer樣式。然後藉由樣式來更深刻體會這些機制的設計意涵。如此,除了更能活用Android之外,也能逐漸提升你自己設計新AF的興趣、能力和信心。

  2. 複習:使用繼承(即Template Method樣式)

  茲複習你已經熟悉的Template Method樣式,如下述之範例:

  

 // Student.java

public class Student {

           private String m_name;

           private int m_credit;

           Student(String na, int cr ){

                     m_name = na;

                     m_credit = cr;

           }

           public void print(){

                     System.out.println(m_name + ",  " +String.valueOf(template_computeTuition()));

           }

    public float template_computeTuition() {

                        if (m_credit > 6)  m_credit = 6;  

                        return hook_getValue(m_credit) + 5000;

            }

    protected float hook_getValue(int credit){

       return (credit -1) * 500;

    }

}

// Graduate_Student.java

public class Graduate_Student extends Student{

           Graduate_Student(String na, int cr ){

                     super(na, cr);

           }

           protected float hook_getValue(int credit){

                   return credit * 700;

           }

}

// Undergraduate_Student.java

public class Undergraduate_Student extends Student {

           Undergraduate_Student(String na, int cr ){

                     super(na, cr);

           }

}

// JMain.java

public class JMain {

           public static void main(String[] args) {

    int credit = 5;

           Graduate_Student mike = new Graduate_Student("Mike", 5);

           mike.print();

           Undergraduate_Student linda = new Undergraduate_Student("Linda", 7);

           linda.print();

}

}

  這是以Template Method樣式來實踐反向控制。

3.使用委託(即Observer樣式)

  在Template Method樣式裡,hook method的具體實現是撰寫在子類別裡。使用委託時,則將hook method定義於獨立的類別裡,如下述的HookClass:

 

  把hook method定義於另外的類別:

// HookClass.java

public class HookClass {

              protected float getValue(int credit){

                  return (credit -1) * 500;

               }

}

template method委託HookClass類別之物件去處理客製化的部份:

// Student.java

public class Student {

           private String m_name;

           private int m_credit;

           private HookClass hookObject = null;

           Student(String na, int cr ){

                     m_name = na;

                     m_credit = cr;

           }

           public void setHook(HookClass hk){

                     hookObject = hk;

           }

           public void print(){

                     System.out.println(m_name + ",  " +String.valueOf(template_computeTuition()));

           }

    public float template_computeTuition() {

                        if (m_credit > 6)  m_credit = 6;  

                        return hookObject.getValue(m_credit) + 5000;

            }

}

定義HookClass的子類別:GraduateHook,並且讓Student的物件委託給GraduateHook子類別之物件:

// Graduate_Student.java

public class Graduate_Student extends Student{

           Graduate_Student(String na, int cr ){

                     super(na, cr);

                     setHook(new GraduateHook());

           }

           private static class GraduateHook extends HookClass{

                     @Override

                     protected float getValue(int credit){

                             return credit * 700;

                     }

           }

}        

// JMain.java

public class JMain {

           public static void main(String[] args) {

     int credit = 5;

     Student mike = new Graduate_Student("Mike", credit);

           mike.print();

 }

}

於是,Student類別的template method 呼叫了HookClass抽象類別的getValue()函數,進而反向呼叫到GraduateHook子類別的getValue()函數。

4.GoF的Observer樣式

在GoF的<<Design Patterns>>一書裡,其Observer樣式的架構圖為:

 

圖1  Observer樣式[GoF]

  這裡的Subject類別就對應到上述程式碼的Student類別。ConcreteSubject就對應到Graduate_Student類別。Observer類別對應到HookClass。ConcreteObserver對應到GraduateHook類別。

  5. Observer樣式又稱為Listener樣式

   上述的範例可改寫為:

 

// IListener.java

public interface IListener {

           float getValue(int credit);

}

// Student.java

public class Student {

           private String m_name;

           private int m_credit;

           private IListener plis = null;

           Student(String na, int cr ){

                     m_name = na;

                     m_credit = cr;

           }

           public void setListener(IListener lis){

                     plis = lis;

           }

           public void print(){

                     System.out.println(m_name + ",  " +String.valueOf(template_computeTuition()));

           }

    public float template_computeTuition() {

                        if (m_credit > 6)  m_credit = 6;  

                        return plis.getValue(m_credit) + 5000;

            }

 }

// Graduate_Student.java

public class Graduate_Student extends Student{

           Graduate_Student(String na, int cr ){

                     super(na, cr);

                     setListener(new GraduateListener());

           }

           private static class GraduateListener implements IListener{

                     public float getValue(int credit) {

                                return credit * 700;

                     }

           }        

}

// JMain.java

public class JMain {

           public static void main(String[] args) {

     int credit = 5;

     Student mike = new Graduate_Student("Mike", credit);

           mike.print();

 }

}

6.      欣賞Android裡的Observer樣式(即Listener樣式)

  欣賞Android程式範例

6.1        畫面情境

 

  撰寫程式

  建立Android程式專案:

// ac01.java

package com.misoo.pkzz;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

public class ac01 extends Activity {

            @Override

               public void onCreate(Bundle icicle) {

                   super.onCreate(icicle);

                   setContentView(R.layout.main);

                   Button btn = (Button)findViewById(R.id.button);

                   Button btn2 = (Button)findViewById(R.id.button2);

                   btn.setBackgroundResource(R.drawable.gray);

                   btn.setOnClickListener(clickListener);

                   btn2.setBackgroundResource(R.drawable.byw);

                   btn2.setOnClickListener(clickListener_2);

                 }

            OnClickListener clickListener = new OnClickListener()

               {

                  public void onClick(View v){

                            String name = ((Button)v).getText().toString();

                                    setTitle( name + " button clicked");

                  }

        };

            OnClickListener clickListener_2 = new OnClickListener()

                 {

                          public void onClick(View v)

                     {

                      finish();

                      }

                   };

   }

圖2  Android裡的Listener樣式

   這裡的ac01就是ConcreteObserver;而onClick()就是hook method(或稱為primitive method)。

  View就是Subject。而Button則是ConcreteSubject。

 
 

二. 新手攻略之设计模式
(一.) factory


 
Factory 模式

    Fctory模式主要分为Simple Factory模式、Factory Method模式、Abstract Factory模式三种。


Simple Factory模式

 


如上图所示的,Client代表了客户的角色,它只依赖于接口ProductInterface,而不关心特定的具体操作,如何产生ProductInterface的实例由SimpleFactory完成。客户只要面对Factory,客户依赖于产品接口,产品的具体创建操作是可以与客户分离的。


Factory Method模式

 

 


Factory Method中的AbstractOperator中拥有一个抽象的factoryMethod()方法,它负责生成一个IProduct类型的对象,由于目前还不知道将如何实现这个类型,所以将其推迟至子类别中实现,在AbstractOperator中先实现IProduct操作,利用多态操作即可完成各种不同的IProduct类型的对象操作。


Abstract Factory模式

 


简单的说,在Abstract Factory模式中将具体的Product封装在具体Factory实现中,而客户仍只要面对Factory与Product的抽象介面,避免依赖于具体的Factory与Product,由于Factory封装了所必须的Product,所以要更换掉所有的元件,只要简单的抽换掉Factory就可以了,不用修改客户端的代码。

以上就是Factory模式的基本结构和用法,一般来说:使用factory模式

1) 可以完成一系列的初始化操作。

比方说有一个类A,最简单的当然是A a = new A()了,但是可能想做一些初始化工作,那最简单的我们就会把这些操作写在A的构造函数里面,如:A a = new A(param)了,但如果有很多的初始化工作,那把所有的初始化放在构造函数里面,可能就不大合适了,在这种情形下可以使用工厂模式,协助完成一系列初始化。

2) 封装对象的创建。

在一个大程序中可能一个接口有很多类的实现,那在不同的地方就会写很多的

Interface intr1 = new Impl1();

Interface intr2 = new Impl2();

….

根据这样程序的扩展性、可维护性都会变差。比较好的做法是:

Interface intr1 = XXXFactory.getImpl1();

Interface intr2 = XXXFactory.getImpl2();

但是这样的话,每当多一个实现,Factory接口就要多一个方法,这样Factory接口就经常需要变动,要解决这个问题,也有不少方法。采用Abstract Factory模式,AbstractFactory的子类通过多态来实现:

Interface intr1 = Impl1Factory.getImpl1();

Interface intr2 = Impl2Factory.getImpl2();

这样做可以使接口封闭,但是增加类这个做法也不是太好,所以不推荐。采用Interface intr1 = XXXFactory.getImpl(param1);

这样的形式,这个应该是比较推荐的。此外还可以采用template模式的方法,以后可以继续介绍。

3) 使产品类的创建能够动态改变。

在工厂的方法中,可以采用动态加载类的方式来加载产品类。这样如果碰到需要改变产品类的实现时,只需要修改配置而不需要更改和重新编译任何现有代码,使系统的扩展性大大加强。

此外,需要注意的是,一般来说Factory类的实例都采用Singleton模式来实现。因为创建一个Factory的实例系统开销比较大,而且Factory主要是用来创建其它Product类的,一般不可能有很多个,所以将Factory类用Singleton模式来实现可以节约系统成本,提高运行效率。

如果大家对于Factory模式的使用还有什么经验总结的话,欢迎大家积极回复。


( 二.) builder与prototype

 

 

2009-01-05  |  15:38分类:未分类  |作者: Sean |   257 views
Builder 模式


 

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们,用户不知道内部的具体构建细节。

在实际使用中,我们经常在下面的场景中见到Builder的身影:

我们经常会用到一个概念——”池”(Pool),当资源提供者无法提供足够的资源,并且这些资源需要被很多用户反复共享时,就需要使用池。

“池”实际是一段内存,当池中有一些复杂的资源的”断肢”(比如数据库的连接池,也许有时一个连接会中断),如果循环再利用这些”断肢”,将提高内存使用效率,提高池的性能。修改Builder模式中Director类使之能诊断”断肢”断在哪个部件上,再修复这个部件。

看到这里,我们不禁会联想到上节中的一个模式:Abstract Factory。猛地一看,Builder模式的构造与Abstract Factory模式的确十分相像。但是两者在应用场景和应用方式上还是有不少区别的。

Builder的概念在于,由Director使用Builder提供的BuildXxx的接口一步一步的构建一个目标对象,当然,Builder只是个抽象基类,Director使用的其实是Builder的一个实例,但是它并不知道用的是哪个实例。每个实例产生的Product不同,所以Director同样也不知道产生了什么产品。

而Abstract Factory的概念在于,把目标对象视为Product,负责构建对象的类视为Factory。对于所有的Product要遵循Abstract Product的接口,同样所有的Factory也要遵循Abstract Factory的接口。这样的好处呢,就是对于客户Client看到的,不再是具体的工厂和产品,而是都使用抽象工厂和抽象产品的接口,这是接口固定且一致,便容易多了。

两者的主要区别有下:Builder是在Director的引导下,一步一步的构建对象;而Abstract Factory则是一次性创建对象。对于Abstract Factory,客户直接操作Factory和Product,所以两者都要抽象出接口;而Builder模式下,Director只操作Builder实例,不直接操作Product,所以Product是什么样,什么接口对于Director压根不关心,自然也就不必抽象出接口。

Prototype 模式

 

Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。

Prototype 模式的一大特点就是提供了一种快捷的创建对象的方式。一般在程序中我们可以使用的对象创建方法有:

1. 使用new关键字创建对象,这是最最普通的一种。

2. 使用对象序列化(Serialize)创建对象

3. 使用反射机制(Reflection)动态创建对象

4. 使用clone(克隆)方式创建对象

这里的Prototype模式用的就是第4种方法,它通过复制一个已经存在的对象实例来创建新的对象实例,被复制的对象实例称为”原型”。原型模式多应用在创建复杂的或者耗时的实例,因为在这种情况下通过复制一个已经存在的对象使得程序的运行效率更高;或者值相等而命名不一样的同类数据。

Prototype模式的重点在于clone(),它负责复制物件本身并传回,但这个clone()本身在实作上存在一些困难,尤其是当物件本身又继承另一个物件时,如何确保复制的物件完整无误,这是个比较困难的问题。

但是,因为Java中的提供clone()方法来实现对象的克隆,所以Prototype模式实现一下子变得很简单。Java语言的构件模型直接支持原型模式。所有的类都继承自java.lang.Object,而Object类提供一个clone()方法,可以将一个类复制一份;但是这个类必须实现一个标示接口Cloneable表明这个类支持复制。如果一个对象没有实现这个接口而调用clone()方法,Java编译器会抛出CloneNotSupportedException异常。

从功能上讲,Prototype模式的主要特点包括客户端不知道具体产品类,而只知道抽象产品类,客户端不需要知道这么多的具体产品名称。如果有新的产品类加入,客户端不需要进行改造就可直接使用。这一点我们又不禁联想到之前提到的Abstract Factory模式。的确Abstract Factory模式有许多与Prototype模式相同的效果,但是两者的使用范围却不相同。

假设一个系统的产品类是动态加载的,而且产品类具有一定的等级结构。

这个时候如果采取工厂模式的话,工厂类就不得不具有一个相应的等级结构;而产品类的等级结构一旦变化,工厂类的等级结构就不得不有一个相应的变化。这对于产品结构可能会有经常性变化的系统来说,采用工厂模式就有不方便之处。

在这种时候如果采取Prototype模式,给每一个产品类配备一个克隆方法(大多数的时候只需给产品类等级结构的根类配备一个克隆方法),便可以避免使用工厂模式所带来的具有固定等级结构的工厂类。

这样,一个使用了Prototype模式的系统与它的产品对象是怎么创建出来,以及这些产品对象之间的结构是怎样的,以及这个结构会不会发生变化是没有关系的。

Prototype模式又有其特有的优点:

第一、Prototype模式允许动态地增加或减少产品类。由于创建产品类实例的方法是产品类内部具有的,因此增加新产品对整个结构没有影响。

第二、Prototype模式提供简化的创建结构。工厂方法模式常常需要有一个与产品类等级结构相同的等级结构,而Prototype模式就不需要这样。对于Java设计师来说,Prototype模式又有其特有的方便之处,因为Java语言天生就将Prototype模式设计到了语言模型里面。善于利用Prototype模式和Java语言的特点,可以事半而功倍。

第三、给一个应用软件动态加载新功能的能力。例如,一个分析Web服务器的记录文件的应用软件,针对每一种记录文件格式,都可以有一个相应的”格式类”负责。如果出现了应用软件所不支持的新的Web服务器,只需要提供一个格式类的克隆,并在客户端等登记即可;而不必给每个软件的用户提供一个全新的软件包。

第四、产品类不需要非得有任何事先确定的等级结构,因为Prototype模式适用于任何的等级结构。

然而,Prototype模式并不是可以随便使用的,它最主要缺点,是每一个类都必须配备一个克隆方法。配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类来说不是很难;而对于已经有的类不一定很容易。特别是当一个类引用到不支持串行化的间接对象,或者引用含有循环结构的时候。所以在这种场合使用Prototype模式就不怎么恰当了。

Singleton 模式


 

Singleton模式可是说是设计模式中最简单的一个模式了。说它简单是因为,它的结构没有其它模式那么复杂其功能——保证在Java应用程序中,一个类Class只有一个实例存在,也没有其它模式那么复杂。但是结构简单并不代表它的作用就不大,相反你能够在各个复杂的架构中发现Singleton的身影。

程序中经常有这样的要求,整个程序运行时只有一个实例被使用。比如建立目录、数据库连接池、系统参数配置、Java API 中的 Runtime、 Calendar等都需要这样的单线程操作。

使用Singleton模式就可以很好地解决这样的问题。

我们常常看到工厂模式中类装入器(class loader)中也用Singleton模式实现的,因为被装入的类实际也属于资源。抽象窗口工具包(AWT)就是组合使用这两个模式的典型例子。

虽然Singleton设计模式是最简单的设计模式之一,但它也存在一些缺陷。

1. 多线程应用程序中的构造问题

在多线程应用程序中,你必须仔细构造Singleton模式。当Singleton不存在时,如果两个线程即将同时执行创建方法,这两个线程必须检查Singleton实例,但只有一个线程应当创建新对象。这个问题的典型解决办法就是对类使用相互排斥,指出对象正在被实例化。

2. 提前考虑克隆预防

你仍然可以使用对象的clone()方法克隆对象,建立一个Singleton对象。要禁用这一功能,你需要禁用对象的克隆方法,这产生一个CloneNotSupportedException例外。

3. 考虑使singleton类位于最后

你可能希望将Singleton类放在最后,以避免Singleton的子类造成其它问题。

4. 不要忘记垃圾收集

根据不同的执行,你的Singleton类和它的所有数据可能被当作垃圾收集。因此,在应用程序运行时,你必须保证存在一个Singleton类的实时引用。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/liranke/archive/2009/11/05/4771589.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值