在Java中应用设计模式--Factory Method

news/2024/7/4 0:53:41 标签: 设计模式, java, string, class, 产品, exception
class="baidu_pl">
class="article_content clearfix">
class="htmledit_views">  
class="tags" href="/tags/SheJiMoShi.html" title=设计模式>设计模式中 ,Factory Method 也是比较简单的一个 , 但应用非常广泛 ,EJB,RMI,COM,CORBA,Swing 中都可以看到此模式的影子 , 它是最重要的模式之一 . 在很多地方我们都会看到 xxxFactory 这样命名的类 , 那么 , 什么是 Factory Method, 为什么要用这个模式 , 如何用 Java 语言来实现该模式 , 这就是本文想要带给大家的内容 .
   基本概念
   Factory Method 是一种创建性模式 , 它定义了一个创建对象的接口 , 但是却让子类来决定具体实例化哪一个类 . 当一个类无法预料要创建哪种类的对象或是一个类需要由子类来指定创建的对象时我们就需要用到 Factory Method 模式了 . 简单说来 ,Factory Method 可以根据不同的条件产生不同的实例 , 当然这些不同的实例通常是属于相同的类型 , 具有共同的父类 .Factory Method 把创建这些实例的具体过程封装起来了 , 简化了客户端的应用 , 也改善了程序的扩展性 , 使得将来可以做最小的改动就可以加入新的待创建的类 . 通常我们将 Factory Method 作为一种标准的创建对象的方法 , 当发现需要更多的灵活性的时候 , 就开始考虑向其它创建型模式转化
   简单分析
  图 1 Factory Method 模式的结构图 , 这里提供了一些术语 , 让我们可以进行更方便的描述 :

1: Factory Method 模式结构
   1 Product: 需要创建的产品的抽象类 .
   2 ConcreteProduct: Product 的子类 , 一系列具体的产品 .
   3 Creator: 抽象创建器接口 , 声明返回 Product 类型对象的 Factory Method.
   4 ConcreteCreator: 具体的创建器 , 重写 Creator 中的 Factory Method, 返回 ConcreteProduct 类型的实例 .
  由此可以清楚的看出这样的平行对应关系 : Product <====> Creator ; ConreteProduct <====> ConreteCreator
  抽象产品对应抽象创建器 , 具体产品对应具体创建器 . 这样做的好处是什么呢 ? 为什么我们不直接用具体的产品和具体的创建器完成需求呢 ? 实际上我们也可以这样做 . 但通过 Factory Method 模式来完成 , 客户 (client) 只需引用抽象的 Product Creater, 对具体的 ConcreteProduct ConcreteCreator 可以毫不关心 , 这样做我们可以获得额外的好处 :
  首先客户端可以统一从抽象创建器获取产生的实例 ,Creator 的作用将 client 产品创建过程分离开来 , 客户不用操心返回的是那一个具体的产品 , 也不用关心这些产品是如何创建的 . 同时 ,ConcreteProduct 也被隐藏在 Product 后面 ,ConreteProduct 继承了 Product 的所有属性 , 并实现了 Product 中定义的抽象方法 , 按照 Java 中的对象造型 (cast) 原则 , 通过 ConcreteCreator 产生的 ConcreteProduct 可以自动的上溯造型成 Product. 这样一来 , 实质内容不同的 ConcreteProduct 就可以在形式上统一为 Product, 通过 Creator 提供给 client 来访问 .
  其次 , 当我们添加一个新的 ConcreteCreator , 由于 Creator 所提供的接口不变 , 客户端程序不会有丝毫的改动 , 不会带来动一发而牵全身的灾难 , 这就是良好封装性的体现 . 但如果直接用 ConcreteProduct ConcreteCreator 两个类是无论如何也做不到这点的 . 优良的面向对象设计鼓励使用封装 (encapsulation) 和委托 (delegation), Factory Method 模式就是使用了封装和委托的典型例子 , 这里封装是通过抽象创建器 Creator 来体现的 , 而委托则是通过抽象创建器把创建对象的责任完全交给具体创建器 ConcreteCreator 来体现的 .
  现在 , 请再回头看看基本概念中的那段话 , 开始也许觉得生涩难懂 , 现在是不是已经明朗化了很多 .
  下面让我们看看在 Java 中如何实现 Factory Method 模式 , 进一步加深对它的认识 .
   具体实施
  先说明一点 , Factory Method 模式创建对象并不一定会让我们的代码更短 , 实事上往往更长 , 我们也使用了更多的类 , 真正的目的在于这样可以灵活的 , 有弹性的创建不确定的对象 . 而且 , 代码的可重用性提高了 , 客户端的应用简化了 , 客户程序的代码会大大减少 , 变的更具可读性 .
  标准实现 : 这里我采用 Bruce Eckel 用来描述 OO 思想的经典例子 Shape. 这样大家会比较熟悉一些 . 我完全按照图 1 中所定义的结构写了下面的一段演示代码 . 这段代码的作用是创建不同的 Shape 实例 , 每个实例完成两个操作 :draw erase. 具体的创建过程委托給 ShapeFactory 来完成 .
   1.a 首先定义一个抽象类 Shape, 定义两个抽象的方法 .
abstract class Shape {
  //
勾画 shape
  public abstract void draw();
  //
擦去 shape
  public abstract void erase();
  public String name;
  public Shape(String aName){
    name = aName;
  }
}
   1.b 定义 Shape 的两个子类 : Circle, Square, 实现 Shape 中定义的抽象方法
// 圆形子类
class Circle extends Shape {
  public void draw() {
    System.out.println("It will draw a circle.");
  }
  public void erase() {
    System.out.println("It will erase a circle.");
  }
  //
构造函数
  public Circle(String aName){
    super(aName);
  }
}
// 方形子类
class Square extends Shape {
  public void draw() {
    System.out.println("It will draw a square.");
  }
  public void erase() {
    System.out.println("It will erase a square.");
  }
  //
构造函数
  public Square(String aName){
    super(aName);
  }
}
   1.c 定义抽象的创建器 ,anOperation 调用 factoryMethod 创建一个对象 , 并对该对象进行一系列操作 .
abstract class ShapeFactory { 
  protected abstract Shape factoryMethod(String aName);
  //
anOperation 中定义 Shape 的一系列行为
public void anOperation(String aName){
    Shape s = factoryMethod(aName);
    System.out.println("The current shape is: " + s.name);
    s.draw();
    s.erase();
  }
}
   1.d 定义与 circle square 相对应的两个具体创建器 CircleFactory,SquareFactory, 实现父类的 methodFactory 方法
// 定义返回 circle 实例的 CircleFactory
class CircleFactory extends ShapeFactory {
  //
重载 factoryMethod 方法 , 返回 Circle 对象
  protected Shape factoryMethod(String aName) {
    return new Circle(aName + " (created by CircleFactory)");
  }
}
 
//
定义返回 Square 实例的 SquareFactory
class SquareFactory extends ShapeFactory {
  //
重载 factoryMethod 方法 , 返回 Square 对象
protected Shape factoryMethod(String aName) {
    return new Square(aName + " (created by SquareFactory)");
  }
}
   1.e 测试类 : 请注意这个客户端程序多么简洁 , 既没有罗嗦的条件判断语句 , 也无需关心 ConcreteProduct ConcreteCreator 的细节 ( 因为这里我用 anOperation 封装了 Product 里的两个方法 , 所以连 Product 的影子也没看见 , 当然把 Product 里方法的具体调用放到客户程序中也是不错的 ).
class Main {
  public static void main(String[] args){
    ShapeFactory sf1 = new SquareFactory();
    ShapeFactory sf2 = new CircleFactory();
    sf1.anOperation("Shape one");
    sf2.anOperation("Shape two");
  }
  运行结果如下 :
   The current shape is: Shape one (created by SquareFactory)
   It will draw a square.
   It will erase a square.
   The current shape is: Shape two (created by CircleFactory)
   It will draw a circle.
   It will erase a circle.
  参数化的 Factory Method: 这种方式依靠指定的参数作为标志来创建对应的实例 , 这是很常见的一种办法 . 比如 JFC 中的 BorderFactory 就是个很不错的例子 . 以下的这个例子是用字符串作为标记来进行判断的 , 如果参数的类型也不一样 , 那就可以用到过载函数来解决这个问题 , 定义一系列参数和方法体不同的同名函数 , 这里 class="tags" href="/tags/JAVA.html" title=java>java.util.Calendar.getInstance() 又是个极好的例子 . 参数化的创建方式克服了 Factory Method 模式一个最显著的缺陷 , 就是当具体产品比较多时 , 我们不得不也建立一系列与之对应的具体构造器 . 但是在客户端我们必须指定参数来决定要创建哪一个类 .
   2.a 我们在第一种方法的基础上进行修改 , 首先自定义一个的异常 , 这样当传入不正确的参数时可以得到更明显的报错信息 .
class NoThisShape extends Exception {
  public NoThisShape(String aName) {
    super(aName);
  }
}
   2.b 去掉了 ShapeFactory 的两个子类 , 改为由 ShapeFactory 直接负责实例的创建 . ShapeFactory 自己变成一个具体的创建器 , 直接用参数化的方法实现 factoryMethod 返回多种对象 .
abstract class ShapeFactory { 
  private static Shape s;
  private ShapeFactory() {}
   
  static Shape factoryMethod(String aName, String aType) throws NoThisShape{
    if (aType.compareTo("square")==0)
      return new Square(aName);
    else if (aType.compareTo("circle")==0)
      return new Circle(aName);
    else throw new NoThisShape(aType); 
  }
 
  //
anOperation 中定义 Shape 的一系列行为
  static void anOperation(String aName, String aType) throws NoThisShape{
    s = factoryMethod(aName, aType);
    System.out.println("The current shape is: " + s.name);
    s.draw();
    s.erase();
  }
}
   2.c 测试类 : 这里客户端必须指定参数来决定具体创建哪个类 . 这个例子里的 anOperation 是静态函数 , 可以直接引用 .
class Main {
  public static void main(String[] args) throws NoThisShape{
    ShapeFactory.anOperation("Shape one","circle");
    ShapeFactory.anOperation("Shape two","square");
    ShapeFactory.anOperation("Shape three", "delta");
  }
}
  运行结果如下 :
   The current shape is: Shape one
   It will draw a circle.
   It will erase a circle.
   The current shape is: Shape two
   It will draw a square.
   It will erase a square.
   Exception in thread "main" NoThisShape: delta
        at ShapeFactory.factoryMethod(ShapeFactory.class="tags" href="/tags/JAVA.html" title=java>java:10)
        at ShapeFactory.anOperation(ShapeFactory.class="tags" href="/tags/JAVA.html" title=java>java:15)
        at Main.main(Main.class="tags" href="/tags/JAVA.html" title=java>java:5)
   动态装载机制 :
  有的时候我们会把 ConcreteProduct 的实例传给创建器作为参数 , 这种情况下 , 如果在创建器里完成创建过程 , 就必须判断参数的具体类型 ( instanceof), 然后才能产生相应的实例 , 那么比较好的做法是利用 Java 的动态装载机制来完成这件事 . 比如 :
  我们得到一个 Shape 的子类 s, 但不知道具体是那个子类 , 就可以利用 Class 类自带的方法 newInstance() 得到实例
   return (Shape)s.getClass().newInstance();
  这种方法有兴趣得读者可以自己尝试 , 限于篇幅 , 不写具体代码出来了 .
   后话 :
  看完这篇文章后 , 相信读者对 Factory Method 模式有一个比较清楚的了解了 . 我想说的是 , 我们不仅应该关心一个具体的模式有什么作用 , 如何去实现这个模式 , 更应该透过现象看本质 , 不但知其然 , 还要知其所以然 . 要通过对模式的学习加深对面向对象思想的理解 , 让自己的认识得到升华 .Factory Method 模式看似简单 , 实则深刻 . 抽象 , 封装 , 继承 , 委托 , 多态 , 针对接口编程等面向对象中的概念都在这里得到了一一的体现 . 只有抓住了它的本质 , 我们才能够不拘于形式的灵活运用 , 而不是为了使用模式而使用模式 . 

http://www.niftyadmin.cn/n/612609.html

相关文章

使用jar命令创建可执行的jar包

1、创建可执行的jar包。手工写manifest.mf文件(jar命令自动生成的MANIFEST.MF文件中不会包含Main-Class属性)&#xff0c;举例说明&#xff1a;目录结构&#xff1a;mymanifest.mf //该文件可以随意放置,只要在执行jar命令时指定mymanifest.mf文件所在位置.-src??-test???…

使用JNDI和 企业级JAVABEANS

一.在Tomcat中配置JNDI对象 解决方案&#xff1a;在servlet.xml或表示web应用程序的XML文件中创建Resource和ResourceParam元素。然后向web.xml中添加一个resource-env-ref元素。tomcat的jndi对象在conf/server.xml文件&#xff0c;就要在这个xml文件中配置jndi对象。例&…

el-progress进度条 内部显示数字的位置_显示器接头VGA DVI HDMI DP 区别及优缺点

VGA VGA端子(Video Graphics Array (VGA) connector)&#xff0c;(其他名字RGB端子&#xff0c;D-sub 15&#xff0c;或mini D15)是一种3排共15针的DE-15。VGA端子通常在电脑的显卡、显示器及其他设备。是用作发送模拟信号。VGA接口也称为D-Sub接口。IBM于1987年提出的一个使用…

android顶部渐变显示,Android实现直播聊天区域中顶部的渐变效果

Android实现直播聊天区域中顶部的渐变效果发布时间&#xff1a;2020-10-15 08:24:18来源&#xff1a;脚本之家阅读&#xff1a;102作者&#xff1a;MG屠夫背景在4月份开发直播时&#xff0c;有一个需求&#xff0c;需要实现一个RecylerView顶部渐变的效果实际效果解决思路图层重…

经典:Java及相关字符集编码问题研究

1. 概述 本文主要包括以下几个方面&#xff1a;编码基本知识&#xff0c;java&#xff0c;系统软件&#xff0c;url&#xff0c;工具软件等。 在下面的描述中&#xff0c;将以"中文"两个字为例&#xff0c;经查表可以知道其GB2312编码是"d6d0 cec4"&…

Eclipse中CVS使用建议

Eclipse中CVS使用建议1、在Eclipse同CVS服务器同步之前&#xff0c;要先刷新&#xff0c;否则可能会因为在Eclipse外面编辑的文件&#xff0c;Eclipse中没有刷新而导致同步报错&#xff0c;可以将Eclipse设置为自动刷新&#xff0c;操作方法&#xff1a;Window->Preferences…

MD5加密算法

介绍MD5加密算法基本情况MD5的全称是Message-Digest Algorithm 5&#xff0c;在90年代初由MIT的计算机科学实验室和RSA Data Security Inc发明&#xff0c;经MD2、MD3和MD4发展而来。Message-Digest泛指字节串(Message)的Hash变换&#xff0c;就是把一个任意长度的字节串变换成…

珍藏的最全的windows操作系统快捷键

珍藏的最全的windows操作系统快捷键 一、常见用法&#xff1a; F1 显示当前程序或者windows的帮助内容。 F2 当你选中一个文件的话&#xff0c;这意味着“重命名” F3 当你在桌面上的时候是打开“查找&#xff1a;所有文件” 对话框 F10或ALT 激活当前程序的菜单栏 windows键…