设计模式(一):简单工厂模式

举报
水滴技术 发表于 2023/06/15 17:16:07 2023/06/15
【摘要】 简单工厂模式(Simple Factory Pattern)是工厂模式的一种简单实现方式,它属于创建型模式。简单工厂模式中只有一个“工厂”类,它可以根据类型来创建不同的“产品”实例。

简单工厂模式(Simple Factory Pattern)是工厂模式的一种简单实现方式,它属于创建型模式。简单工厂模式中只有一个“工厂”类,它可以根据类型来创建不同的“产品”实例。

也就是说,创建“产品”的实例,不再是通过直接new的方式,而是通过“工厂”进行生成,客户端无需关心实例创建的细节。

在简单工厂模式中,创建“产品”实例的方法通常是静态方法,因此简单工厂模式又叫作静态工厂方法模式(Static Factory Method Pattern)。

简单工厂模式并不在GoF 23种设计模式之列,有资料说它不是一种设计模式,而更像是一种编程习惯。但由于它逻辑简单,经常被使用,还是有必要深入了解一下的。

角色

通过上面的介绍可以看出,简单工厂模式主要有下面几种角色:

  • 抽象产品(Product):它是所有具体产品的公共接口。在工厂类中的创建产品实例方法,应该返回此接口(面向接口编程)。
  • 具体产品(ConcreteProduct):是工厂类中创建的对象。
  • 工厂(Factory):它是简单工厂模式的核心类,负责实现所有产品实例的创建逻辑。它有一个通过类型来创建产品实例的方法,该方法通常是静态的。
  • 客户端(Client):上面的工厂就是为客户端服务的,客户端调用工厂类中方法,来获取产品实例。

下面是该模式的UML类图:

实现

下面通过一个手机的案例,来讲解该模式的实现。

抽象产品接口:Phone

创建一个手机接口,它可以打电话和发短信。

/**
 * 手机接口
 */
public interface Phone {

    /**
     * 打电话
     */
    void call();

    /**
     * 发短信
     */
    void sendSMS();
}

两个具体产品类:XiaomiPhone和RedmiPhone

为手机接口创建两个实现类,一个是小米手机,一个是红米手机,当然也可以有更多的实现类。

/**
 * 小米手机实现类
 */
public class XiaomiPhone implements Phone {
    @Override
    public void call() {
        System.out.println("使用小米手机打电话");
    }

    @Override
    public void sendSMS() {
        System.out.println("使用小米手机发短信");
    }
}

/**
 * 红米手机实现类
 */
public class RedmiPhone implements Phone {
    @Override
    public void call() {
        System.out.println("使用红米手机打电话");
    }

    @Override
    public void sendSMS() {
        System.out.println("使用红米手机发短信");
    }
}

工厂类:PhoneFactory

创建一个手机工厂类,该类可以根据手机类型获取手机实例。

注:在通常情况下,这个类型应该使用枚举类来表示

/**
 * 手机工厂类
 */
public class PhoneFactory {

    /**
     * 根据类型获取手机
     *
     * @param type
     * @return
     */
    public static Phone createPhone(String type) {
        switch (type) {
            case "xiaomi":
                return new XiaomiPhone();
            case "redmi":
                return new RedmiPhone();
            default:
                return null;
        }
    }
}

客户端类:Client

创建一个客户端类,用于调用手机工厂类,来生成手机实例。

/**
 * 客户端类
 */
public class Client {

  public static void main(String[] args) {
        System.out.println("================小米手机================");
        Phone xiaomiPhone = PhoneFactory.createPhone("xiaomi");
        if (xiaomiPhone != null) {
            xiaomiPhone.call();
            xiaomiPhone.sendSMS();
        }

        System.out.println("================红米手机================");
        Phone redmiPhone = PhoneFactory.createPhone("redmi");
        if (redmiPhone != null) {
            redmiPhone.call();
            redmiPhone.sendSMS();
        }

    }
}

运行结果

运行客户端main方法,会打印如下内容:

================小米手机================
使用小米手机打电话
使用小米手机发短信
================红米手机================
使用红米手机打电话
使用红米手机发短信

优缺点

优点:

  • 使对象的创建和使用分离,对象的创建交给专门的工厂类负责,客户端不需要关心是怎么创建的,只关心如何使用就行了。
  • 只有一个工厂类,逻辑清晰,便于理解。

缺点:

  • 工厂类不够灵活,每增加一个产品,就要修改工厂类,代码耦合性太高。
  • 工厂类负责所有产品的创建,违反了单一职责

典型应用

java.util.Calendar类是Java自带的日历抽象类,它是典型的简单工厂模式。

该类的角色,即是工厂,也是抽象产品。它有三个子类:BuddhistCalendarJapaneseImperialCalendarGregorianCalendar ,这个三子类是具体产品角色。

Calendar类有多个获取实例的重载方法getInstance(),但最终都会调用createCalendar()方法,来生成具体的实例。从下面代码片段也可以看出,它是根据不同的区域类型,来创建对应实例的。

// 获取默认时区和区域的日历实例
public static Calendar getInstance()
{
    return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}

// 获取指定时区和默认区域的日历实例
public static Calendar getInstance(TimeZone zone)
{
    return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
}

// 获取默认时区和指定区域的日历实例
public static Calendar getInstance(Locale aLocale)
{
    return createCalendar(TimeZone.getDefault(), aLocale);
}

// 获取指定时区和指定区域的日历实例
public static Calendar getInstance(TimeZone zone, Locale aLocale)
{
    return createCalendar(zone, aLocale);
}

// 真正获取日历实例的方法
private static Calendar createCalendar(TimeZone zone, Locale aLocale)
{
    // ......
    Calendar cal = null;

    if (aLocale.hasExtensions()) {
        String caltype = aLocale.getUnicodeLocaleType("ca");
        if (caltype != null) {
            switch (caltype) {
            case "buddhist":
            cal = new BuddhistCalendar(zone, aLocale);
                break;
            case "japanese":
                cal = new JapaneseImperialCalendar(zone, aLocale);
                break;
            case "gregory":
                cal = new GregorianCalendar(zone, aLocale);
                break;
            }
        }
    }
    if (cal == null) {
        if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
            cal = new BuddhistCalendar(zone, aLocale);
        } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                   && aLocale.getCountry() == "JP") {
            cal = new JapaneseImperialCalendar(zone, aLocale);
        } else {
            cal = new GregorianCalendar(zone, aLocale);
        }
    }
    return cal;
}

完整代码

完整代码请访问我的Github,若对你有帮助,欢迎给个⭐,感谢~~🌹🌹🌹

https://github.com/gozhuyinglong/blog-demos/blob/main/design-patterns/src/main/java/io/github/gozhuyinglong/designpatterns/factory/FactorySimple.java

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。