定义一个用于创建对象的接口,让子类决定实例化哪一个类。FactoryMethod 使一个类的实例化延迟到其子类

    工厂方法即Factory Method , 是一种对象创建型模式。
    工厂方法的目的是使得创建对象和使用对象是分离的,并且客户端总是引用抽象工厂和抽象产品:
    工厂方法 - 图1

    我们以具体的例子来说 :假设我们希望实现一个解析字符串到NumberFactory,可以定义如下:

    1. public interface NumberFactory {
    2. Number parse(String s);
    3. }

    有了工厂接口,再编写一个工厂的实现类:

    public class NumberFactoryImpl implements NumberFactory {
        public Number parse(String s) {
            return new BigDecimal(s);
        }
    }
    

    而产品接口是NumberNumberFactoryImp返回的实际产品是BigDecimal
    那么客户端如何创建NumberFactoryImpl呢?通常我们会在接口Factory中定义一个静态方法getFactory()来返回真正的子类:

    public interface NumberFactory {
    
        Number parse(String s);
        static NumberFactory getFactory(){
            return impl;
        }
        static NumberFactory impl = new NumberFactoryImpl();
    }
    

    在客户端中,我们只需要和工厂接口NumberFactoryImpl和实际产品BigDecimal,这样做的好处是允许创建产品的代码独立地变换,而不会影响到调用方。

    有的童鞋会问:一个简单的parse()需要写这么复杂的工厂吗?实际上大多数情况下我们并不需要抽象工厂,而是通过静态方法直接返回产品,即:

    public class NumberFactory{ 
        public static Number parse(String s){
            return new BigDecimal(s);
        }
    }
    

    这种简化的使用静态方法创建产品的方式称为静态工厂方法(Static Factory Method)。
    静态工厂方法广泛地应用在Java标准库中。例如:

    Integer n = Integer.valueOf(100);
    

    Integer既是产品又是静态工厂。它提供了静态方法valueOf()来创建Integer。那么这种方式和直接写new Intege(100) 有何区别?我们观察valueOf()方法

    public final class Integer {
        public static Integer valueOf(int i){
            if(i >= IntegerCache.low && i <= Integer.high)
                return IntegerCache.cache[i+(-IntegerCache.low)];
            return new Integer(i);
        }
    }
    

    它的好处在于,valueOf()内部可能会使用new创建一个新的Integer实例,也有可能直接返回一个缓存的Integer实例。对于调用方来说,没必要知道Integer创建的细节。 :::info 工厂方法可以隐藏创建产品的细节,且不一定每次都会真正的创建产品,完全可以返回缓存的产品,从而提供速度并减少内存消耗。 ::: 如果调用方直接使用Integer n = new Integer(100)那么就失去了使用缓存优化的可能性。

    我们经常使用的另一个静态工厂方法是List.of()

    List<String> list = List.of("a","b","c");
    

    这个静态工厂方法接收可变参数,然后返回List接口。需要注意的是,调用方获取的产品总是List接口,而且并不关心它的实际类型。即使调用方知道List产品的实际类型是java.util.ImmutableCollections$ListN,也不要去强制转型为子类,因为静态工厂方法List.of()保证返回List,但也完全可以修改为返回java.util.ArrayList。这就是里氏替换原则:返回 实现接口的任意子类 都可以满足该方法的要求,且不影响调用方。 :::info 总是引用接口而非实现类,能允许变换子类而不影响调用方,即尽可能面向抽象编程。 ::: 和List.of()类似,我们使用MessageDigest时,为了创建某个摘要算法,总是使用静态工厂方法getInstance(String)

    MessageDigest md5 = MessageDigest.getInstance("MD5");