1. 工厂设计模式简介

1.1. 工厂模式的定义

在基类中定义创建对象的一个接口,让子类决定实例化哪个类。工厂方法让一个类的实例化延迟到子类中进行。

1.2. 工厂模式的分类

  1. 简单工厂,又称静态工厂方法模式;
  2. 工厂方法模式,又称多态性工厂模式或虚拟构造子模式;
  3. 抽象工厂模式,又称工具箱模式。

    1.3. 使用工厂模式的目的

    (1) 解耦 :把对象的创建和使用的过程分开
    (2)降低代码重复: 如果创建某个对象的过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。
    (3) 降低维护成本 :由于创建过程都由工厂统一管理,所以发生业务逻辑变化,不需要找到所有需要创建对象B的地方去逐个修正,只需要在工厂里修改即可,降低维护成本。

2. 简单工厂模式

简单工厂模式是工厂模式的一个特殊实现。但常规的简单工厂模式违背了“开放-封闭”原则(可扩展,而不可修改的),可以用反射原理来构造简单工厂方法。

2.1. 适用场景

1.需要创建的对象较少
2.客户端不关心对象的创建过程

2.2. 简单工厂模式角色分配

1.工厂角色(Factory):简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。
2.抽象产品角色(Product):简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
3.具体产品角色(concrete product):简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。

2.3. 简单工厂实例

从下面的实例中可以看出所有可能创造的具体产品类名称都已经写死,所有将简单工厂称为静态工厂,这种方法是无法动态增加产品对象的,当需要增加产品对象的时候必须要修改getPhone(String phoneName)方法,这不符合“开放-封闭”原则。

  1. public class TestSimpleFactoryModule {
  2. public static void main(String[] args) {
  3. Phone huaWei = PhoneFactory.getPhone("Huawei");
  4. huaWei.call();
  5. Phone xiaoMi = PhoneFactory.getPhone("XiaoMi");
  6. xiaoMi.call();
  7. }
  8. }
  9. //工厂角色(Factory)
  10. class PhoneFactory {
  11. public static Phone getPhone(String phoneName) {
  12. if (phoneName == null) {
  13. return null;
  14. }
  15. if (phoneName.equalsIgnoreCase("Iphone")) {
  16. return new Iphone();
  17. }
  18. if (phoneName.equalsIgnoreCase("HuaWei")) {
  19. return new HuaWei();
  20. }
  21. if (phoneName.equalsIgnoreCase("XiaoMi")) {
  22. return new XiaoMi();
  23. }
  24. return null;
  25. }
  26. }
  27. //抽象产品角色(product)
  28. interface Phone{
  29. void call();
  30. }
  31. //具体产品角色(concrete product)
  32. class Iphone implements Phone {
  33. @Override
  34. public void call() {
  35. System.out.println("喂,我用的苹果手机在和你打电话");
  36. }
  37. }
  38. class HuaWei implements Phone {
  39. @Override
  40. public void call() {
  41. System.out.println("喂喂喂,我用的华为手机在和你打电话");
  42. }
  43. }
  44. class XiaoMi implements Phone {
  45. @Override
  46. public void call() {
  47. System.out.println("喂喂喂,靠,小米手机么得信号!");
  48. }
  49. }

2.4. 反射的方式创建简单工厂实例

使用反射的方式创建简单工厂可以满足“开放-封闭”原则,当增加新的产品类时,只需要通过全类名就可以获取类对象。但缺点也很明显,通过全类名获取类对象的方式很麻烦,可以用反射+配置文件的形式来改善。

  1. public class TestSFMByReflect {
  2. public static void main(String[] args) throws ClassNotFoundException {
  3. Phone iphone = (Iphone)PhoneFactory2.getPhone(designModule.Iphone.class);
  4. iphone.call();
  5. }
  6. }
  7. class PhoneFactory2 {
  8. public static Object getPhone(Class<? extends Phone> clazz) {
  9. Object obj = null;
  10. try {
  11. //利用类名,直接反射获取类对象
  12. obj = Class.forName(clazz.getName()).newInstance();
  13. } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
  14. e.printStackTrace();
  15. }
  16. return obj;
  17. }
  18. }

2.5. 简单工厂在JDK中的应用

Calendar类使用简单工厂创建对象,源码如下:

  1. Calendar calendar = Calendar.getInstance();
  2. public static Calendar getInstance()
  3. {
  4. return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
  5. }
  6. private static Calendar createCalendar(TimeZone zone,
  7. Locale aLocale)
  8. {
  9. CalendarProvider provider =
  10. LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
  11. .getCalendarProvider();
  12. if (provider != null) {
  13. try {
  14. return provider.getInstance(zone, aLocale);
  15. } catch (IllegalArgumentException iae) {
  16. // fall back to the default instantiation
  17. }
  18. }
  19. Calendar cal = null;
  20. if (aLocale.hasExtensions()) {
  21. String caltype = aLocale.getUnicodeLocaleType("ca");
  22. //可以看出来这部分应用到了简单工厂模式
  23. if (caltype != null) {
  24. switch (caltype) {
  25. case "buddhist":
  26. cal = new BuddhistCalendar(zone, aLocale);
  27. break;
  28. case "japanese":
  29. cal = new JapaneseImperialCalendar(zone, aLocale);
  30. break;
  31. case "gregory":
  32. cal = new GregorianCalendar(zone, aLocale);
  33. break;
  34. }
  35. }
  36. }
  37. if (cal == null) {
  38. // If no known calendar type is explicitly specified,
  39. // perform the traditional way to create a Calendar:
  40. // create a BuddhistCalendar for th_TH locale,
  41. // a JapaneseImperialCalendar for ja_JP_JP locale, or
  42. // a GregorianCalendar for any other locales.
  43. // NOTE: The language, country and variant strings are interned.
  44. if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
  45. cal = new BuddhistCalendar(zone, aLocale);
  46. } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
  47. && aLocale.getCountry() == "JP") {
  48. cal = new JapaneseImperialCalendar(zone, aLocale);
  49. } else {
  50. cal = new GregorianCalendar(zone, aLocale);
  51. }
  52. }
  53. return cal;
  54. }

3. 工厂方法

工厂方法模式应该是在工厂模式家族中是用的最多模式,一般项目中存在最多的就是这个模式。
工厂方法模式是简单工厂的仅一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说 每个对象都有一个与之对应的工厂

3.1. 适用场景

  • 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
  • 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏
  • 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无需关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。

3.2. 工厂方法模式角色分配

  1. 抽象工厂角色(Abstract Factory):是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。
  2. 具体工厂角色 (Concrete Factory):这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建某一种产品对象。
  3. 抽象产品角色(AbstractProduct) :工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
  4. 具体产品角色 (Concrete Product):这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应

image.png

3.3. 工厂方法实例

创建一个工厂类的接口,为每个产品类创建一个工厂类。
创建产品类之前需要先创建对应的工厂类对象。

// 抽象工厂
interface PhoneFactoryInterface {
    Phone getPhone();
}

// 具体工厂
class IphoneFactory implements PhoneFactoryInterface{
    @Override
    public Phone getPhone() {
        return new Iphone();
    }
}

// 具体工厂
class HuaWeiFactory implements PhoneFactoryInterface{
    @Override
    public Phone getPhone() {
        return new HuaWei();
    }
}

// 具体工厂
class XiaoMiFactory implements PhoneFactoryInterface{
    @Override
    public Phone getPhone() {
        return new XiaoMi();
    }
}

//抽象产品角色(product)
interface Phone{
    void call();
}

//具体产品角色(concrete product)
class Iphone implements Phone {

    @Override
    public void call() {
        System.out.println("喂,我用的苹果手机在和你打电话");
    }
}

class HuaWei implements Phone {

    @Override
    public void call() {
        System.out.println("喂喂喂,我用的华为手机在和你打电话");
    }
}

class XiaoMi implements Phone {

    @Override
    public void call() {
        System.out.println("喂喂喂,靠,小米手机么得信号!");
    }
}

public class TestPhoneFactory{
    public static void main(String[] args) {
        PhoneFactoryInterface xiaoMiFactory = new XiaoMiFactory();
        Phone xiaoMi = xiaoMiFactory.getPhone();
        xiaoMi.call();
    }
}

4.抽象工厂方法

在工厂方法模式中,其实我们有一个潜在意识的意识。那就是我们生产的都是同一类产品。抽象工厂模式是工厂方法的仅一步深化,在这个模式中的工厂类不单单可以创建一种产品,而是可以创建一组产品。

4.1. 适用场景

  • 和工厂方法一样客户端不需要知道它所创建的对象的类。
  • 需要一组对象共同完成某种功能时,并且可能存在多组对象完成不同功能的情况。(同属于同一个产品族的产品)
  • 系统结构稳定,不会频繁的增加对象。(因为一旦增加就需要修改原有代码,不符合开闭原则)

4.2. 抽象工厂方法模式角色分配:

  1. 抽象工厂角色 (AbstractFactory):是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。
  2. 具体工厂类角色 (ConreteFactory):这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建某一种产品对象。
  3. 抽象产品角色(Abstract Product):工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
  4. 具体产品角色 (Concrete Product):抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例。在抽象工厂中创建的产品属于同一产品族,这不同于工厂模式中的工厂只创建单一产品。

4.3. 抽象工厂方法和工厂方法的区别

抽象工厂是生产一整套产品(至少有两个产品),这些产品必须是有关系或有依赖的,而工厂方法中的工厂是生产单一产品的工厂。
比方说:一个生产小米手机的工厂也生产小米手机壳,一个生产苹果手机的工厂也生产AirPods。

参考文献:https://blog.csdn.net/qq_34337272/article/details/80472071