天天看点

Effective Java学习:静态工厂方法静态工厂方法1.静态工厂方法的优点2.静态工厂方法的缺点

最近在啃《Effective Java》这本神书。简单记录一下,方便以后温习。

静态工厂方法

静态工厂方法是什么?直接从字面应该就很好理解:

工厂方法:大家应该都知道,就是构建实例的方法呗。(比如:“江南皮革厂”就是创建“皮包”这个对象的工厂)

静态方法:这还有解释的必要么.......

合起来就是静态的构建对象的方法呗。。哦了,这就是静态工厂方法。本文到此结束,感谢大家观看!

哈哈,开个玩笑。 虽然静态工厂方法是什么很好理解。可是静态工厂方法到底有什么用呢,我们又为什么要用它呢?

一般情况下,java中我们创建对象要写一个公有的构造函数,外部调用构造函数来创建一个对象。静态工厂方法就是与它相对应的。代码分别如下:

class People {

    private String sex = "男";
    private String appearance = "一般";
    private String asset = "穷";//原则上来讲,默认数据要符合普遍情况。

    //普通构造函数--无参
    public People() {
    }

    //普通构造函数--有参
    public People(String sex) {
        this.sex = sex;
    }

    public People(String sex, String appearance) {
        this.sex = sex;
        this.appearance = appearance;
    }

    public People(String sex, String appearance, String asset) {
        this.sex = sex;
        this.appearance = appearance;
        this.asset = asset;
    }

    //静态工厂方法
    public static People createGirlfriend() {//程序员的基本操作,new一个女朋友。
        People people = new People();
        people.sex = "女";
        people.appearance = "倾国倾城";
        people.asset = "市中心十栋楼";
        return people;
    }

}
           

上面就是一个简单的静态工厂方法,其实本质上就是加了一层封装。有了这层封装自己可以操作和控制很多东西。

下面看看两者的区别:

1.静态工厂方法的优点

1.1 静态工厂方法是有名称的

我们在java中应该经常看到类似这种构造函数:

class People {

    private String sex = "男";
    private String appearance = "一般";
    private String asset = "穷";

    //普通构造函数--无参
    public People() {
    }

    //普通构造函数--有参
    public People(String sex) {
        this.sex = sex;
    }

    public People(String sex, String appearance) {
        this.sex = sex;
        this.appearance = appearance;
    }

    public People(String sex, String appearance, String asset) {
        this.sex = sex;
        this.appearance = appearance;
        this.asset = asset;
    }

}
           

当然,官方文档的注释还是很完善和规范的。我们可以看到每个构造函数的用处和每个参数的作用。可是如果在多点参数呢,又如果是自己写方法呢。(有时候懒再少些两行注释~~后期维护两行泪~) 调用的时候是不是就会一脸懵逼?我该调用哪个?这个参数是什么?要传什么值?况且每次构建对象的时候都要点进去看注释也挺麻烦的。

为了解决上述问题,我们可以使用静态工厂方法。因为静态工厂方法是可以有名字的,这样构建对象时就非常直观。比如文章开始的第一个demo。通过静态工厂方法createGirlfriend()方法可以直接创建一个“People”的实例,同时我们可以直接从方法名看出我们创建出来的“people”实例是“女朋友”。是不是很方便易懂呢~~

1.2 静态工厂方法可以不必在每次调用他们的时候都创建一个新对象

这个应该很好理解,因为静态工厂方法是多一层封装的。所以在这里面我们可以自由控制返回的对象,可以全局只使用一个对象(单例)或者控制什么时候创建新的实例,什么时候使用缓存的实例。代码如下:

class People {

    private String sex = "男";
    private String appearance = "一般";
    private String asset = "穷";

    //静态工厂方法
    public static People createGirlfriend() {
        People people = new People();//这里是我们可控制的,所以你可以选择new新的对象或者使用缓存的已创建的对象
        people.sex = "女";
        people.appearance = "倾国倾城";
        people.asset = "市中心十栋楼";
        return people;
    }

}
           

1.3 静态工厂方法可以返回原返回类型的任何子类型

这一点也好理解,先看代码:

class People {

    //静态工厂方法
    public static People createChildren() {
        Children people = new Children();
        return people;
    }

}

//People的子类
class Children extends People {
    public Children() {
    }
}
           

这个很好理解了,Children是People的子类。所以在静态工厂方法中可以直接返回Children的实例。

不过这里我们不得不提一下设计原则--里式替换原则:任何基类可以出现的地方,子类一定可以出现。 --百度百科

最通俗的讲,就是我们在写子类的时候要注意:可以拓展父类功能和属性,但是不能修改。(也就是对扩展开放,对修改关闭)

1.4 静态工厂方法返回的对象的类可以随着每次调用而发生改变,这取决于所传参数值

理解:静态工厂方法可以根据参数不同返回不同的子类对象。代码如下:

class People {

    /**
     * @param timeForSingle 单身时长
     * @return 对象
     */
    public static People createGirlfriend(int timeForSingle) {
        if (timeForSingle < 5) {//根据所传参数,返回不同的子类对象。
            EighteenBeauty eighteenBeauty = new EighteenBeauty();
            return eighteenBeauty;
        } else {//单身超过五年,看男生都感觉很清秀了呢~~
            Man man = new Man();
            return man;
        }
    }

}

//18岁年轻漂亮的小姑娘
class EighteenBeauty extends People {
    public EighteenBeauty() {
    }
}

//男人
class Man extends People {
    public Man() {
    }
}
           

OK,看代码就很好理解了。根据所传递参数不同返回了不同的子类对象。

1.5 静态工厂方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在(该篇唯一难点)

重头戏到了,这是本篇唯一难点。这个概念理解起来倒是很简单:静态工厂方法返回对象所属的类,在编写包含该静态工厂方法时可以不存在。(好像是把概念又抄了一遍,不过单从字面实在没什么可解释的了~~)。

不过重点在于,怎么实现呢?

这里直接从 这种静态工厂方法最典型的实现--服务提供者框架 来探讨。

服务提供者框架包含四大组件:(概念不太好理解,可以直接先看下面的例子讲解,然后回过头来再看概念)

  1. 服务接口:这是服务提供者要去实现的接口
  2. 服务提供者接口:生成服务接口实例的工厂对象(就是用来生成服务接口的)(可选)
  3. 提供者注册API:服务者 提供服务者自身的实现
  4. 服务访问API:根据客户端指定的某种条件去实现对应的服务提供者

概念太拗口,上栗子讲解:

//四大组成之一:服务接口
public interface LoginService {//这是一个登录服务
    public void login();
}

//四大组成之二:服务提供者接口
public interface Provider {//登录服务的提供者。通俗点说就是:通过这个newLoginService()可以获得一个服务。
    public LoginService newLoginService();
}

/**
 * 这是一个服务管理器,里面包含了四大组成中的三和四
 * 解释:通过注册将 服务提供者 加入map,然后通过一个静态工厂方法 getService(String name) 返回不同的服务。
 */
public class ServiceManager {
    private static final Map<String, Provider> providers = new HashMap<String, Provider>();//map,保存了注册的服务

    private ServiceManager() {
    }

    //四大组成之三:提供者注册API  (其实很简单,就是注册一下服务提供者)
    public static void registerProvider(String name, Provider provider) {
        providers.put(name, provider);
    }

    //四大组成之四:服务访问API   (客户端只需要传递一个name参数,系统会去匹配服务提供者,然后提供服务)  (静态工厂方法)
    public static LoginService getService(String name) {
        Provider provider = providers.get(name);
        if (provider == null) {
            throw new IllegalArgumentException("No provider registered with name=" + name);

        }
        return provider.newLoginService();
    }
}
           

OK,代码中注释的很清楚了。

思考下,这么做有什么好处呢??重点看一下上面demo中的 “四大组成之四:服务访问API ”

想一想,是不是以后想要增加服务时只需要实现服务提供者接口、服务接口,然后约定一个服务名就可以了?

2.静态工厂方法的缺点

2.1 如果类不含公有的或者受保护的构造器时,就是能被子类化

如果类的构造方法是私有的,那么这个类就不能被继承了。这时候建议用复合代替继承实现类的拓展。

2.2 静态工厂方法实际上就是静态方法,如果命名不规范的话程序员很难发现他们

这时候就需要我们自我约束了,利用静态工厂方法时一定要遵循命名规范,下面是一些常用的静态工厂方法命名:

  1. from    类型转换方法,它只有单个参数,返回该类型的一个实例,并把它们合并起来。
  2. of    聚合方法,带有多个参数,返回该类型的一个实例,把他们合并起来。
  3. valueOf    比from和of更繁琐的一种替代方法。
  4. instance或者getInstance    返回的实例是通过方法的(如有)参数来描述的,但是不能说与参数具有同样的值。
  5. create或者newInstance    像instance或者getInstance一样,但create或者newInstance能够确保每次调用都返回一个新的实例
  6. getType    像getInstance一样,但是在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的对象类型。
  7. newType    像getInstance一样,但是在工厂方法处于不同的类中的时候使用。
  8. type    getType和newType的精简版。