设计模式之简单工厂

简单工厂

简单工厂虽然不是一个标准的设计模式,反而更像是一种编程习惯,太常用了,所以我们也有必要介绍一下它。
简单工厂又叫静态工厂方法模式,它是通过使用静态方法接收不同的参数类型返回不同的实例对象。

目的

在工厂类中提供一个封装的静态工厂方法,用于隐藏对象初始化细节,使客户端可以专注于使用,而不用关心类的初始化过程。

解释

假设我们有一个需要连接到 SQL Server 的 Web 应用,但现在我们需要切换到连接 Oracle。为了不修改现有代码的情况下做到这一点,
我们需要实现简单工厂模式。在这种模式下,可以通过调用一个静态方法来创建与给定数据库的连接。

维基百科

工厂类是一个用于创建其他对象的对象 – 从形式上看,工厂方法是一个用于返回不同原型或类型的函数或方法。

动机

常规的创建对象的方法使用关键字new,new带来的问题就是实现依赖,不能应对具体实例化类型的变化;那么我们如何解决这个问题。
我们知道设计模式的最最核心的理念就是封装变化点,哪里变化,封装哪里。
new对象带来的问题就是创建对象依赖具体的实例化对象,因此变化点就在对象的创建,我们就要使用简单工厂封装对象的创建。

引例

本例主要引入new关键字带来的问题,示例代码如下:

public interface Api {

    public void test(String str);

}

public class Impl implements Api {

    @Override
    public void test(String str) {
        System.out.println("print::::::"+str);
    }
}

public class Client {

    public static void main(String[] args) {
        // 客户端在调用时候,不但知道了接口,还知道了实现,接口的思想就是"封装隔离";
        // 而实现类Impl应该是被接口Api封装并同客户端隔离开的,也就是说客户端根本就
        // 不应该知道具体的实现类是Impl
        Api api = new Impl();
        api.test("new has some problem");
    }

}

上述代码问题如下:
客户端在调用时候,不但知道了接口,还知道了实现。
接口的思想就是"封装隔离"而实现类Impl应该是被接口Api封装并同客户端隔离开的,也就是说客户端根本就
不应该知道具体的实现类是Impl。
那么此问题如何解决?当然使用简单工厂了

定义

提供一个创建对象实例的功能,而无须关心具体实现。被创建的实例的可以是接口、抽象类、也可以是具体的类

简单工厂重构上述例子

在这里,我只列举出来变化的类,代码如下:

/**
 * 类描述: 简单工厂
 */
public class Factory {

    // 静态工厂方法
    public static Api createApi() {
        return new Impl();
    }

}

public class Client {

    public static void main(String[] args) {
        Api api = Factory.createApi();
        api.test("new has some problem");
    }

}

上述客户端代码,不再依赖具体的实例化对象,依赖一个简单工厂,解决了new对象带来的耦合问题;
那么你可能会问,你不就是把new Impl()的代码放入到了Factory中,那么Factory不就又与具体的
实例耦合在一起了吗?针对这个问题,首先我们要明白简单工厂所处的位置,简单工厂的位置位于封装体内
也就是简单工厂是跟接口和具体类的实现在一起的,算是封装体内部的一个类,所以简单工厂知道具体的实例化类没有关系的

虽然说简单工厂的方法大多是用来创建接口的,但是仔细分析就会发现,真正能实现功能的是具体的实现类,这些类是已经做好的
并不是真的要靠简单工厂来创造出来的,简单工厂的方法无外乎就是:实现了选择一个合适的实现类来使用

说明:我上述的引例,api的接口实现类就写了一个,因此在客户端无需选择,如果有多个实现类的话,客户端传递选择的条件。

本质

简单工厂的本质是:选择实现

首先我们来看以下示意例子

public interface Car {
    void print();
}

/**
 * 类描述: 奔驰
 *
 */
public class Ben implements Car {

    @Override
    public void print() {
        System.out.println("this is Ben");
    }
}

/**
 * 类描述: 奥迪
 *
 */
public class Audi implements Car {

    @Override
    public void print() {
        System.out.println("this is Audi");
    }
}

public enum  CarTypeEnum {

    BEN(Ben::new),
    AUDI(Audi::new);

    private final Supplier<Car> constructor;

    CarTypeEnum(Supplier<Car> constructor) {
        this.constructor = constructor;
    }

    public Supplier<Car> getConstructor() {
        return this.constructor;
    }

}

public class CarFactory {

    public static Car getCar(CarTypeEnum typeEnum) {
        return typeEnum.getConstructor().get();
    }

}

public class CarTest {

    public static void main(String[] args) {
        
        Car ben = CarFactory.getCar(CarTypeEnum.BEN);
        Car audi = CarFactory.getCar(CarTypeEnum.AUDI);
        ben.print();
        audi.print();
    }

}

上述代码Car ben = CarFactory.getCar(CarTypeEnum.BEN);通过枚举类选择车型,验证了简单工厂的本质,选择实现

适用场景

在你只关心对象的创建,但不关心如何创建、管理它的时候,请使用简单工厂模式。

优点

可以把对象创建代码集中在一个地方,避免在代码库存散布 “new” 关键字。
可以让代码更加低耦合。它的一些主要优点包括更好的可测试性、更好的可读性、组件可替换性、可拓展性、更好的隔离性。

缺点

会使代码变得比原来的更加复杂一些。

案例

java.util.Calendar#getInstance()
java.util.ResourceBundle#getBundle()
java.text.NumberFormat#getInstance()
java.nio.charset.Charset#forName()
java.net.URLStreamHandlerFactory#createURLStreamHandler(String)
java.util.EnumSet#of()
javax.xml.bind.JAXBContext#createMarshaller()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值