宝塔服务器面板,一键全能部署及管理,送你10850元礼包,点我领取

文章目录

  • 一. 用静态工厂方法代替构造器
      • 优势:
      • 劣势:
      • 实例代码:
  • 二. 遇到多个构造器参数时要考虑使用构建器
      • ① 重叠构建器
      • ② JavaBeans模式
      • ③ Builder模式
  • 三. 用私有构造器或枚举类型强化Singleton属性
      • 方法一:公有静态成员是个final域
      • 方法二:公有的成员是个静态工厂
      • 方法三:一个包含单个元素的枚举类型
  • 四. 通过私有构造器强化不可实例化的能力

一. 用静态工厂方法代替构造器

优势:

  • 有名称,可以确切地描述正被返回的对象。
  • 不必在每次调用的时候都创建新对象
  • 可以返回原返回类型的任何子类型对象
  • 返回的对象的类可以随着每次调用而产生变化
  • 返回的对象所属的类,在编写静态工厂方法时可以不存在

劣势:

  • 类如果不含公有的或者受保护的构造器,就不能被子类化
  • 程序员很难发现他们,但是可以通过遵守标准的命名习惯来弥补。

实例代码:

这是第二版的代码,在第三版里已经删去了。不过可以用来便于理解。

// 英文部分可以不看,或者作为参考。/**
用于描述Provider所提供的服务
*/
public interface Service {
... // Service-specific methods go here
}
/**
用于描述Provider,每个Provider都要有自己的newService
*/
public interface Provider {
Service newService();
}
// Noninstantiable class for service registration and access
public class Services {
// 私有化构造方法,不会把工厂实例化,直接用类名。
private Services() { } // Prevents instantiation (Item 4)// 使用Map来存储各provider及其name的映射。
// 使用final:只是providers一直指向当前HashMap,对HashMap里的内容变化没有影响。
// Maps service names to services
private static final Map<String, Provider> providers =
new ConcurrentHashMap<String, Provider>();
public static final String DEFAULT_PROVIDER_NAME = "<def>";
ITEM 1: CONSIDER STATIC FACTORY METHODS INSTEAD OF CONSTRUCTORS 9
// Provider registration API
public static void registerDefaultProvider(Provider p) {
registerProvider(DEFAULT_PROVIDER_NAME, p);
}
// 注册Provider到工厂Services里。
public static void registerProvider(String name, Provider p){
providers.put(name, p);
}
// Service access API
public static Service newInstance() {
return newInstance(DEFAULT_PROVIDER_NAME);
}
// 根据name从工厂中取出对应Provider提供的服务。
public static Service newInstance(String name) {
Provider p = providers.get(name);
if (p == null)
throw new IllegalArgumentException(
"No provider registered with name: " + name);
return p.newService();
}
}

二. 遇到多个构造器参数时要考虑使用构建器

① 重叠构建器

参考下面的代码,活用this,但是参数多的时候难写,并且用起来容易出错。

public class NutritionFacts {
private final int servingSize; // (mL) required
private final int servings; // (per container) required
private final int calories; // optional
private final int fat; // (g) optional
private final int sodium; // (mg) optional
private final int carbohydrate; // (g) optional
public 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);
}
12 CHAPTER 2 CREATING AND DESTROYING OBJECTS
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;
}
}

② JavaBeans模式

  • 构造函数就是:ClassName(){}
  • 赋值交给Setters来实现
  • 弥补了重叠构造器的不足,容易创建实例,并且易读
  • 缺点1:构造过程中JavaBean可能处于不一致的状态(毕竟要多次赋值)
  • 缺点2:使得把类做成不可变的可能性不存在(不能final,毕竟之后还要set的嘛)
public class NutritionFacts {
// Parameters initialized to default values (if any)
private int servingSize = -1; // Required; no default value
private int servings = -1; // " " " "
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts() { }
ITEM 2: CONSIDER A BUILDER WHEN FACED WITH MANY CONSTRUCTOR PARAMETERS 13
// 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; }
}

③ Builder模式

  • 既保证了像重叠构造器的安全性,也保证像JavaBeans的可读性
  • 描述:用类的Builder,来构建类的对象。
// Builder Pattern
public 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;
// 在构造函数前,先写好内部类Builder
public static class Builder {
// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters - initialized to default values
// 与类的final不同,此处因为要用setters,因此不设为final。
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;
}
// 逐个属性设定值,每次都return this:和之后的具体调用相关
public Builder calories(int val)
{ calories = val; return this; }
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; }
// build():使用this builder作为参数来构造类的对象。
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
// 私有的构造函数,使用已经完全初始化的builder来进行唯一的一次初始化。
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
  • 还适用于类层次结构(这里会麻烦点,涉及到抽象类、范式等等)
// 先来个抽象的父类Pizza!
// Builder pattern for class hierarchies
public abstract class Pizza {
public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
final Set<Topping> toppings;
// Builder也是抽象的,这里用到T进行一个约束:只能是Pizza.Builder的字类
abstract static class Builder<T extends Builder<T>> {
EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) {
toppings.add(Objects.requireNonNull(topping));
// 这里不能return T,所以先自己写一个self()函数来代替(返回还未构成的类)
return self();
}
abstract Pizza build();
// Subclasses must override this method to return "this"
protected abstract T self();
}
// <?>:只要是继承了Builder的参数都可用 
Pizza(Builder<?> builder) {
toppings = builder.toppings.clone(); // See Item 50
}
}// 再来两个子类披萨
// 重写之前的抽象函数,并且构造函数中要有super(builder)
public class NyPizza extends Pizza {
public enum Size { SMALL, MEDIUM, LARGE }
private final Size size;
public static class Builder extends Pizza.Builder<Builder> {
private final Size size;
public Builder(Size size) {
this.size = Objects.requireNonNull(size);
}
@Override public NyPizza build() {
return new NyPizza(this);
}
@Override protected Builder self() { return this; }
}
private NyPizza(Builder builder) {
super(builder);
size = builder.size;
}
}
public class Calzone extends Pizza {
private final boolean sauceInside;
public static class Builder extends Pizza.Builder<Builder> {
private boolean sauceInside = false; // Default
public Builder sauceInside() {
sauceInside = true;
return this;
}
@Override public Calzone build() {
return new Calzone(this);
}
@Override protected Builder self() { return this; }
}
private Calzone(Builder builder) {
super(builder);
sauceInside = builder.sauceInside;
}
}// 然后实际使用的时候
// 使用类名建builder,一直builder下去,最后让builder来build对象出来。
NyPizza pizza = new NyPizza.Builder(SMALL)
.addTopping(SAUSAGE).addTopping(ONION).build();
Calzone calzone = new Calzone.Builder()
.addTopping(HAM).sauceInside().build();

三. 用私有构造器或枚举类型强化Singleton属性

  • 单例模式

方法一:公有静态成员是个final域

// Singleton with public final field
public class Elvis {
// 公有 静态 final
public static final Elvis INSTANCE = new Elvis();
// 构造函数私有化
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}

方法二:公有的成员是个静态工厂

相对于方法一,毕竟类的成员还是要用方法来访问比较好。

// Singleton with public final field
public class Elvis {
// 把public 变成 private
private static final Elvis INSTANCE = new Elvis();
// 构造函数私有化
private Elvis() { ... }
// 公有静态方法,返回唯一的实例。
public static Elvis getInstance(){return INSTANCE}
public void leaveTheBuilding() { ... }
}

方法三:一个包含单个元素的枚举类型

  • Java的enum和C++的差别很大。
  • 这样子写会比较简便。
// Enum singleton - the preferred approach
public enum Elvis{// 只有一个“实例”INSTANCE;public void leaveTheBuilding(){...}
}

四. 通过私有构造器强化不可实例化的能力

  • 这块感觉没啥好讲的,就是给构造函数加个private。
  • 好坏处之类的可以参考书里的内容。