【转】 从Java类库看设计模式

从Java类库看设计模式

http://www.ibm.com/developerworks/cn/java/l-jdkdp/part1/

 
 

 
 


级别: 初级

刘武东 (wdliu@chinaren.com),

2001 年 11 月 06 日

 

在这一部分的内容中,介绍的是一个相对简单但功能强大的模式:Observer模式。希望通过这部分地叙述,大家看了之后,能够对设计模式有一个比较全面地,感性的认识。

很多时候,对于一个设计来说(软件上的,建筑上的,或者它他工业上的),经验是至关重要的。好的经验给我们以指导,并节约我们的时间;坏的经验则给我们以借鉴,可以减少失败的风险。然而,从知识层面上来讲,经验只是作为一种工作的积累而存在于个人的大脑中的,很难被传授或者记录。为了解决这样的问题,人们提出了所谓的模式的概念。所谓模式,是指在一个特定背景下,反复出现的问题解决方案。模式是经验的文档化。

软件模式的概念现在比较的广泛,涉及到分析,设计,体系结构,编码,测试,重构等软件构造生命期中的各个部分。这儿主要讨论的是设计模式,指的是在软件设计过程中反复出现的一些问题的解决方法了。不过我们一般在提到设计模式的时候,一般都是指GOF的经典书《Design Pattern--Elements of Reusable Object-Oriented Software》出现的23个模式,因而,它是具体的针对于面向对象软件设计过程的。

从全局上看来,模式代表了一种语言,一种被文档化的经验,甚至是一种文化。往往很多不方便描叙,或者描叙起来很复杂的问题,用模式语言来叙说,会让听者产生心领神会的感觉。当然,这需要交流双方都能够很好地把握模式语言的含义。然而,这并不是一件容易的事情。模式在各个人的理解上往往存在差异,这篇文章旨在从一个具体的应用角度:Java类库,来阐叙设计模式。并结合具体的例子,希望能够加深大家对设计模式的理解。

这儿说的Java类库,其实并没有局限于JDK本身,还包括了一些其他的类库中的例子,比如JAXP等(当然,下一个版本的JDK中也会包含JAXP了)。其实设计模式的思想现在应用的如此广泛,无论在什么样的设计中,只要稍微大一点的设计,都可以找到很多很多设计模式的踪迹,或者说都不可避免的用到设计模式。下面所讲的设计模式,大部分都是GOF的那部经典中出现过的23个模式,然而,还有一些,比如MVC,并不属于那里。一般的来讲,我们认为GOF的23个模式是一些中级的模式,在它下面还可以抽象出一些更为一般的低层的模式,在其上也可以通过组合来得到一些高级的模式。当然,这儿的低中高的区别,如同区别不同的语言一样,并没有优劣之分,仅仅是在应用层面上的区别。

Observer模式

Observer模式的功用,是希望两个(或多个)对象,我们称之为Subject和Observer,当一方的状态发生改变的时候,另一方能够得到通知。也就是说,作为Observer的一方,能够监视到Subject的某个特定的状态变化,并为之做出反应。一个简单的例子就是:当一个用户视图中的数据被用户改变后,后端的数据库能够得到更新,而当数据库被其他方式更新后,用户视图中的数据显示也会随之改变。


图一:Obverser模式的类图
图一:Obverser模式的类图

在JDK中实际上有一个对Observer模式的简单的实现:就是类java.util.Observerable和接口java.util.Observer。java.util.Observerable类对应于Subject,而java.util.Observer就是观察者了。JDK中并没有把这两个部分都设计为接口,而是让类java.util.Observerable提供了部分的实现,简化了许多编程的工作。当然,这也减少了一定的灵活性。

下面列出了Observer和Observeral的函数列表,及其简单的功能说明

java.util.Observer:
public void update(Observable obs, Object obj)

java.util.Observer 接口很简单,只定义了这一个方法,狭义的按照Observer模式的说法,Observer应该在这个方法中调用Subject的getXXX()方法来取得最新的状态,而实际上,你可以只是在其中对Subject的某些事件进行响应。这便是Java中的代理事件模型的一个雏形--对事件进行响应。只不过,在Observer模式中将事件特定化为某个状态/数据的改变了。

java.util.Observable

public void addObserver(Observer obs)

向Subject注册一个Observer。也就是把这个Observer对象添加到了一个java.util.Observable内部的列表中。在JDK中对于这个列表是简单的通过一个java.util.Vector类来实现的,而实际上,在一些复杂的Observer模式的应用中,需要把这个部分单另出来形成一个Manager类,来管理Subject和Observer之间的映射。这样,Subject和Observer进一步的被解藕,程序也会具有更大的灵活性。

public void deleteObserver(Observer obs)
从Subject中删除一个已注册了Observer的引用。

public void deleteObservers()
从Subjec中删除所有注册的Observer的引用。

public int countObservers()
返回注册在Subject中的Observer个数。

protected void setChanged()
设置一个内部的标志以指明这个Ovserver的状态已经发生改变。注意这是一个protected方法,也就是说只能在Observer类和其子类中被调用,而在其它的类中是看不到这个方法的。

protected void clearChanged()
清除上叙的内部标志。它在notifyObservers()方法内部被自动的调用,以指明Subject的状态的改变已经传递到Ovserver中了。

public boolean hasChanged()
确定Subject的状态是否发生了改变。

public void notifyObservers(Object obj)
它首先检查那个内部的标志,以判断状态是否改变,如果是的话,它会调用注册在Subject中的每个Observer的update()方法。在JDK中这个方法内部是作为synchronized来实现的,也就是如果发生多个线程同时争用一个java.util.Observerable的notifyObservers()方法的话,他们必须按调度的等待着顺序执行。在某些特殊的情况下,这会有一些潜在的问题:可能在等待的过程中,一个刚刚被加入的Observer会被遗漏没有被通知到,而一个刚刚被删除了的Observer会仍然收到它已经不想要了的通知。

public void notifyObservers()
等价于调用了notifyObservers(null)。

因而在Java中应用Observer就很简单了,需要做的是:让需要被观察的Subject对象继承java.util.Observerable,让需要观察的对象实现java.util.Observer接口,然后用java.util.Observerable的addObserver(Observer obj)方法把Observer注册到Subject对象中。这已经完成了大部分的工作了。然后调用java.util.Observerable的notifyObservers(Object arg)等方法,就可以实现Observer模式的机理。我们来看一个简单使用了这个模式的例子。这个例子有三个类:FrameSubject,DateSubject,FrameObject和EntryClass,FrameSubject中用户可以设置被观察的值,然后自动的会在FrameObject中显示出来,DateSubject封装被观察的值,并且充当Observer模式中的Subject。

public class FrameSubject extends JFrame {
…………..
  //因为无法使用多重继承,这儿就只能使用对象组合的方式来引入一个
//java.util.Observerable对象了。
  DateSubject subject=new DateSubject();
 //这个方法转发添加Observer消息到DateSubject。
  public void registerObserver(java.util.Observer  o){
    subject.addObserver(o);
 }
  //数据改变,事件被触发后调用notifyObservers()来通知Observer。
void jButton1_actionPerformed(ActionEvent e) {
    subject.setWidthInfo(Integer.parseInt(jTextField1.getText()));
    subject.setHeightInfo(Integer.parseInt(jTextField2.getText()));
    subject.notifyObservers();
  }
……………
}
public class DateSubject extends Observable {
    //封装被观察的数据
  private int widthInfo;
  private int heightInfo;
  public int getWidthInfo() {
    return widthInfo;
  }
  public void setWidthInfo(int widthInfo) {
this.widthInfo = widthInfo;
//数据改变后,setChanged()必须被调用,否则notifyObservers()方法会不起作用
this.setChanged();
  }
  public void setHeightInfo(int heightInfo) {
    this.heightInfo = heightInfo;
    this.setChanged();
  }
  public int getHeightInfo() {
    return heightInfo;
  }
}
public class FrameObserver extends JFrame implements java.util.Observer {
…………..
    //观察的数据
   int widthInfo=0;
   int heightInfo=0;
//在update()方法中实现对数据的更新和其它必要的反应。
  public void update(Observable o, Object arg) {
    DateSubject subject=(DateSubject) o;
    widthInfo=subject.getWidthInfo();
    heightInfo=subject.getHeightInfo();
    jLabel1.setText("The heightInfo from subject is:  ");
    jLabel3.setText(String.valueOf(heightInfo));
    jLabel2.setText("The widthInfo from subject is:  ");
    jLabel4.setText(String.valueOf(widthInfo));
  }
…………….
}
public class EntryClass {
    public static void main(String[] args) {
    ……………..
    FrameSubject frame = new FrameSubject();
FrameObserver frame2=new FrameObserver();
//在Subject中注册Observer,将两者联系在一起
frame.registerObserver(frame2);
…………..
frame.setVisible(true);
frame2.setVisible(true);
    ……………..
    }
}

我认为在JDK中这个Observer模式的实现,对于一般的Observer模式的应用,已经是非常的足够了的。但是一方面它用一个类来实现了Subject,另一方面它使用Vector来保存Subject对于Observer的引用,这虽然简化了编程的过程,但会限制它在一些需要更为灵活,复杂的设计中的应用,有时候(虽然这种情况不多),我们还不得不重新编写新的Subject对象和额外的Manager对象来实现更为复杂的Observer模式的应用。

 

小结:

这一部分主要的讨论了模式的概念。随着现代软件工业的不断进步,软件系统的规模的日益扩大,越来越需要对某些个不断出现的问题进行模式化思维,以成功的经验或者失败的教训来减少软件开发失败的风险。模式代表了一种文档化的经验,它为某一类的问题提供了最好(或者说很好)的解决方案,使得即使不是经验丰富的软件工程师,也能够根据模式来构建相对成功的系统。本节给出的一个Obverser模式的示例,比较好的说明了这一点。Obverser模式主要解决在对象间的状态映射或者镜像的问题。

在上一部分的内容中,我们讲到什么是模式,什么是设计模式,以及对一个设计模式Observer的详细阐叙。相信大家对于模式的概念应该是比较的理解了。这部分及以后的内容,将会步入正题,从Java类库的分析入手,来阐叙设计模式是如何应用到一个完美的设计中的。实际上,Java类库非常的庞杂,这儿不可能把所有能够找到的设计模式的例子一一列举,只是找了一些容易发现的例子。实际上也没有必要,因为只要对一个设计模式有足够的理解,对于它的具体应用而言,倒是一件不是很困难的事情。

Command模式

在设计一般用途的软件的时候,在C或者C++语言中,用的很多的一个技巧就是回调函数(Callback),所谓的回调函数,意指先在系统的某个地方对函数进行注册,让系统知道这个函数的存在,然后在以后,当某个事件发生时,再调用这个函数对事件进行响应。在C或者C++中,实现的回调函数方法是使用函数指针。但是在Java中,并不支持指针,因而就有了Command模式,这一回调机制的面向对象版本。

Command模式用来封装一个命令/请求,简单的说,一个Command对象中包含了待执行的一个动作(语句)序列,以执行特定的任务。当然,并不是随便怎么样的语句序列都可以构成一个Command对象的,按照Command模式的设计,Command对象和它的调用者Incvoker之间应该具有接口约定的。也就是说,Invoker得到Command对象的引用,并调用其中定义好的方法,而当Command对象改变(或者是对象本身代码改变,或者干脆完全另外的一个Command对象)之后,Invoker中的代码可以不用更改。这样,通过封装请求,可以把任务和任务的实现加以分离。


图二:Command模式的类图
图二:Command模式的类图

而对于请求的处理又有两种不同的方法,一种是Command只充当代理,将请求转发给某个接受者对象,还有一种是Command对象自己处理完所有的请求操作。当然,这只是两个极端,更多的情况是Command完成一部分的工作,而另外的一部分这则交给接受者对象来处理。

在新的JDK的代理事件模型中,就可以看作是这样的一个Command模式。在那个模型中,一个事件监听者类EventListener监听某个事件,并根据接口定义,实现特定的操作。比如,当用Document对象的addDocumentListener(DocumentListener listener) 方法注册了一个DocumentListener后,以后如果在Document对象中发生文本插入的事件,DocumentListener中实现的insertUpdate(DocumentEvent e)方法就会被调用,如果发生文本删除事件,removeUpdate(DocumentEvent e)方法就会被调用。怎么样,想想看,这是不是一个Command模式的应用呢?

然而,最经典的Command模式的应用,莫过于Swing中的Action接口。Action实际上继承的是ActionListener,也就是说,它也是一个事件监听者(EventListener)。但是Action作为一种ActionListener的扩展机制,提供了更多的功能。它可以在其中包含对这个Action动作的一个或者多个文字的或图标的描叙,它提供了Enable/Disable的功能许可性标志。并且,一个Action对象可以被多个Invoker,比如实现相同功能的按钮,菜单,快捷方式所共享。而这些Invoker都知道如何加入一个Action,并充分利用它所提供的扩展机制。可以说,在这儿Action更像一个对象了,因为它不仅仅提供了对方法的实现,更提供了对方法的描叙和控制。可以方便的描叙任何的事务,这更是面向对象方法的威力所在。

下面我们看一个Command模式的应用的例子。假设要实现这样的一个任务:Task Schedule。也就是说,我想对多个任务进行安排,比如扫描磁盘,我希望它每1个小时进行一次,而备份数据,我希望它半个小时进行一次,等等等等。但是,我并不希望作为TaskSchedule的类知道各个任务的细节内容,TaskSchedule应该只是知道Task本身,而对具体的实现任务的细节并不理会。因而在这儿,我们就需要对TaskSchedule和Task进行解耦,将任务和具体的实现分离出来,这不正是Command模式的用武之地吗?


图三:Command模式的应用例子
图三:Command模式的应用例子

程序清单:

//抽象的Task接口,作为回调的Command模式的主体
public interface Task {
  public void taskPerform();
}
//具体的实现了Task接口的子类,实现特定的操作。
public class BackupTask implements Task{
  public void taskPerform(){
    System.out.println("Backup Task has been performed");
  }
}
//具体的实现了Task接口的子类,实现特定的操作。
public class ScanDiskTask implements Task{
  public void taskPerform(){
    System.out.println("ScanDisk Task has been performed");
  }
}
//一个封装了Task的一个封装类,提供了一些与Task相关的内容,也可以把这些内容
//这儿不过为了突出Command模式而把它单另出来,实际上可以和Task合并。
public class TaskEntry {
  private Task task;
  private long timeInterval;
  private long timeLastDone;
  public Task getTask() {
    return task;
  }
  public void setTask(Task task) {
    this.task = task;
  }
  public void setTimeInterval(long timeInterval) {
    this.timeInterval = timeInterval;
  }
  public long getTimeInterval() {
    return timeInterval;
  }
  public long getTimeLastDone() {
    return timeLastDone;
  }
  public void setTimeLastDone(long timeLastDone) {
    this.timeLastDone = timeLastDone;
  }
  public TaskEntry(Task task,long timeInteral){
    this.task=task;
    this.timeInterval =timeInteral;
  }
}
//调度管理Task的类,继承Thread只是为了调用其sleep()方法,
//实际上,如果真的作Task调度的话,每个Task显然应该用单独的Thread来实现。
public class TaskSchedule extends java.lang.Thread {
  private java.util.Vector taskList=new java.util.Vector();
  private long sleeptime=10000000000l;//最短睡眠时间
  public void addTask(TaskEntry taskEntry){
    taskList.add(taskEntry);
    taskEntry.setTimeLastDone(System.currentTimeMillis());
    if (sleeptime>taskEntry.getTimeInterval()) 
    sleeptime=taskEntry.getTimeInterval();
  }
  //执行任务调度
  public void schedulePermorm(){
    try{
      sleep(sleeptime);
      Enumeration e = taskList.elements();
      while (e.hasMoreElements()) {
        TaskEntry te = (TaskEntry) e.nextElement();
        if (te.getTimeInterval() + te.getTimeLastDone() < 
                System.currentTimeMillis()) {
          te.getTask().taskPerform();
          te.setTimeLastDone(System.currentTimeMillis());
          }
      }
    }catch (Exception e1){
      e1.printStackTrace();
    }
  }
  public static void main (String args[]){
    TaskSchedule schedule=new TaskSchedule();
    TaskEntry taks1=new TaskEntry(new ScanDiskTask(),10000);
    TaskEntry taks2=new TaskEntry(new BackupTask(),3000);
    schedule.addTask(taks1);
    schedule.addTask(taks2);
    while (true){
        schedule.schedulePermorm();
      }
  }
}

程序本身其实没有多大的意义,因而,程序在编码的时候也只是用的最简单的方法来实现的,如果要做一个真正的TaskSchedule的话,这个程序除了结构上的,其它没有什么好值得参考的了。





回页首

AbstractFactory 和 FactoryMethod

基本上来说,AbstractFacotry模式和FactoryMethod模式所作的事情是一样的,都是用来创建与具体程序代码无关的对象,只是面对的对象层次不一样,AbstractFactory创建一系列的对象组,这些对象彼此相关。而FactoryMethod往往只是创建单个的对象。

再开始这两个模式之前,有必要先陈叙一个在设计模式,或者说在整个面向对象设计领域所遵循的一个设计原则:针对接口编程,而不是针对具体的实现。这个思想可以说是设计模式的基石之一。现在的很多对象模型,比如EJB,COM+等等,无不是遵照这个基本原则来设计的。针对接口编程的好处有很多,通过接口来定义对象的抽象功能,方便实现多态和继承;通过接口来指定对象调用之间的契约,有助于协调对象之间的关系;通过接口来划分对象的职责,有助于寻找对象,等等。

AbstractFactory和FactoryMethod,还有其他的一些创建型的设计模式,都是为了实现这个目的而设计出来的。它们创建一个个符合接口规范的对象/对象组,使得用同一个Factory创建出来的对象/对象组可以相互替换。这种可替换性就称为多态,是面向对象的核心思想之一。而多态,是通过动态绑定来实现的。


图四:AbstractFactory模式的类图
图四:AbstractFactory模式的类图

客户程序使用具体的AbstractFacotry对象(ConcreteFactoryX)调用CreateProductX()方法,生成具体的ConcreteProductX。每个AbstractFactory所能生成的对象,组成一个系列的对象组,他们可能是相互相关的,紧耦合的。应为各个AbstractFactory对象所能够生成的对象组都遵循一组相同的接口(AbstractProductX),因而当程序是针对接口进行编程的时候,这些实现方法各不相同的对象组却可以相互的替换。

实际上,客户程序本身并不关心,也不知道具体使用的是那些产品对象。它甚至能够不理会到底是哪个AbstractFactory对象被创建。在这种情况下,你可能会问,那么一个AbstractFactory又该如何生成呢?这时候,就该用该FactoryMethod模式了。

前面有说过,AbstractFactory着重于创建一系列相关的对象,而这些对象与具体的AbstractFactory相关。而FactoryMethod则着重于创建单个的对象,这个对象决定于一个参数或者一个外部的环境变量的值;或者,在一个抽象类中定义一个抽象的工厂方法(也成为虚拟构造器),然后再实现的子类中返回具体的产品对象。

FactoryMethod可以借助一个参数或者一个外部的标志来判断该具体生成的哪一个子类的实例。比如对于不同的具体情况,需要有不同的AbstractFactory来生成相应的对象组。这时候,FactoryMethod通常作为一个AbstractFactory对象的静态方法出现,使得其能够在具体的对象被创建之前就能够被调用。

在JAVA中,应用这两个模式的地方实在太多,下面我们来看一个在JAXP中这两个模式的应用。JAXP是用来处理XML文档的一个API。我们都知道XML文件的一个特点就是其平台无关,流通性能好。因而往往也需要处理他们的程序具有更好的平台无关性。Java语言是一个比较好的平台无关语言,可以作为一个选择,但是对XML进行解析的解析器确有很多。有时候需要在不同的解析器之间进行切换,这时候,JAXP的良好设计就能够体现出来了。它能够允许在不同解析器之间竟进行切换的时候,不用更改程序的代码。

我们就拿JAXP中的DOM解析器来作为例子,来例示AbstractFactory和FactoryMethod的用法。


图五:DOM中工厂模式的应用
图五:DOM中工厂模式的应用

上图中为了方便起见,只画出了抽象类和接口,DocumentBuilderFactory和DocumentBuilder都是抽象类。

DocumentBuilderFactory的静态方法newInstance()方法根据一个外部的环境变量javax.xml.parsers.DocumentBuilderFactory的值来确定具体生成DocumentBuilderFactory的哪一个子类。这儿的newInstance()是一个工厂方法。当DocumentBuilderFactory被创建后,可以调用其newDocumentBuilder()来创建具体一个DocumentBuilder的子类。然后再由DocumentBuilder来生成Document等DOM对象。

下面是创建一个DOM对象的代码片段:

       //第一步:创建一个DocumentBuilderFactory。
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        //第二步:创建一个DocumentBuilder
        DocumentBuilder db = dbf.newDocumentBuilder();
        //第三步:解析XML文件得到一个Document对象
        Document doc = db.parse(new File(filename));

在这儿,DocumentBuilder,Document,Node等等对象所组成的一个产品组,是和具体的DocumentBuilderFactory相关的。这也就是AbstractFactory模式的含义所在。

当然,FactoryMethod模式应用的很广。这是一个具体的例子,但他不应该限制我们的思路,FactoryMethod和AbstractFactory是解决面向对象设计中一个基本原则--面向接口编程的主要方法。





回页首

Singleton模式

Singleton模式要解决的是对象的唯一性问题。由Singleton模式创建的对象在整个的应用程序的范围内,只允许有一个对象的实例存在。这样的情况在Java程序设计的过程中其实并不少见,比如处理JDBC请求的连接池(Connection Pool),再比如一个全局的注册表(Register),等等,这都需要使用到Singleton,单件模式。

在Java中,最简单的实现Singleton模式的方法是使用static修饰符,static可以用在内部类上,也可以用在方法和属性上,当一个类需要被创建成Singleton时,可以把它所有的成员都定义成static,然后再用final和private来修饰其构造函数,使其不能够被创建和重载。这在程序语法上保证了只会有一个对象实例被创建。比如java.util.Math就是这样的一个类。

而Singleton模式所作的显然要比上面介绍的解决方法要复杂一些,也更为安全一些。它基本的思路也还是使用static变量,但是它用一个类来封装这个static变量,并拦截对象创建方法,保证只有一个对象实例被创建,这儿的关键在于使用一个private或者protected的构造函数,而且你必须提供这样的一个构造函数,否则编译器会自动的为你创建一个public的构造函数,这就达不到我们想要的目的了。

public class Singleton {
   //保存唯一实例的static变量
   static private Singleton _instance = null;
/*为了防止对象被创建,可以为构造函数加上private修饰符,但是这同样也防止了子类的对象被创建,因而,可以选用protected修饰符来替代private。*/
   protected Singleton() {
     // ...
   }
    //static方法用来创建/访问唯一的对象实例,这儿可以对对象的创建进行控制,使得可//以很容易的实现只允许指定个数的对象存在的泛化的Singleton模式。
   static public Singleton instance() {
      if(null == _instance) {
         _instance = new Singleton();
      }
      return _instance;
   }
//  ...
}

对象创建的方法,除了使用构造函数之外,还可以使用Object对象的clone()方法,因而在Singleton中也要注意这一点。如果Singleton类直接继承于Object,因为继承于Object的clone()方法仍保留有其protected修饰,因而不能够被其他外部类所调用,所以可以不用管它,但是如果Singleton继承于一个其他的类,而这个类又有重载clone()方法,这时就需要在Singleton中再重载clone()方法,并在其中抛出CloneNotSupportedException,这样就可以避免多个Singleton的实例被创建了。

在JDK1.2以前的版本中使用Singleton模式的时候有一些需要额外注意的地方,因为Singleton类并没有被任何其他的对象所引用,所以这个类在创建后一段时间会被unload,Singleton类的静态方法就会出现问题,这是由于Java中垃圾收集机制造成的。解决的方法也很容易,只需要为其创建一个引用就行了。而在JDK1.2以后的版本中,Sun重新定义了Java规范,改正了其垃圾收集机制中的一些问题,这个问题也就不复存在了,这儿指出只是为了提起大家的主意。





回页首

小结:

Command模式用来封装请求,也描叙了一致性的发送请求的接口,允许你配置客户端以处理不同的请求,为程序增添了更大的灵活性。Singleton模式为提供对象的单一入口提供了帮助。AbstractFactory和FactoryMethod模式在功能上比较类似,都是用来处理对象的创建的,但应用在不同的层面上。在创建型模式中,还有Builder模式和Prototype模式,这儿不打算详细的讨论了,简单的说,Builder模式用来处理对象创建的细节。在两个工厂模式中都没有涉及到对象创建的具体细节,都是通过接口来返回一个给定类型的对象。而Builder模式则需要对创建一个给定类型对象的过程进行建模。这对创建复杂对象时很有用,使得创建对象的算法独立于对象各个组成部分的创建。而Prototype模式使用原型机制,通过创建简单原型的拷贝来创建对象。

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值