构造器优化——静态工厂和构建器
来源:互联网 发布:unity3d开发的大型游戏 编辑:程序博客网 时间:2024/05/07 16:44
一、简介
构造器是我们日常开发使用最多的,这种创建对象的方式尤其独特的优点,但是也有他相应的而不足之处。
当一个类的公用构造器的数量比较少的时候,应该考虑使用静态工厂方法来替代构造器。
当一个类的公用构造器的数量比较多且要想更易于拓展时,应该考虑使用构建器。
二、静态工厂
静态工厂(static factory method):是一个返回类的实例的静态方法,而不是提供公有的构造器。
注意:此处的工厂与设计模式中的工厂模式不是一个概念,不要混淆。
1. 静态工厂的缺点
首先写缺点感觉怪怪的,但是我认为这更加便于对静态工厂的理解。
- 静态工厂与其他的静态方法实际上没有任何区别。(这句话也是促使我先写缺点的原因)
- 类不包含共有的或者受保护的构造器,就不能被子类化。
在API文档中静态工厂与静态类是完全一样的,不像构造器那样被明确表明出来。所以查询通过静态工厂来实例化一个类,是很困难的。因此我们要遵循一定的命名原则。
对于共有工厂返回非公有类也是不能被子类化,例如:要想将Collections Framework(util工具包中的框架Collections)中任何方便的实现类子类化是不可能的。但是因祸得福,以为它鼓励我们使用复合(composition),而不是继承。
2.静态工厂惯用名称
- valueOf——>这种静态方法实际上是类型转换方法。该方法返回的实例于它的参数具有相同的值。
- of——>valueOf的一种更为简洁的替代。
- getInstance——>返回的实例是通过方法的参数来描述的,但是不能够说与参数具有相同的值。(在单例模式中就是返回当前工具类的唯一实例)
- newInstance——>与上面的getInstance类同,但是newInstance能够确保返回的每个实例都与所有其他实例不同
- getType——>与上面的getInstance类同,但是在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的是对象类型
- newType——>与上面的newInstance类同,但是在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的是对象类型
3.静态工厂的优点
<1>静态工厂具有名称
通过名称就能大体了解到了他所实现的功能。
例如:构造器BigInteger(int,int,Random)返回的实例可能是素数。然而静态工厂BigInteger.probalePrime就一目了然。
一个类只能有一个带有指定签名的构造器(我当前的理解是:在具有相同数量的参数时,具有的参数类型不一样)。通常避免这种限制的方法是提供两个构造器,但是他们的参数列表只在参数类型的顺序上有所不同。实际上面对这样的设计,使用者很容易混淆,容易调用错误的构造器。
由于静态工厂方法有名称,所以不受上述限制。当一个类需要多个带有相同签名(当前理解:参数)的构造器时,就用静态工厂方法代替构造器,并且慎重地选择名称以便突出它们之间的区别。
<2>不必再每次调用的时候都创建一个新对象
因为是静态的,所以只在只创建一次。(优缺点很明显)程序经常请求创建相同的对象,并且创建对象的代价恩高,这是这项技术的优点。
静态工厂方法能够为重复的调用返回相同的对象,这样有助于类总能严格控制在某个时刻哪些实例应该存在,这种类被称作实例受控的类(instance-controlled)。
<3>可以返回原返回类型的任何子类型的对象
<------------------------------理解中--------------------------------------------->
<4>创建参数化的实例的时候更加简洁
通常在调用参数化的构造器时,即使类型参数很明显,也必须指明(声明的时候已经书写,但是new的时候还得写一遍)。
例如:Map<String,List<String>> m = new HashMap <String,List<String>>();
如果类型参数变得更长,更复杂,这显得很痛苦,但有了静态工厂方法,编译器就可以替你找到类型参数。这被称作类型推到(Type inference)。
下面就是优化过程:
//假设HashMap提供了这个静态工厂public static <K, V> HashMap<K, V> newInstance(){return new HashMap<K, V>();}//我们就可以用更加简洁的代码代替繁琐的声明Map<String,List<String>> m = HashMap.newInstance();
三、构建器
静态工厂和构造器共同拥有的局限性:它们都不能很好的扩展到大量的可选参数。通常我们针对多个可选参数采用的方法是重叠构造器(telescoping constructor)模式,提供一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选构造参数,以此类推,最后一个构造器包含所有可选参数。
//Telescoping constructor pattern(重叠构造器模式)- does not scale well!public class NutritionFacts {private final int servingSize; // (mL) requiredprivate final int servings; // (per container) requiredprivate final int calories; // optionalprivate final int fat; // (g) optionalprivate final int sodium; // (mg) optionalprivate final int carbohydrate; // (g) optionalpublic NutritionFacts(int servingSize, int servings) {this(servingSize, servings, 0);}public NutritionFacts(int servingSize, int servings, int calories) {this(servingSize, servings, calories, 0);}public NutritionFacts(int servingSize, int servings, int calories, int fat) {this(servingSize, servings, calories, fat, 0);}public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {this(servingSize, servings, calories, fat, sodium, 0);}public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {this.servingSize = servingSize;this.servings = servings;this.calories = calories;this.fat = fat;this.sodium = sodium;this.carbohydrate = carbohydrate;}public void display() {// 显示构造信息System.out.println(servingSize);System.out.println(servings);System.out.println(calories);System.out.println(fat);System.out.println(sodium);System.out.println(carbohydrate);}public static void main(String[] args) {NutritionFacts cocaCola = new NutritionFacts(1, 2);cocaCola.display();}}重叠构造器模式肯定是可行的,但是当有许多参数的时候,客户端的代码就会很难编写,并且仍然难以阅读。
遇到许多构造器参数的时候,还有第二种代替办法,即JavaBean模式,在这种模式下调用一个无参构造器来创建对象,然后调用setter方法来设置每个必要的参数,以及每个相关的可选参数:
// JavaBeans Pattern - allows inconsistency, mandates mutabilitypublic class NutritionFacts { // Parameters initialized to default values (if any) private int servingSize = -1; // Required; no default value private int servings = -1; // Required; no default value private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0; public NutritionFacts() { } // Setters public void setServingSize(int val) { servingSize = val; } public void setServings(int val) { servings = val; } public void setCalories(int val) { calories = val; } public void setFat(int val) { fat = val; } public void setSodium(int val) { sodium = val; } public void setCarbohydrate(int val) { carbohydrate = val; } public static void main(String[] args) { NutritionFacts cocaCola = new NutritionFacts(); cocaCola.setServingSize(240); cocaCola.setServings(8); cocaCola.setCalories(100); cocaCola.setSodium(35); cocaCola.setCarbohydrate(27); }}JavaBean模式相对于重叠构造器模式来说创建实例更容易了,但是其也有着严重的缺点:
- 因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。
- 类无法仅仅通过检验构造器参数的有效性来保证一致性(调用可选参数,可选参数被初始化),试图使用处于不一致状态的对象,将会导致失败,这种错误与包含错误的代码大相径庭,因此很难调试。
- JavaBean模式阻止了把类做成不可变得可能,这就需要程序员付出额外的女里来确保它的线程安全。
然而构建器既能保证向重叠构造器模式那样的安全,也能保证想JavaBean模式那么好的可续行。这就是Builder模式的一种形式。
其实现方式是:不直接生成想要的对象。然后客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象。然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法来生成不可变得对象。这个builder是它构建的类的静态成员类。下面就是它的示例:
// Builder Patternpublic class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate; public static class Builder { // Required parameters private final int servingSize; private final int servings; // Optional parameters - initialized to default values private int calories = 0; private int fat = 0; private int carbohydrate = 0; private int sodium = 0; public Builder(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; } public Builder calories(int val) { calories = val; return this; }//返回Builder对象本身,以便于可以把调用连接起来。(简而言之:通过点一直链接就行,就像main方法中一样) public Builder fat(int val) { fat = val; return this; } public Builder carbohydrate(int val) { carbohydrate = val; return this; } public Builder sodium(int val) { sodium = val; return this; } public NutritionFacts build() { return new NutritionFacts(this); } } private NutritionFacts(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbohydrate = builder.carbohydrate; } public static void main(String[] args) { NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8). calories(100).sodium(35).carbohydrate(27).build(); }}
********************************************************************************结束语********************************************************************************************
我在写这篇博客的时候也是一名初学者,有任何疑问或问题请留言,或发邮件也可以,邮箱为:577328725@qq.com,我会尽早的进行更正及更改。
在我写过的博客中有两篇博客是对资源的整理,可能对大家都有帮助,大家有兴趣的话可以看看!!
下载资料整理——目录:http://blog.csdn.net/fanxiaobin577328725/article/details/51894331
这篇博客里面是我关于我见到的感觉不错的好资源的整理,里面包含了书籍及源代码以及个人搜索的一些资源,如果有兴趣的可以看看,我会一直对其进行更新和添加。
优秀的文章&优秀的学习网站之收集手册:http://blog.csdn.net/fanxiaobin577328725/article/details/52753638
这篇博客里面是我对于我读过的,并且感觉有意义的文章的收集整理,纯粹的个人爱好,大家感觉有兴趣的可以阅读一下,我也会时常的对其进行更新。
********************************************************************************感谢********************************************************************************************
- 构造器优化——静态工厂和构建器
- 使用静态工厂方法代替构造器和构建器使用
- 构建器使用举例-静态工厂和构造器的替代方法
- 构造器和静态工厂方法
- effective--构造器和静态工厂方法
- 【代码优化】考虑使用静态工厂方法代替构造器
- 创建和销毁对象(1)——考虑用静态工厂方法代替构造器
- 构造器与静态工厂
- EffectiveJava(1) 构造器和静态工厂方法
- 第1条 创建和销毁对象——考虑用静态工厂方法代替构造器
- Effective Java(静态工厂类、构建器)
- 静态工厂方法与构造器
- 静态工厂方法与构造器
- 用静态工厂方法代替构造器
- 用静态工厂方法代替构造器
- 单例,静态工厂,构造器
- 第一条: 静态工厂代替构造器
- 采用静态工厂方法替代构造器
- hadoop提示Could not reserve enough space for object heap
- 珍珠笔记--python系统知识
- android 鼠标滚轮事件监听
- 写hdfs的API
- Mac OS X与远程linux服务器文件传输
- 构造器优化——静态工厂和构建器
- Java四种线程池的使用
- 跟着SY速学Ubuntu系列四:编译安装PHP7下面的memcached扩展
- 【4】重建二叉树
- 《重构改善既有代码的设计》-重构原则(学习笔记)
- 地鼠的困境SSL1333 最大匹配
- ELK搭建日志联合分析系统
- 学霸君
- Single Number III