zoukankan      html  css  js  c++  java
  • 静态工厂方法

    开始读《Effective Java》第二版,对于第一小节,上网找了两篇比较好的说明静态工厂方法的文章,初步理解:

    所谓的静态工厂方法,就是一个静态方法,可以返回类的实例,可以避免每次都new一个对象。


            创建类的实例最常见的是new 除此外还可以使用静态工厂方法,来封装实例的细节,并且能控制实例的数量,减轻jvm的堆栈中的压力。 

    静态工厂方法与用new语句调用的构造方法相比,有以下区别: 

    1:构造方法的名字必须与类名相同。这一特性的优点是符合Java语言的规范,缺点是类的所有重载的构造方法的名字都相同,不能从名字上区分每个 重载方法,容易引起混淆。静态工厂方法的方法名可以是任意的,这一特性的优点是可以提高程序代码的可读性,在方法名中能体现与实例有关的信息。

    1. eg:public class TottiStaticDemo{   
    2.   
    3.             private static final TottiStaticDemo demo = new TottiStaticDemo();   
    4.   
    5.             public static TottiStaticDemo getInstance(){   
    6.   
    7.                    return demo;   
    8.   
    9.             }   
    10.   
    11.             public void outPutStatic(){   
    12.   
    13.                   System.out.println("Test Static Factory Class!");   
    14.   
    15.             }   
    16.   
    17.        }   


        
    1. 如果需要在其他类中调用TottiStaticDemo类中的outPutStatic方法,那么只需要使用如下语句即可:   
    2.   
    3.    TottiStaticDemo.getInstance().outPutStatic();而不必使用new关键字。   



    2:每次执行new语句时,都会创建一个新的对象。而静态工厂方法每次被调用的时候,是否会创建一个新的对象完全取决于方法的实现。

    3:new语句只能创建当前类的实例,而静态工厂方法可以返回当前类的子类的实例,这一特性可以在创建松耦合的系统接口时发挥作用。 

    ***静态工厂方法最主要的特点是:每次被调用的时候,不一定要创建一个新的对象。利用这一特点,静态工厂方法可用来创建以下类的实例。 

    <1> 单例类:只有惟一的实例的类。 

    <2>枚举类:实例的数量有限的类。 

    <3>具有实例缓存的类:能把已经创建的实例暂且存放在缓存中的类。 

    <4>具有实例缓存的不可变类:不可变类的实例一旦创建,其属性值就不会被改变。






     对于类而言,为了让客户端获取它自身的一个实例,最常用的方法就是提供一个公有的构造器.还有一种方法,也应该占有一席之地.类可以提供一个公有的静态工厂方法,它是一个返回类的实例的静态方法.下面是一个来自Boolean 类的简单示例.这个方法将boolean 基本类型值转换成了一个Boolean 对象引用:


    1 public static Boolean valueOf(boolean b) {
    2     return b ? Boolean.TRUE : Boolean.FALSE;
    3 }


    提供静态工厂方法而不是公有的构造器,这样做有几大优势.

    1) 它们有名称.

    如果构造器的参数本身没有确切的描述正被返回的对象,那么具有适当名称的静态工厂方会更容易使用,产生的客户端代码也更易于阅读.

    例如,构造器BigInteger(int,int,Randon) 返回的BigInteger 可能为素数,如果用名为BigInteger.probablePrime的静态工厂方法来表示,显得更为清楚.(1.4的发型版本最终增加了这个方法)

     

    2) 不必在每次调用它们的时候都创建一个新对象.

    这使得不可变类可以使用预先构建好的实例,或者将构建好的实例缓存起来,进行重复利用,从而避免创建不必要的重复对象.Boolean.valueOf(boolean)方法说明了这项技术:它从来不创建对象.

    静态工厂方法能够为重复的调用返回相同的对象,这样有助于类总能严格控制在某个时刻哪些实例应该存在.这种类被称为实例受控的类(instance-controlled).编写实例受控的类有几个原因:实例受控使得类可以确保它是一个Singleton或者是不可实例化的;使得不可变的类可以确保不会存在两个相等的实例,即当且仅当a==b的时候才有a.equals(b)为true.如果类保证了这一点,客户端就可以使用==操作符来代替equals(Object)方法,从而提升性能.枚举类型保证了这一点.

     

    3) 它们可以返回原返回类型的任何子类型的对象.

    这种灵活性的一种应用是,API可以返回对象,同时又不会使对象的类变成公有的.这种技术适用于基于接口的框架,因为在这种框架中,接口为静态工厂方法提供了自然返回类型.接口不能有静态方法,因此按照惯例,接口Type的静态工厂方法被放在一个名为Types的不可实例化的类中.

    例如,Java Collections Framework的集合接口有32个便利实现,几乎所有的这些实现都通过静态工厂方法在一个不可实例化的类(Java.util.Collections)中导出.

    静态工厂方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不必存在.这种灵活的静态工厂方法构成了服务提供者框架(Service Provider Framework)的基础,例如JDBC(Java Databse Connectivity) API.

    ◇ 服务提供者框架: 多个服务提供者实现一个服务,系统为服务提供者提供多个实现,并把他们从多个实现中解耦出来

    服务提供者框架中有三个重要的组件:

    ① 服务接口(Service Interface):这是提供者实现的;[JDBC:Collection]

    ② 提供者注册API(Provider Registration API):这是系统用来注册实现; [JDBC:Driver.registerDriver]

    ③ 服务访问API(Provider Access API):是客户端用来获取服务的实例的.服务访问API是”灵活的静态工厂”,构成了服务提供者框架的基础. [JDBC:Driver.getConnection]

     服务提供者框架的第四个组件是可选的:服务提供者接口(Service Provider Interface),这些提供这负责创建其服务实现的实例.[JDBC:Driver]

    下面是一个简单的实现,包含一个服务提供者接口和一个默认提供者: 

     

    01 // Service Provider framework sketch
    02
    03 // Service interface
    04 public interface Service{
    05     ... //Service-specific methods go here
    06 }
    07
    08 // Service provider interface
    09 public interface Provider{
    10     Service newService();
    11 }
    12
    13 // Noninstantiable class for service registration and access
    14 public class Services{
    15     private Services(){ }
    16
    17     // Maps service names to services
    18     private static final Map<String, Provider> providers = new HashMap<String, Provider>();
    19      
    20     public static final String DEFAULT_PROVIDER_NAME = "<def>";
    21
    22     // Provider registration API
    23     public static void registerDefaultProvider(Provider p){
    24         registerProvider(DEFAULT_PROVIDER_NAME, p);
    25     }
    26     public static void registerProvider(String name, Provider p){
    27         providers.put(name, p);
    28     }
    29      
    30     // Service access API
    31     public static Service newInstance(){
    32         newInstance(DEFAULT_PROVIDER_NAME);
    33     }
    34     public static Service newInstance(String name){
    35         Provider p = providers.get(name);
    36         if(p == null){
    37             throw new IllegalArgumentException("No provider registered with name: " + name);
    38         }
    39         return p.newService();
    40     }
    41 }


    4) 在创建参数化类型实例的时候,它们使代码变得更加简洁.

    遗憾的是,在调用参数化类的构造器时,即使类型参数很明显,也必须指明.这通常要求接连两次提供类型参数:


    1 Map<String, List<String>> m = new HashMap<String, List<String>>();


    不过有了静态工厂方法,编译器就可以替你找到类型参数.这被称做类型推导(type inference).例如,假设HashMap提供了这个静态工厂:


    1 public static <K, V> HashMap<K, V> newInstance(){
    2     return new HashMap<K, V>();
    3 }


    就可以用下面这句简洁的代码代替上面繁琐的声明: 

    1 Map<String, List<String>> m = HashMap.newInstance();

    总有一天,Java将能够在构造器调用以及方法调用中执行这种类型推导,但到发行版本1.6为止还无法这么做.(事实上,Java 7 已经加入了类型推导).

  • 相关阅读:
    Linux网络配置之虚拟网卡的配置(Red Hat 6.5)
    Linux网络配置之虚拟网卡的配置(ubuntu 16.04)
    Red Hat 6.5 本地yum源的配置
    Red Hat 6.5 网络yum源的配置
    动手学深度学习 | 物体检测和数据集 | 40
    软件测试兼职平台
    Android共性问题
    软件测试人员应该知道的
    面试
    设计测试用例时需要考虑的思路
  • 原文地址:https://www.cnblogs.com/allenzhaox/p/3201818.html
Copyright © 2011-2022 走看看