用静态工厂代替构造器思想(很详细,很详细,很详细)

  • 背景:对于类而言,为了让客户端获取他自身的一个实例,最传统的方法是:提供一个构造器。但还有一个方法,类可以提供一个公有的静态工厂方法(static factory method),它只是一个返回类的实例的静态方法。

    • 例如:下面是一个来自Boolean(boolean的装箱类):

        //该方法将基本类型boolean装换为Boolean对象的引用
        public static Boolean valueOf(boolean b){
        	return b ? Boolean.TRUE : Boolean.FALSE;
        }
      
    • 注意这里所指的静态工厂方法并不直接对应于设计模式的工厂方法。静态工厂方法而不是构造器,这样既有优势又有劣势。

  • 优势:

    • 它们有名称
      • 如果构造器的参数本身没有确切地描述正被返回的对象,那么具有适当名称的静态工厂会更容易使用,产生的代码也更易阅读。
        • 例:构造器BigInteger(int , int ,Random)返回的BigInteger可能为素数,如果用名为BigInteger.probablePrime的静态工厂方法来表示,显然更为清楚。
      • 当一个类需要多个带有签名(参数类型)的构造器时,就用静态工厂方法代替构造器,并且仔细地选择名称以便突出静态工厂方法之间的区别。
    • 不必在每次调用他们的时候都创建一个新对象。
      • 这使得不可变类可以预先构建好的实例,或者将构建好的实例缓存起来,重复利用,类似于享元(Flyweight)模式。如果程序频繁的请求相同的对象,反复创建销毁新对象将导致消耗较多资源,运行速度也将下降,而这项技术将极大地提升性能。
      • 静态工厂方法能够为重复的调用返回相同的对象,这有助于类总能严格控制在某个时刻哪些实例应该存在。这种类被称作实例受控的类(instance-controlled)。
        1. 实例受控使得类可以确保它是一个Singleton或者是不可实例化的。
        2. 它还使得不可变值类可以确保不会存在两个相等的实例,即当且仅当a==b时,a.equals(b)才为true。
    • 它们可以返回原返回类型的任何子类型的对象。
      • 这样我们在选择对象的类时就有了更大的灵活性。这种灵活性的一种应用是,API可以返回对象,同时又不会使对象的类变成公有的。以这种方式隐藏实现类会使API变得非常简洁。
      • 这项技术适用于基于接口的框架(interface-based framework),因为在这种框架中,接口为静态工厂方法提供了自然返回类型。
      • 在java8之前,接口不能有静态方法,接口的Type的静态工厂方法被放在一个名为Types的不可实例化的伴生类中。
      • 例如:Java Collection Framework 的集合接口有45个工具实现,分别提供了不可修改的集合、同步集合,等等。几乎所有这些实现通过静态工厂方法在一个不可实例化的类(java.util.Collections)中导出。所有返回对象的类都是非公有的。
    • 所返回的对象的类可以随着每次调用而发生变化,这取决于静态工厂方法的参数值。
      • 只要是已声明的返回值类型的子类型,都是允许的。返回的对象的类也可能随着发行版本的不同而不同。
      • 例如:e=EnumSet没有公有的构造器,只有静态工厂方法。在OpenJDK实现中,它们返回的两种子类之一的一个实例,具体取决于底层枚举的大小:
      • 如果它的元素有64个或者更少,静态工厂返回一个RegalarEnumSet实例,用单个long进行支持,如果枚举类型有65及以上,工厂就返回JumboEnumSet实例,用一个long数组进行支持。
    • 方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在。
      • 这种灵活的静态工厂方法构成了服务提供者框架(Service Provider Framework)的基础,例如JDBCAPI。
      • 服务提供者框架,是指这样一个系统:多个服务提供者提供一个服务,系统 为服务提供者的客户端提供多个实现,并把它们从多个实现中解耦出来。
      • 服务提供者框架中有三个重要的组件:服务接口(Service Interface),这是提供者实现的;提供者注册API(Provider Registration API),这是提供者用来注册实现的;服务访问API(Service Access API)这是客户端从来获取的实例。
      • 第四个组件:服务提供者接口(Service Provider Interface)是可选的,它表示产生服务接口之实例的工厂对象。如果没有该接口,实现就通过反射进行实例化。
  • 劣势:

    • 类如果不含有公有的或者受保护的构造器,就不能被实例化。
    • 程序员难以发现他们
      • 在API文档中,它们没有像构造器那样在API文档中明确标识出来。同时通过在类或者接口注释中关注静态工厂,并遵守命名习惯

      • from——类型转换方法,它只有单个参数,返回该类型的一个实例,把它们合并起来,例如:

          Date d = Date.from(instance);
        
      • of——聚合方法,带有多个参数,返回该类型的一个实例,把它们合并起来,例如:

          Set<Rank> faceCards = EnumSet.of(JACK,QUEEN,KING);
        
      • instance or getInstance——返回的实例是通过方法的参数来描述的,但是不能说与参数具有同样的价值,例如:

          StackWalker luke = StackWalker.getInstance(options);
        
      • valueOf——比from和of更繁琐的替代方法,例如:

          BigInteger prime = BigInteger.valueOf(Inteiet.MAX_VALUE);
        
      • create或者newInstance——像instance和getInstance一样,但create或者newInstance能够确保每次调用都返回一个实例,例如:

          Object newArray = Array.newInstance(classObject, arrayLen):
        
      • getType——像getInstance一样,但是在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的对象类型,例如:

          FileStore fs = Files.getFilesStore(path);
        
      • newType——像newInstance一样,但是在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的对象类型,例如:

          BufferReader br = Files.newBufferReader(path);
        
      • type ——getType和newType的简单版,例如:

          List<Complaint> litany = Collection.list(legacyLitany);
        
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值