设计模式之简单工厂和工厂方法

从今天开始,我们就要正式开始学习设计模式了。关于设计模式的重要性,不言而喻,你写的是代码还是诗,一个重要的考察维度就是代码的健壮性,可扩展性,这些都离不开设计模式的支持。

本文,我们就从最简单的工厂方法模式开始,带大家揭开设计模式的神秘面纱。

简单工厂

要学习工厂方法,我们得先来学习下简单工厂模式。工厂方法模式则是在简单工厂的基础上做的进一步优化。

模式定义

简单工厂模式也叫静态工厂方法模式,它是一种创建型模式。创建型模式还包括工厂方法模式、抽象工厂模式、建造者模式、单例模式以及原型模式。

在简单工厂模式中,我们定义一个专门的类用来创建其他的实例,这个专门定义的类会根据不同的参数,返回不同的实例,这些不同的实例一般来说都有一个共同的父类。

但是需要注意的是,简单工厂模式并不属于 GOF 定义的 23 种设计模式。

模式抽象

我们先来通过一个简单的 UML 图来理解下简单工厂模式到底是什么样子的:

图-2
模式抽象
从这个 UML 图中可以看到,在简单工厂模式中,我们会首先定义一个产品的抽象类,然后每一个具体的产品都是实现这个抽象类,再通过工厂方法来创建一个产品的实例。

实例分析

我们通过一个生活中常见的场景来给大家描述一下这个模式。

例如我现在有两台电脑,一台台式机,另一台是笔记本,两台电脑都有一个开机的方法,如下:

public class MacBook {
    public void open() {
        System.out.println("MacBook open...");
    }
}
public class MacPro {
    public void open() {
        System.out.println("MacPro open...");
    }
}

如果今天打算用 MacPro 来 coding 的话,那我就按照如下方式开机:

MacPro macPro = new MacPro();
macPro.open();

如果我今天打算用 MacBook 来 coding 的话,那我就按照如下方式开机:

MacBook macBook = new MacBook();
macBook.open();

上面的代码看着没问题,但是它毕竟是代码,还不是诗,我们要将它变成诗。

首先,既然都是电脑,都有开机的方法,问什么不能统一处理开机问题呢?

于是,我们首先来定义一个 Mac 接口,如下:

public interface Mac {
    void open();
}

这个 Mac 就相当于我们上文说的抽象产品,然后让 MacPro 和 MacBook 分别实现这个接口,并且实现接口中的 open 方法,它们是具体的产品,如下:

public class MacPro implements Mac{
    public void open() {
        System.out.println("MacPro open...");
    }
}
public class MacBook implements Mac{
    public void open() {
        System.out.println("MacBook open...");
    }
}

最终的类结构如下图:

图-3

最后我们在通过一个工厂类来提供具体产品的实例:

public class MacFactory {
    public static Mac getInstance(String type) throws Exception {
        if ("macpro".equals(type)) {
            return new MacPro();
        } else if ("macbook".equals(type)) {
            return new MacBook();
        }
        throw new Exception("");
    }
}

这样,当用户需要获取 MacBook 或者 MacPro 的实例时,只需要通过给工厂方法传入不同的参数,就可以获取到这个实例。如下:

Mac mac = MacFactory.getInstance("macbook");
mac.open();

这就是我们一直心心心念念的简单工厂。那么这么做到底有什么好处呢?

优缺点分析

优点

在简单工厂模式中,有三个重要的元素,就是我们上面 UML 图中的抽象产品、具体产品以及具体工厂,三个元素中,最最核心的当属工厂类了,工厂类根据具体的需要来创建不同的实例,需求方只需要告诉工厂它需要创建什么实例即可,剩下的事情就交给工厂类去完成,需求方不需要去创建产品的实例,实现一个解耦。

缺点

有优点,当然就会有缺点,最大的缺点在于这个工厂类不够灵活,当产品增加的时候,我们就得修改这个工厂类,不够友好;并且由于我们使用了静态方法,导致简单工厂也没法形成继承结构。

对于这些问题,我们在下面的设计模式中将会逐个解决。

实际应用

简单工厂在 Java 中还是有很多非常广泛的应用,例如格式化一个本地日期:

public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale locale);

这就是简单工厂模式,接下来我们将在工厂模式种对此做进一步的优化。

工厂方法

工厂方法模式是对简单工厂的一个升级。我们来具体看下。

模式定义

工厂方法模式也属于创建型模式,又名工厂模式、虚拟构造器模式或者多态工厂模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

在简单工厂模式中,实例的创建都是在工厂类中完成的,如果添加了新产品就得修改工厂方法,现在,我们对工厂类也进行抽象,抽出一个接口,然后创建多个工厂类,不同的工厂类创建不同的产品,这样,如果添加新的产品线,我们只需要提供一个相应的工厂类即可。

模式抽象

我们通过一个简单的 UML 图来看下工厂模式是什么样子:

图-4

从这张图中可以看到,比上面多了一个抽象工厂而已。

实例分析

接下来,我们在上文的基础上,继续来完善。

首先 MacPro 和 MacBook 的定义就不需要变了,我们只需要重新定义 MacFactory,首先我们来定义 MacFactory:

public interface MacFactory {
    Mac getMac();
}

然后再来定义 MacFactory 的实现类:

public class MacBookFactory implements MacFactory {
    public Mac getMac() {
        return new MacBook();
    }
}
public class MacProFactory implements MacFactory {
    public Mac getMac() {
        return new MacPro();
    }
}

在这里我们分别定义了两个实现类,不同的实现类用来创建不同的 Mac 对象。

最终,通过如下方式来创建实例:

MacFactory macProFactory = new MacProFactory();
Mac mac = macProFactory.getMac();
mac.open();
MacFactory macBookFactory = new MacBookFactory();
Mac mac2 = macBookFactory.getMac();
mac2.open();

这就是我们说的工厂方法模式,就比上面的多了一个工厂类的抽象,以后如果有新的产品上线,我们只需要提供相关工厂类即可。

优缺点分析

优点

  1. 工厂类满足单一职责原则,一个工厂类只创建一个类。
  2. 符合开-闭原则。
  3. 实例方法,可以形成工厂类的等级结构。

缺点

  1. 每次添加新产品,都需要提供对应的工厂类。
  2. 一个具体的工厂只能创建一种产品。
  3. 如果要换 Mac 系的产品,还是要修改工厂类。

实际应用

工厂方法设计模式应用相当广泛,最典型的莫过于 java.util.Collection 接口中的 iterator() 方法。

图-5

在这张图中,Collection 和 Iterator 分别相当于产品的抽象类和工厂的抽象类。

文章内容参考公众号Java极客技术

其他设计模式可以参考以下文章

设计模式之单例设计模式
设计模式之装饰器模式
设计模式之静态代理和动态代理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值