工厂模式

/ 设计模式

什么是工厂模式

工厂模式:通过建造工厂,让产品使用与生产过程解耦。

GoF23种设计模式中,属于创建型模式( Creational patterns)。

工厂模式一般有3种形式。

这三种形式其实就是工厂模式的演进。本质上没有任何区别。就是提供一个公共的接口,把具体实现交给子类,增加程序拓展性,降低耦合。

简单工厂

简单工厂又叫静态工厂方法。

简单工厂

Phone.java

public interface Phone {
    void show();
}

HuaWeiPhone.java

public class HuaWeiPhone implements Phone{
    @Override
    public void show() {
        System.out.println("This is a HuaWei Phone !");
    }
}

ApplePhone.java

public class ApplePhone implements Phone{
    @Override
    public void show() {
        System.out.println("This is an Apple Phone !");
    }
}

Factory.java

public class Factory {

    public static Phone getPhone(String name) {
        Phone phone = null;

        if ("HuaWei".equals(name)) {
            phone = new HuaWeiPhone();
        }

        if ("Apple".equals(name)) {
            phone = new ApplePhone();
        }

        return phone;
    }

}

Test.java

public class Test {

    public static void main(String[] args) {

        Phone phone;

        phone = Factory.getPhone("HuaWei");
        phone.show();

        phone = Factory.getPhone("Apple");
        phone.show();
    }
}

output

This is a HuaWei Phone !
This is an Apple Phone !

当产品类型不多时,且长时间不会改动时,就很适合用该种模式。如:org.slf4j.impl.Log4jLoggerFactory#getLogger。

public class Log4jLoggerFactory implements ILoggerFactory {
    
    ...

    public Logger getLogger(String name) {
        Logger slf4jLogger = (Logger)this.loggerMap.get(name);
        if (slf4jLogger != null) {
            return slf4jLogger;
        } else {
            org.apache.log4j.Logger log4jLogger;
            if (name.equalsIgnoreCase("ROOT")) {
                log4jLogger = LogManager.getRootLogger();
            } else {
                log4jLogger = LogManager.getLogger(name);
            }

            Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
            Logger oldInstance = (Logger)this.loggerMap.putIfAbsent(name, newInstance);
            return (Logger)(oldInstance == null ? newInstance : oldInstance);
        }
    }
    ...
}

该方法传入name参数,然后通过判断后返回Logger对象。

因此,简单工厂在运用时,很普遍,且不一定严格采用上述格式命名,但理念确实是一致的。

通过简单工厂模式,调用只用关心产品的使用,而屏蔽掉了产品的生产过程。实现代码解耦。

违背开闭原则。以上述模型举例,业务新增小米手机业务时,我们需要在工厂类中新增判断逻辑,修改已经稳定的代码,这本身就是一种风险。实际业务开发中,老代码的重构,往往都是一件极为恶心的事情。

工厂方法

简单工厂是将将产品类的实例化操作延迟到工厂子类中完成。新增产品类型时,只需要实现产品和工厂基类,即可完成拓展。对已有代码无需修改。遵守开闭原则,对修改关闭对拓展打开。

工厂方法

Factory.java

public interface Factory {

    Phone getPhone();
}

HuaWeiFactory.java

public class HuaWeiFactory implements Factory {
    @Override
    public Phone getPhone() {
        return new HuaWeiPhone();
    }
}

AppleFactory.java

public class AppleFactory implements Factory {
    @Override
    public Phone getPhone() {
        return new ApplePhone();
    }
}

Phone.java

public interface Phone {
    void show();
}

HuaWeiPhone.java

public class HuaWeiPhone implements Phone{
    @Override
    public void show() {
        System.out.println("This is a HuaWei phone!");
    }
}

ApplePhone.java

public class ApplePhone implements Phone {
    @Override
    public void show() {
        System.out.println("This is an Apple Phone!");
    }
}

Test.java

public class Test {
    public static void main(String[] args) {

        Factory factory;
        Phone phone;

        factory = new HuaWeiFactory();
        phone = factory.getPhone();
        phone.show();

        factory = new AppleFactory();
        phone = factory.getPhone();
        phone.show();
    }
}

output

This is a HuaWei phone!
This is an Apple Phone!

当不能确定产品的具体个数时,创建产品工厂可以采用工厂方法模式。这样既不影响现有产品的生产,又能保留未来产品的拓展性。如:org.apache.dubbo.cache.CacheFactory

apche-dubbo-cachefactory

遵守开闭原则,代码维护成本低。

增加新的产品时,需要实现新的工厂基类和产品基类。容易类爆炸。

抽象工厂

抽象工厂生产组合型产品的工厂类。上面我们知道,工厂方法模式容易类爆炸。于是,我们想能不能将多个工厂方法模式进行抽象合并成一个工厂,同时生产多个产品。最终,抽象工厂模式来了。

抽象工厂

LapTop.java

public interface LapTop {
    void show();
}

HuaWeiLapTop.java

public class HuaWeiLapTop implements LapTop {
    @Override
    public void show() {
        System.out.println("This is a HuaWei LapTop!");
    }
}

AppleLapTop.java

public class AppleLapTop implements LapTop {
    @Override
    public void show() {
        System.out.println("This is an Apple LapTop!");
    }
}

Phone.java

public interface Phone {
    void show();
}

HuaWeiPhone.java

public class HuaWeiPhone implements Phone {
    @Override
    public void show() {
        System.out.println("This is a HuaWei Phone!");
    }
}

ApplePhone.java

public class ApplePhone implements Phone {
    @Override
    public void show() {
        System.out.println("This is an Apple Phone!");
    }
}

Factory.java

public interface Factory {

    Phone getPhone();

    LapTop getLapTop();
}

HuaWeiFactory.java

public class HuaWeiFactory implements Factory {
    @Override
    public Phone getPhone() {
        return new HuaWeiPhone();
    }

    @Override
    public LapTop getLapTop() {
        return new HuaWeiLapTop();
    }
}

AppleFactory.java

public class AppleFactory implements Factory {
    @Override
    public Phone getPhone() {
        return new ApplePhone();
    }

    @Override
    public LapTop getLapTop() {
        return new AppleLapTop();
    }
}

Test.java

public class Test {
    public static void main(String[] args) {

        Factory factory;
        Phone phone;
        LapTop lapTop;

        factory = new HuaWeiFactory();
        phone = factory.getPhone();
        lapTop = factory.getLapTop();
        phone.show();
        lapTop.show();

        factory = new AppleFactory();
        phone = factory.getPhone();
        lapTop = factory.getLapTop();
        phone.show();
        lapTop.show();

    }
}

output

This is a HuaWei Phone!
This is a HuaWei LapTop!
This is an Apple Phone!
This is an Apple LapTop!

通常当多个产品之间有组合关系时,我们就可以用抽象工厂模式来提供统一的接口输出。

比如说上面的例子中,无论是手机或是电脑都可以归入电子产品类,无论是华为或者苹果都有能力进行生产。故就可以建立一个电子设备工厂,进行统一生产,即上述Factory.java

但是请注意,如果这时要加一个汽车的产品类,就不能用抽象工厂,因为汽车和手机、电脑关系性弱。华为、苹果目前为止也没见生产汽车,对吧 - -

实例如:org.springframework.beans.factory.BeanFactory

BeanFactory

这个BeanFactory可以getBean、getType、getAliases。 因为bean、type、aliases正是基本bean的组成部分。

产品族与产品等级

产品

上述举例中,我所说的产品和产品类型有个官方的名词。就是产品等级和产品族。

产品等级:其实就是产品的种类。几个等级,就是有几种产品。只不过名词这么叫。

产品族:族即为种族。不管你是老师、医生还是程序员,只要你姓赵。就都是赵氏是宗族的人,500年前是一家嘛。同理,无论是华为手机、电脑还是路由都是华为产的,那就都属于华为这一产品族。

产品等级就是产品,产品族就是多个产品的共性。

显然,工厂方法适合生产一种产品。而抽象工厂可以生产多个组合的产品。

总结

仅有一个产品等级下,当我们已经知道产品族的个数,且产品族长时间不会改变,就使用简单工厂。

仅有一个产品等级下,当我们不确定产品族的个数,需要保持其拓展性时,就是用工厂方法。

有多个和组合的产品等级下,我们可以使用抽象工厂。